JDK并发工具之多线程团队协作:同步控制

一.synchronized的功能扩展:重入锁(java.util.concurrent.locks.ReentrantLock)
重入锁可以完全替代synchronized关键字。在JDK 5.0的早期版本中,重入锁的性能远远好于synchronized,但从JDK 6.0开始,JDK在syn-chronized上做了大量的优化,使得两者的性能差距并不大。

01 public class ReenterLock implements Runnable{
02 public static ReentrantLock lock=new ReentrantLock();
03 public static int i=0;
04 @Override
05 public void run() {
06 for(int j=0;j<10000000;j++){
07 lock.lock();
08 try{
09 i++;
10 }finally{
11 lock.unlock();
12 }
13 }
14 }
15 public static void main(String[] args) throws InterruptedException {
16 ReenterLock tl=new ReenterLock();
17 Thread t1=new Thread(tl);
18 Thread t2=new Thread(tl);
19 t1.start();t2.start();
20 t1.join();t2.join();
21 System.out.println(i);
22 }
23 }

与synchronized相比,重入锁有着显示的操作过程。开发人员必须手动指定何时加锁,何时释放锁。也正因为这样,重入锁对逻辑控制的灵活性要远远好于synchronized。但值得注意的是,在退出临界区时,必须记得释放锁。
重入锁是可以反复进入的,这里的反复仅仅局限于一个线程。上述代码的第7~12行,可以写成下面的形式:
lock.lock();
lock.lock();
try{
i++;
}
finally{
lock.unlock();
lock.unlock();
}

重入锁还提供了一些高级功能。比如,重入锁可以提供中断处理的能力。
1.中断响应
对于synchronized来说,如果一个线程在等待锁,那么结果只有两种情况,要么它获得这把锁继续执行,要么它就保持等待。而使用重入锁,则提供另外一种可能,那就是线程可以被中断。也就是在等待锁的过程中,程序可以根据需要取消对锁的请求。
try {
lock.lockInterruptibly(); 申请锁(替换上面的 lock.lock();)
}catch{}
finally
{ if (lock.isHeldByCurrentThread()) {
lock1.unlock();
}
}
可通过 t2.interrupt(); 中断 进行通知 并捕获释放锁

2.锁申请等待限时
除了等待外部通知之外,要避免死锁还有一种方法,那就是限时等待。
我们可以使用tryLock()方法进行一次限时的等待
if(lock.tryLock(5, TimeUnit.SECONDS)) {}
else {}
tryLock()方法接收两个参数,一个表示等待时长,另外一个表示计时单位。
ReentrantLock.tryLock()方法也可以不带参数直接运行。在这种情况下,当前线程会尝试获得锁,如果锁并未被其他线程占用,则申请锁会成功,并立即返回true。如果锁被其他线程占用,则当前线程不会进行等待,而是立即返回false。这种模式不会引起线程等待,因此也不会产生死锁
3.公平锁
如果我们使用synchronized关键字进行锁控制,那么产生的锁就是非公平的。
而重入锁允许我们对其公平性进行设置。它有一个如下的构造函数:
public ReentrantLock(boolean fair)
当参数fair为true时,表示锁是公平的。公平锁看起来很优美,但是要实现公平锁必然要求系统维护一个有序队列,因此公平锁的实现成本比较高,性能相对也非常低下,因此,默认情况下,锁是非公平的。如果没有特别的需求,也不需要使用公平锁。公平锁和非公平锁在线程调度表现上也是非常不一样的。
对于非公平锁,根据系统的调度,一个线程会倾向于再次获取已经持有的锁,这种分配方式是高效的,但是无公平性可言。

对上面ReentrantLock的几个重要方法整理如下。
?lock():获得锁,如果锁已经被占用,则等待。
?lockInterruptibly():获得锁,但优先响应中断。
?tryLock():尝试获得锁,如果成功,返回true,失败返回false。该方法不等待,立即返回。
?tryLock(long time, TimeUnit unit):在给定时间内尝试获得锁。
?unlock():释放锁。
就重入锁的实现来看,它主要集中在Java层面。在重入锁的实现中,主要包含三个要素:
第一,是原子状态。原子状态使用CAS操作(在第4章进行详细讨论)来存储当前锁的状态,判断锁是否已经被别的线程持有。
第二,是等待队列。所有没有请求到锁的线程,会进入等待队列进行等待。待有线程释放锁后,系统就能从等待队列中唤醒一个线程,继续工作。
第三,是阻塞原语park()和unpark(),用来挂起和恢复线程。没有得到锁的线程将会被挂起。有关park()和unpark()的详细介绍,可以参考3.1.7线程阻塞工具类:LockSupport。

