线程同步中有七种同步方式,上一篇
线程同步
射手座的程序员,公众号:射手座的程序员线程同步原来这么简单【synchronized,volatile】
讲述了四种,剩下三种,大家可以继续深入了解下:
package java.util.concurrent.locks;import java.util.concurrent.TimeUnit;** @see ReentrantLock* @see Condition* @see ReadWriteLock** @since 1.5* @author Doug Lea*/public interface Lock {void lock();void lockInterruptibly() throws InterruptedException;boolean tryLock();boolean tryLock(long time, TimeUnit unit) throws InterruptedException;void unlock();Condition newCondition();}
需要注意的类:
ReentrantLock:public class ReentrantLock implements Lock, java.io.Serializable {}Condition :接口public interface Condition{}ReadWriteLock 接口public interface ReadWriteLock{}ReentrantReadWriteLock:public class ReentrantReadWriteLockimplements ReadWriteLock, java.io.Serializable{}
Lock的特性:
1).Lock不是Java语言内置的;
2).synchronized是在JVM层面上实现的,如果代码执行出现异常,JVM会自动释放锁,但是Lock不行,要保证锁一定会被释放,就必须将unLock放到finally{}中(手动释放);
3).在资源竞争不是很激烈的情况下,Synchronized的性能要优于ReetarntLock,但是在很激烈的情况下,synchronized的性能会下降几十倍;
4).ReentrantLock增加了锁:
a. void lock(); 无条件的锁;
b. void lockInterruptibly throws InterruptedException;//可中断的锁;
解释:使用ReentrantLock如果获取了锁立即返回,如果没有获取锁,当前线程处于休眠状态,直到获得锁或者当前线程可以被别的线程中断去做其他的事情;但是如果是synchronized的话,如果没有获取到锁,则会一直等待下去;
c. boolean tryLock();//如果获取了锁立即返回true,如果别的线程正持有,立即返回false,不会等待;
d. boolean tryLock(long timeout,TimeUnit unit);//如果获取了锁立即返回true,如果别的线程正持有锁,会等待参数给的时间,在等待的过程中,如果获取锁,则返回true,如果等待超时,返回false;
在这里要介绍的是Condition:
Condition的特性:
1.Condition中的await()方法相当于Object的wait()方法,Condition中的signal()方法相当于Object的notify()方法,Condition中的signalAll()相当于Object的notifyAll()方法。不同的是,Object中的这些方法是和同步锁捆绑使用的;而Condition是需要与互斥锁/共享锁捆绑使用的。
2.Condition它更强大的地方在于:能够更加精细的控制多线程的休眠与唤醒。对于同一个锁,我们可以创建多个Condition,在不同的情况下使用不同的Condition。
例如,假如多线程读/写同一个缓冲区:当向缓冲区中写入数据之后,唤醒”读线程”;当从缓冲区读出数据之后,唤醒”写线程”;并且当缓冲区满的时候,”写线程”需要等待;当缓冲区为空时,”读线程”需要等待。
如果采用Object类中的wait(), notify(), notifyAll()实现该缓冲区,当向缓冲区写入数据之后需要唤醒”读线程”时,不可能通过notify()或notifyAll()明确的指定唤醒”读线程”,而只能通过notifyAll唤醒所有线程(但是notifyAll无法区分唤醒的线程是读线程,还是写线程)。但是,通过Condition,就能明确的指定唤醒读线程。
示例代码:
package com.blog.spring.thread;import java.util.LinkedList;import java.util.List;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class ConditionTask {private final Lock lock = new ReentrantLock();private final Condition addCondition = lock.newCondition();private final Condition subCondition = lock.newCondition();private static int num = 0;private List<String> lists = new LinkedList<String>();public void add() {lock.lock();try {System.out.println("==============当前添加线程:"+Thread.currentThread().getName()+"================");while(lists.size() == 3) {//当集合已满,则"添加"线程等待System.out.println("当前集合大小为: " + lists.size()+"-->集合已满,添加线程"+Thread.currentThread().getName()+"开始等待,去唤醒减少线程");this.subCondition.signal();addCondition.await();}num++;String str="元素" + num;lists.add(str);System.out.println("添加 [" + str + "]");System.out.println("当前集合大小为: " + lists.size());System.out.println("当前添加线程为: " + Thread.currentThread().getName());System.out.println("==============去唤醒减少线程================");this.subCondition.signal();} catch (InterruptedException e) {e.printStackTrace();} finally {//释放锁lock.unlock();}}public void sub() {lock.lock();try {System.out.println("==============当前减少线程:"+Thread.currentThread().getName()+"================");while(lists.size() == 0) {//当集合为空时,"减少"线程等待System.out.println("当前集合大小为: " + lists.size()+"-->集合为空,减少线程"+Thread.currentThread().getName()+"开始等待,去唤醒添加线程");addCondition.signal();subCondition.await();}num--;String str = lists.get(0);lists.remove(0);System.out.println("移除 [" + str + "]");System.out.println("当前集合大小为: " + lists.size());System.out.println("当前减少线程为: " + Thread.currentThread().getName());System.out.println("===============去唤醒添加线程===============");addCondition.signal();} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}}//测试类package com.blog.spring.thread;public class TestCondition {public static void main(String[] args) {final ConditionTask task = new ConditionTask();final ConditionTask task = new ConditionTask();for (int i = 0; i < 10; i++) {new Thread(new Runnable() {@Overridepublic void run() {task.add();}},"添加线程"+i).start();}for (int i = 0; i < 10; i++) {new Thread(new Runnable() {@Overridepublic void run() {task.sub();}},"减少线程"+i).start();}}
运行结果:
==============当前添加线程:添加线程0================添加 [元素1]当前集合大小为: 1当前添加线程为: 添加线程0==============去唤醒减少线程==============================当前添加线程:添加线程6================添加 [元素2]当前集合大小为: 2当前添加线程为: 添加线程6==============去唤醒减少线程==============================当前添加线程:添加线程9================添加 [元素3]当前集合大小为: 3当前添加线程为: 添加线程9==============去唤醒减少线程==============================当前添加线程:添加线程1================当前集合大小为: 3-->集合已满,添加线程添加线程1开始等待,去唤醒减少线程==============当前添加线程:添加线程2================当前集合大小为: 3-->集合已满,添加线程添加线程2开始等待,去唤醒减少线程==============当前添加线程:添加线程3================当前集合大小为: 3-->集合已满,添加线程添加线程3开始等待,去唤醒减少线程==============当前添加线程:添加线程4================当前集合大小为: 3-->集合已满,添加线程添加线程4开始等待,去唤醒减少线程==============当前添加线程:添加线程5================当前集合大小为: 3-->集合已满,添加线程添加线程5开始等待,去唤醒减少线程==============当前添加线程:添加线程7================当前集合大小为: 3-->集合已满,添加线程添加线程7开始等待,去唤醒减少线程==============当前减少线程:减少线程3================移除 [元素1]当前集合大小为: 2当前减少线程为: 减少线程3===============去唤醒添加线程=============================当前添加线程:添加线程8================添加 [元素3]当前集合大小为: 3当前添加线程为: 添加线程8==============去唤醒减少线程==============================当前减少线程:减少线程0================移除 [元素2]当前集合大小为: 2当前减少线程为: 减少线程0===============去唤醒添加线程=============================当前减少线程:减少线程1================移除 [元素3]当前集合大小为: 1当前减少线程为: 减少线程1===============去唤醒添加线程=============================当前减少线程:减少线程2================移除 [元素3]当前集合大小为: 0当前减少线程为: 减少线程2===============去唤醒添加线程=============================当前减少线程:减少线程4================当前集合大小为: 0-->集合为空,减少线程减少线程4开始等待,去唤醒添加线程添加 [元素1]当前集合大小为: 1当前添加线程为: 添加线程1==============去唤醒减少线程==============================当前减少线程:减少线程5================移除 [元素1]当前集合大小为: 0当前减少线程为: 减少线程5===============去唤醒添加线程=============================当前减少线程:减少线程6================当前集合大小为: 0-->集合为空,减少线程减少线程6开始等待,去唤醒添加线程==============当前减少线程:减少线程7================当前集合大小为: 0-->集合为空,减少线程减少线程7开始等待,去唤醒添加线程==============当前减少线程:减少线程8================当前集合大小为: 0-->集合为空,减少线程减少线程8开始等待,去唤醒添加线程==============当前减少线程:减少线程9================当前集合大小为: 0-->集合为空,减少线程减少线程9开始等待,去唤醒添加线程添加 [元素1]当前集合大小为: 1当前添加线程为: 添加线程2==============去唤醒减少线程================添加 [元素2]当前集合大小为: 2当前添加线程为: 添加线程3==============去唤醒减少线程================添加 [元素3]当前集合大小为: 3当前添加线程为: 添加线程4==============去唤醒减少线程================当前集合大小为: 3-->集合已满,添加线程添加线程5开始等待,去唤醒减少线程移除 [元素1]当前集合大小为: 2当前减少线程为: 减少线程4===============去唤醒添加线程===============添加 [元素3]当前集合大小为: 3当前添加线程为: 添加线程7==============去唤醒减少线程================移除 [元素2]当前集合大小为: 2当前减少线程为: 减少线程6===============去唤醒添加线程===============移除 [元素3]当前集合大小为: 1当前减少线程为: 减少线程7===============去唤醒添加线程===============移除 [元素3]当前集合大小为: 0当前减少线程为: 减少线程8===============去唤醒添加线程===============当前集合大小为: 0-->集合为空,减少线程减少线程9开始等待,去唤醒添加线程添加 [元素1]当前集合大小为: 1当前添加线程为: 添加线程5==============去唤醒减少线程================移除 [元素1]当前集合大小为: 0当前减少线程为: 减少线程9===============去唤醒添加线程===============
读写锁很重要,特在此保留一份:
package com.blog.spring.thread;import java.util.Random;import java.util.concurrent.locks.ReadWriteLock;import java.util.concurrent.locks.ReentrantReadWriteLock;/*** Created by hxf on 17/3/29.*/public class ReadWriteLockTest {public static void main(String[] args) {final Data data = new Data();for (int i = 0; i < 3; i++) {new Thread(new Runnable() {public void run() {for (int j = 0; j < 5; j++) {data.set(new Random().nextInt(30));}}}).start();}for (int i = 0; i < 3; i++) {new Thread(new Runnable() {public void run() {for (int j = 0; j < 5; j++) {data.get();}}}).start();}}static class Data {private int data;// 共享数据private ReadWriteLock rwl = new ReentrantReadWriteLock();public void set(int data) {rwl.writeLock().lock();// 取到写锁try {System.out.println(Thread.currentThread().getName() + "准备写入数据");try {Thread.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}this.data = data;System.out.println(Thread.currentThread().getName() + "写入" + this.data);} finally {rwl.writeLock().unlock();// 释放写锁}}public void get() {rwl.readLock().lock();// 取到读锁try {System.out.println(Thread.currentThread().getName() + "准备读取数据");try {Thread.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "读取" + this.data);} finally {rwl.readLock().unlock();// 释放读锁}}}}
运行结果:
Thread-0准备写入数据Thread-0写入9Thread-0准备写入数据Thread-0写入15Thread-0准备写入数据Thread-0写入14Thread-1准备写入数据Thread-1写入17Thread-1准备写入数据Thread-1写入27Thread-1准备写入数据Thread-1写入26Thread-1准备写入数据Thread-1写入22Thread-1准备写入数据Thread-1写入19Thread-2准备写入数据Thread-2写入18Thread-2准备写入数据Thread-2写入6Thread-2准备写入数据Thread-2写入16Thread-2准备写入数据Thread-2写入18Thread-2准备写入数据Thread-2写入29Thread-3准备读取数据Thread-4准备读取数据Thread-5准备读取数据Thread-5读取29Thread-4读取29Thread-3读取29Thread-0准备写入数据Thread-0写入24Thread-0准备写入数据Thread-0写入16Thread-5准备读取数据Thread-4准备读取数据Thread-3准备读取数据Thread-3读取16Thread-4读取16Thread-5读取16Thread-4准备读取数据Thread-3准备读取数据Thread-5准备读取数据Thread-4读取16Thread-5读取16Thread-3读取16Thread-5准备读取数据Thread-4准备读取数据Thread-3准备读取数据Thread-5读取16Thread-3读取16Thread-4读取16Thread-3准备读取数据Thread-5准备读取数据Thread-4准备读取数据Thread-5读取16Thread-3读取16Thread-4读取16Process finished with exit code 0
思考:比较Condition与ReadWriteLock(ReentrantReadWriteLock是实现类)运行结果的区别?原因是啥?
Condition中的锁为:
private final Lock lock = new ReentrantLock();
结果显示:写入和写入互斥,读取和写入互斥,读取和读取互斥
不能并发执行,效率较低。Condition能够精确唤醒某种条件的线程(注意观察上面运行结果,如果拥有这种条件的线程数就一个,那么就能精确到线程,否则就是其中一个线程(随机唤醒)。),但不能并发执行-即所有线程之间都是互斥的。
那么在这思考下Condition与Object(wait/notify,notifyAll)有什么区别?
ReadWriteLock中的锁为:
private ReadWriteLock rwl = new ReentrantReadWriteLock();
结果显示:虽然写入和写入互斥了,读取和写入也互斥了,但是读取和读取之间不互斥了,能够并发执行读取数据,相对效率较高,我想现在应该能很好的理解下面这句话了:
在资源竞争不是很激烈的情况下,Synchronized的性能要优于ReetarntLock,但是在很激烈的情况下,synchronized的性能会下降几十倍;
作者:淡淡的倔强
来源:blog.csdn.net/article/details/68064253
版权申明:内容来源网络,版权归原创者所有。除非无法确认,我们都会标明作者及出处,如有侵权烦请告知,我们会立即删除并表示歉意。谢谢
更多精彩,关注公众号,一起学习、成长






