文档章节

JAVA线程[Thread]--同步篇

Candy_Desire
 Candy_Desire
发布于 2014/11/14 10:26
字数 1765
阅读 933
收藏 4

一、线程简介:

线程(Thread)是进程中某个单一顺序的控制流。也被称为轻量进程(lightweight processes)。计算机科学术语,指运行中的程序的调度单位。

二、共享资源:

所谓共享资源是指可以被一个以上任务使用的资源叫做共享资源。

为了防止数据被破坏,每个任务在与共享资源打交道时,必须独占该资源。而多个线程之间有可能共享一些资源如:内存,文件,数据库等。 当多个线程同时读写同一份共享资源的时,可能会引起冲突。这时候就需要引入线程“同步”机制。 

三、同步机制:

同步(synchronized)实际含义和字面相反。同步是指有序,各线程之间要有序对共享资源进行操作,而不是同时进行操作。 

对于线程同步容易混淆和需要注意的地方:

(1)、同步是指有序的执行共享资源。

(2)、只有共享的资源才需要同步。

(3)、共享的常量资源不需要同步,只有变量资源需要同步。

(4)、无论是否执行同一段代码,只要共享可变资源就需要同步。

四、同步锁:

线程同步的基本实现思路是给共享资源加锁,只有线程获取了这把锁,才有权利访问该共享资源。 

既然是共享资源需要同步,所以同步锁固然是要加在共享资源上,对于一些比较完善的共享资源如:文件系统,数据库系统等,他们自身就提供了比较完善的同步锁机制,我们就不必再添加锁了。

对于同步锁容易混淆和需要注意的地方:

(1)、一般说线程同步的实现方式是给共享资源加锁,这样的表述容易让人误以为锁是要加在共享资源上面的,而实际上同步锁是要加在访问共享资源的代码段上的。

(2)、访问同一份共享资源的不同代码段,应该加同一个锁。如果不是同一个锁就没有同步的意义啦。就是说同步锁本身也是多线程之间的共享对象。

五、实现方式:

同步锁的声明可以不必为static或public,但是一定要保证同步代码之间要使用同一个锁。在Java中任何一个Object Reference都可以作为同步锁,可以理解为对象在内存分配系统中的内存地址,因此要实现同步就必须保证同步锁指向同一个地址。

同一时刻,只有一个线程可以获得lock的所有权,其他竞争lock的线程只能暂停运行,进入到该同步锁的就绪(Ready)队列。一般同步锁都挂有几个线程的队列,包括就绪队列(Ready)、待召队列(Waiting)等。就绪队列里的线程时刻准备的运行,而待召队列里的线程只能一直等待,直到等到某个信号的通知。才能加入到就绪队列中竞争同步锁。成功获得锁的线程执行完代码段后,会自动释放同步锁,就绪队列的线程可继续竞争锁。因此线程同步时非常消耗资源的,要控制好同步粒度。

【注:竞争同步锁失败的线程进入该锁的就绪队列(Ready)不是待召队列(Waiting)】

(1)、正确例子

public static final Object lock =new Object();
synchronized(lock){
//访问共享资源的代码段
}

注:同步锁也要是共享的对象】

(2)、错误例子

void synTest(){
Object lock =new Object();
    synchronized(lock){
    //访问共享资源的代码段
    }
}

【注:由于同步锁是由函数体内部产生的,每个线程调用这段代码都会新建一个同步锁,因此没有同步意义】

六、同步粒度:

synchronized关键字可以直接加在函数的定义上。

void synchronized synTest() { 
//代码段 
}

上面代码等价于

void  synTest() { 
    synchronized(this){
    //对象本身就是锁
    //代码段 
    }
}

//同样适用于静态函数
void  synTest() { 
    synchronized(Class.forName(...)){
    //类本身就是锁
    //代码段 
    }
}

【注:尽量避免把synchronized直接加载函数上,要控制好同步的粒度,同步的代码段越小越好】

在控制同步粒度的同时也要细分好同步锁。例如要同步不同的共享资源时就没有必要公用同一个锁。

例如:

public static final Object lockOne = new Object(); 

void threadOne(){
    synchronized(lockOne){
    //共享资源A
    }
}

void threadTwo(){
    synchronized(lockOne){
    //共享资源B
    }
}

上段代码不是共享一个资源但是都要竞争同一个锁,这样会降低程序的效率,可重新定义个新锁用于B资源的同步。

七、信号量:

信号量模型比同步锁模型复杂许多可跨进程同步,还有计数和控制同事运行线程数等功能,用于处理更复杂的同步模型如:读写同步、生产者、消费者模型等。

信号量的运行方式是可以主动停下正在运行的线程,等待某个信号量的通知后,此时该线程进入到待召队列(Waiting)等到通知后该线程再次进入到就绪队列(Ready)和其他线程竞争同步锁。