重入锁的好搭档:Condition条件
它和wait()和notify()方法的作用是大致相同的。但是wait()和notify()方法是和synchro-nized关键字合作使用的,而Condtion是与重入锁相关联的。通过Lock接口(重入锁就实现了这一接口)的Condition newCondition()方法可以生成一个与当前重入锁绑定的Condition实例。利用Condition对象,我们就可以让线程在合适的时间等待,或者在某一个特定的时刻得到通知,继续执行。

Condition接口提供的基本方法如下:
void await() throws InterruptedException;
void awaitUninterruptibly();
long awaitNanos(long nanosTimeout) throws InterruptedException;
boolean await(long time, TimeUnit unit) throws InterruptedException;
boolean awaitUntil(Date deadline) throws InterruptedException;
void signal();
void signalAll();
?await()方法会使当前线程等待,同时释放当前锁,当其他线程中使用signal()或者sig-nalAll()方法时,线程会重新获得锁并继续执行。或者当线程被中断时,也能跳出等待。这和Object.wait()方法很相似。
?awaitUninterruptibly()方法与await()方法基本相同,但是它并不会在等待过程中响应中断。
?singal()方法用于唤醒一个在等待中的线程。相对的singalAll()方法会唤醒所有在等待中的线程。这和Obejct.notify()方法

在JDK内部,重入锁和Condition对象被广泛地使用,以ArrayBlockingQueue为例,它的put()方法实现如下:

二、允许多个线程同时访问:信号量(Semaphore)
信号量为多线程协作提供了更为强大的控制方法。广义上说,信号量是对锁的扩展。无论是内部锁synchronized还是重入锁ReentrantLock,一次都只允许一个线程访问一个资源,而信号量却可以指定多个线程,同时访问某一个资源。
信号量主要提供了以下构造函数:
public Semaphore(int permits)
public Semaphore(int permits, boolean fair) //第二个参数可以指定是否公平

主要方法有:
public void acquire()
public void acquireUninterruptibly()
public boolean tryAcquire()
public boolean tryAcquire(long timeout, TimeUnit unit)
public void release()

三、ReadWriteLock读写锁
private static ReentrantReadWriteLock readWriteLock=newReentrantReadWriteLock();
private static Lock readLock = readWriteLock.readLock();
private static Lock writeLock = readWriteLock.writeLock();

?读-读不互斥:读读之间不阻塞。
?读-写互斥:读阻塞写,写也会阻塞读。
?写-写互斥:写写阻塞。如果在系统中,读操作次数远远大于写操作,则读写锁就可以发挥最大的功效,提升系统的性能。

四、倒计时器:CountDownLatch
对于倒计时器,一种典型的场景就是火箭发射。在火箭发射前,为了保证万无一失,往往还要进行各项设备、仪器的检查。只有等所有的检查完毕后,引擎才能点火。这种场景就非常适合使用CountDownLatch。它可以使得点火线程等待所有检查线程全部完工后,再执行。
构造函数:
public CountDownLatch(int count)

CountDownLatch end = new CountDownLatch(10);//创建实例 计数数量为10。这表示需要有10个线程完成任务,等待在CountDownLatch上的线程才能继续执行。
end.countDown(); //通知Count-DownLatch,一个线程已经完成了任务,倒计时器可以减1啦。
end.await(); //等待 要求主线程等待所有10个检查任务全部完成。待10个任务全部完成后,主线程才能继续执行。

五、循环栅栏:CyclicBarrier
CyclicBarrier是另外一种多线程并发控制实用工具。和CountDownLatch非常类似,它也可以实现线程间的计数等待,但它的功能比CountDown-Latch更加复杂且强大。
前面Cyclic意为循环,也就是说这个计数器可以反复使用。比如,假设我们将计数器设置为10,那么凑齐第一批10个线程后,计数器就会归零,然后接着凑齐下一批10个线程,这就是循环栅栏内在的含义。
CyclicBarrier的使用场景也很丰富。比如,司令下达命令,要求10个士兵一起去完成一项任务。这时,就会要求10个士兵先集合报道,接着,一起雄赳赳气昂昂地去执行任务。当10个士兵把自己手头的任务都执行完成了,那么司令才能对外宣布,任务完成!
比CountDownLatch略微强大一些,CyclicBarrier可以接收一个参数作为barrierAc-tion。所谓barrierAction就是当计数器一次计数完成后,系统会执行的动作。如下构造函数,其中,parties表示计数总数,也就是参与的线程总数。
public CyclicBarrier(int parties, Runnable barrierAction)

CyclicBarrier cyclic = new CyclicBarrier(N, new BarrierRun(flag, N));
cyclic.await();

CyclicBarrier.await()方法可能会抛出两个异常。
一个是InterruptedException,也就是在等待过程中,线程被中断,应该说这是一个非常通用的异常。大部分迫使线程等待的方法都可能会抛出这个异常,使得线程在等待时依然可以响应外部紧急事件。
另外一个异常则是CyclicBarrier特有的BrokenBarrierException。一旦遇到这个异常,则表示当前的CyclicBarrier已经破损了,可能系统已经没有办法等待所有线程到齐了。如果继续等待,可能就是徒劳无功的,因此,还是就地散货,打道回府吧。

六、线程阻塞工具类:LockSupport
LockSupport是一个非常方便实用的线程阻塞工具,它可以在线程内任意位置让线程阻塞。和Thread.suspend()相比,它弥补了由于resume()在前发生,导致线程无法继续执行的情况。和Ob-ject.wait()相比,它不需要先获得某个对象的锁,也不会抛出InterruptedException异常。LockSupport的静态方法park()可以阻塞当前线程,类似的还有parkNanos()、parkUntil()等方法。它们实现了一个限时的等待。

LockSupport.park(); //阻塞线程t1
LockSupport.unpark(t1); //释放阻塞

当然,我们依然无法保证unpark()方法发生在park()方法之后。但是执行这段代码,你会发现,它自始至终都可以正常的结束,不会因为park()方法而导致线程永久性的挂起。
这是因为LockSupport类使用类似信号量的机制。它为每一个线程准备了一个许可,如果许可可用,那么park()函数会立即返回,并且消费这个许可(也就是将许可变为不可用),如果许可不可用,就会阻塞。而unpark()则使得一个许可变为可用(但是和信号量不同的是,许可不能累加,你不可能拥有超过一个许可,它永远只有一个)。这个特点使得:即使unpark()操作发生在park()之前,它也可以使下一次的park()操作立即返回。这也就是上述代码可顺利结束的主要原因。
同时,处于park()挂起状态的线程不会像sus-pend()那样还给出一个令人费解的Runnable的状态。它会非常明确地给出一个WAITING状态,甚至还会标注是park()引起的
此外,如果你使用park(Object)函数,还可以为当前线程设置一个阻塞对象。这个阻塞对象会出现在线程Dump中。这样在分析问题时,就更加方便了。在堆栈信息中,我们可以看出当前线程等待的对象。
除了有定时阻塞的功能外,LockSup-port.park()还能支持中断影响。但是和其他接收中断的函数很不一样,LockSupport.park()不会抛出InterruptedException异常。它只是会默默的返回,但是我们可以从Thread.interrupted()等方法获得中断标记。

时间: 2024-11-03 21:28:21

JDK并发工具之多线程团队协作:同步控制的相关文章

JDK并发工具类

在JDK的并发包里提供了几个非常有用的并发工具类.CountDownLatch.CyclicBarrier和Semaphore工具类提供了一种并发流程控制的手段,Exchanger工具类则提供了在线程间交换数据的一种手段. 1等待多线程完成的CountDownLatch CountDownLatch:CountDownLatch允许一个或多个线程等待其他线程完成操作.与thread.join方法类似但功能更多.该计数器只能使用一次 CountDownLatch的构造函数接收一个int类型的参数作

缺陷跟踪工具jira和团队协作与项目管理工具conflunce

一.Atlassian Confluence        Atlassian Confluence(简称Confluence)是一个专业的wiki程序.它是一个知识管理的工具,通过它可以实现团队成员之间的协作和知识共享.Confluence 不是一个开源软件,非商业用途可以免费使用. Confluence使用简单,但它强大的编辑和站点管理特征能够帮助团队成员之间共享信息,文档协作,集体讨论. 目前,Confluence被用于广泛地用于项目团队,开发团队,市场销售团队. Confluence的优

Java学习笔记—多线程(并发工具类,java.util.concurrent.atomic包)