信号量的基本模型是等待/通知模型,其他较为复杂的模型都是在此基础上衍生出来的。这里我们先将就基本模型,其他模型后续再更新。

在Java中任何一个Object Reference都可以作为同步锁和信号量。我们知道Object对象有wait()和notify()两个方法,这两个方法分别就是等待通知和发出通知的方法。

(1)、wait的用法:

public static final Object signal = new Object(); 

void waitTest(){
    //获取信号量(同步锁)
    synchronized(signal){
    //该线程进入signal对象的待召队列(Waiting)
    signal.wait();
    //本线程放弃同步锁,后续代码不在执行。
    }
}

【注:signal.wait()不是signal开始等待,而是当前线程开始等待这个signal对象】

wait其实还可以定义等待时间,当时间到了以后处于待召队列中的线程就不在等待直接进入到就绪队列。

(2)、notify的用法:

public static final Object signal = new Object(); 

void notifyTest(){
    //获取信号量(同步锁)
    synchronized(signal){
    //通知signal对象中待召队列中等待的线程进入到就绪队列
    signal.notify();
    //本线程依然持有同步锁,后续代码继续执行。
    }
}

【注:signal.notify()不是通知signal这个对象本身,而是通知正在等待信号量的其他线程进入到就绪队列,线程本身继续持有同步锁,并正常执行后续代码】

还有一个notifyAll()方法,该方法是通知待召队列里面的所有线程。用法类似这里不做介绍了


© 著作权归作者所有

共有 人打赏支持
Candy_Desire
粉丝 31
博文 71
码字总数 84592
作品 0
浦东
产品经理
私信 提问
JAVA多线程和并发基础面试问答

多线程和并发问题是Java技术面试中面试官比较喜欢问的问题之一。在这里,从面试的角度列出了大部分重要的问题,但是你仍然应该牢固的掌握Java多线程基础知识来对应日后碰到的问题。(校对注:...

LCZ777
2014/05/26
0
0
JAVA多线程和并发基础面试问答

Java多线程面试问题 1. 进程和线程之间有什么不同? 一个进程是一个独立(self contained)的运行环境,它可以被看作一个程序或者一个应用。而线程是在进程中执行的一个任务。Java运行环境是一...

hanzhankang
2014/01/20
0
0
JAVA多线程和并发基础面试问答

Java多线程面试问题 1. 进程和线程之间有什么不同? 一个进程是一个独立(self contained)的运行环境,它可以被看作一个程序或者一个应用。而线程是在进程中执行的一个任务。Java运行环境是一...

清风傲剑
2014/12/06
0
0
threadlocal原理分析

简介 早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。当使用ThreadLocal...

mrliuze
2015/08/11
0
0
从Java到JVM到OS线程睡眠

前言 Java 中有时需要将线程进入睡眠状态,这时一般我们就会通过使线程进入睡眠状态,接下去就看看执行该语句在 JVM 中做了什么。 简单例子 以下是一个简单的例子,使主线程睡眠5秒钟。 JVM ...

超人汪小建
08/20
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Caffe(二)-Python-自定义网络

这里我们用一个例子先来体验一下 首先定义一下我们的环境变量 $PYTHONPATH,我这儿是Windows开发环境,至于Windows Caffe怎么编译由读者自己下去搞定 我使用的控制台是 Windows PowerShell 添...

Pulsar-V
28分钟前
2
0
ActiveMQ从入门到精通(二)之可靠性机制

ActiveMQ的可靠性机制 缘由( 确认JMS消息) 只要消息被确认之后,才认为消息被成功消费了。消息的成功消费包括三个阶段:客户端接收消息、客户端处理消息以及客户端确认消息。在事务性会话中...

一看就喷亏的小猿
36分钟前
0
0
源码分析 Mybatis 的 foreach 为什么会出现性能问题

背景 最近在做一个类似于综合报表之类的东西,需要查询所有的记录(数据库记录有限制),大概有1W条记录,该报表需要三个表的数据,也就是根据这 1W 个 ID 去执行查询三次数据库,其中,有一...

TSMYK
今天
7
0
IC-CAD Methodology企业实战之openlava

在云计算解决安全问题并成为IC界主流运算平台之前,私有的服务器集群系统仍然是各大IC公司的计算资源平台首选。 现在主流的服务器集群管理系统包括lsf,openlava,SkyForm,三者都属于lsf一系...

李艳青1987
今天
5
0
http response stream 字节流 接收与解码

在接收图片、音频、视频的时候,需要用到二进制流。 浏览器会发给客户端 字节Byte流,一串串的发过来_int8格式 -128~127(十进制),也就是8bit(位)。 客户端接收的时候,对接收到的字节收集,...

大灰狼wow
今天
8
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部