在JDK的并发包里提供了几个非常有用的并发工具类.CountDownLatch.CyclicBarrier和Semaphore工具类提供了一种并发流程控制的手段,Exchanger工具类则提供了在线程间交换数据的一种手段.本章会配合一些应用场景来介绍如何使用这些工具类. CountDownLatch CountDownLatch允许一个或多个线程等待其他线程完成操作.假如有这样一个需求:我们需要解析一个Excel里多个sheet的数据,此时可以考虑使用多线程,每个线程解析一个sheet里的数据,

10大协作办公工具:实现团队工作效率最大化

10大协作办公工具:实现团队工作效率最大化 2012-07-17 17:29 原创 Danice 4条评论 近年来移动互联网的 迅速发展以及越来越多的创业公司把他们数据迁移到云服务器上,创业从此不必再拘泥于固定的办公地点,在家办公.异地办公.移动办公等远程办公模式已经逐渐 受到越来越多企业——特别是初创企业——的亲睐.远程办公对公司的成员来说它是一个有效并且有用的方式,因为它允许公司成员之间可以在很远的距离进行沟 通,从而节约了大量的交通时间和成本:对于整个团队来说,也能大大提高工作效率.基于这

国内外开源与 SaaS ,团队协作平台、项目管理工具整理

整理一些开源与 SaaS ,团队协作平台.项目管理工具.还有哪些比较好的工具,可以推荐下? 名称 地址 备注 asana https://asana.com/ 国外 basecamp https://basecamp.com/ 国外 JIRA https://www.atlassian.com/software/jira 国外 confluence https://www.atlassian.com/software/confluence 国外 Open Atrium http://openat

使用国产优秀的敏捷团队协作工具LEANGOO开展SCRUM看板工作

之前的这篇文章里我介绍了国外优秀的看板协作工具Trello,可以帮助我们管理团队任务或者自己的任务安排,并形象化的把这些任务放到看板上,更加直观的看到任务的状态.今天我想介绍给大家的是国产的优秀的敏捷团队协作工具LEANGOO,和Trello不同的是,LEANGOO加入了更多敏捷开发中用到的元素,例如任务的点数.Sprint周期.燃尽图等,下面就把我的使用体验分享给大家: 1.LEANGOO中自定义看板的栏位数量和名称 和Trello一样,在LEANGOO中我们也可以自定义看板的栏位数量和栏位名

Git团队协作工具

-- 故国神游,多情应笑我,早生华发. Git是什么? Git是一个版本控制工具,代码管理工具,团队协作工具.它跟SVN等传统工具实现同样的目的:但从某种程度来说,它更快,更灵活.我想绝大多数读者都已经在接触这个工具了,并且用于日常的项目中去了.我的这篇文章,不是作为一个Git入门教程,也不是作为一本大块头的教科书.(说到教科书,我推荐下面的这本.这本书确实好,很全面.我的这篇文章,其实就是这本书的读书笔记而已.) Pro Git -- http://git.oschina.net/progit

好用的团队协作工具

一人是人,两人是从,三人是众,一个人只能孤独终老,两个人可以组建一个家庭,而一群人可以组建一个企业,一个地区,一个国家!众人拾柴火焰高,只有众人才能展现出强大的能量,凝聚成一个优秀的团队.工作中的团队协作力量很容易展现出来,每个部门都是进行考核制度,这就是检验每一个部门的团队协作能力是否足够强大,越是团队能力强的部门,业绩就越好,公司培养员工搞团建活动就是为了增加大家的团队协作能力!一个优秀的团队在一起工作更能够集思广益,遇到问题思考的层面.方向都不相同,从而就能得到最好的解决方案.一个人遇到一

什么是团队协作?团队协作工具有哪些?

团队协作是一个积极的动态过程,包括学习.提高灵活性.跨越组织边界和学科局限开展创新.团队协作既是一种独特的领导思维方式,也是一种组织能力.团队协作如果运用的好,对管理团队特别的重要,可以培养团队的向心力.下面是笔者整理的一些团队协作内容,希望对大家有所帮助! 一,什么是团队协作 团队协作是一种为达到既定目标所显现出来的资源共享和协同合作的精神,它可以调动团队成员的所有资源与才智,并且会自动地驱除所有不和谐.不公正的现象,同时对表现突出者及时予以奖励,从而使团队协作产生一股强大而持久的力量. 二,