免费学习推荐:java基础教程
Lock锁与生产者消费者问题
- 传统Synchronized锁
- Lock锁
- Synchronized和lock锁的区别
- 传统的生产者和消费者问题
- Lock版的生产者和消费者问题
- Condition实现精准通知唤醒
传统Synchronized锁
实现一个基本的售票例子:
/* 真正的多线程开发,公司中的开发,降低耦合性 线程就是一个单独的资源类,没有任何附属的操作 1.属性,方法 * */public class SaleTicketDemo1 { public static void main(String[] args) { //并发,多个线程操作同一个资源类,把资源类丢入线程 Ticket ticket=new Ticket(); //Runnable借口是一个FunationalInterface函数式接口,接口可以new,jdk1.8以后,lamda表达式()->{代码} new Thread(()->{ for(int i=0;i<60 i=0;i>{ for(int i=0;i<60 number=50;>0){ System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余"+number); } }}登录后复制
注意,这里面用到了lambda表达式,lambda表达式详细描述见Java基础-Lambda表达式
立即学习“Java免费学习笔记(深入)”;
这是使用传统的synchronized实现并发,synchronized的本质就是队列,锁。就好比食堂排队。如果没有排队,就会很乱。只有给一个人服务完成了,另一个人才能接收到服务。
Lock锁
之前已经说道,JVM提供了synchronized关键字来实现对变量的同步访问以及用wait和notify来实现线程间通信。在jdk1.5以后,JAVA提供了Lock类来实现和synchronized一样的功能,并且还提供了Condition来显示线程间通信。
Lock类是Java类来提供的功能,丰富的api使得Lock类的同步功能比synchronized的同步更强大。
在java.util. Concurrent包中,里面有3个接口,Condition,lock(标准锁)。ReadWriteLock锁(读写锁)
Lock实现提供比使用synchronized方法和语句可以获得的更广泛的锁定操作。 它们允许更灵活的结构化,可能具有完全不同的属性,并且可以支持多个相关联的对象Condition。
Lock l = ...; l.lock(); try { // access the resource protected by this lock } finally { l.unlock(); }登录后复制
lock()表示加锁,unlock()表示解锁
JDK官方文档中解释
所有已知实现类:
ReentrantLock可重入锁
ReentrantReadWriteLock.ReadLock 读锁
ReentrantReadWriteLock.writeLock写锁
先说ReentrantLock实现类:
ReentrantLock底层源码构造函数
公平锁:十分公平,可以先来后到。但是问题如果一个3s和一个3h的进程到达,3h先,那么3s等3h,实际上也不利。
非公平锁:十分不公平,可以插队(默认)
之后,我们会具体解释。
怎么用,用之前加锁,用之后解锁
//lock锁三部曲
//1.new ReentranLock();构造
//2.Lock.lock();加锁
//3.finally();解锁
public class SaleTicketDemo2 { public static void main(String[] args) { //并发,多个线程操作同一个资源类,把资源类丢入线程 Ticket ticket=new Ticket(); //Runnable借口是一个FunationalInterface函数式接口,接口可以new,jdk1.8以后,lamda表达式()->{代码} new Thread(()->{for(int i=0;i{for(int i=0;i{for(int i=0;i<60 number=50; lock=new>0){ System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余"+number); } } catch (Exception e) { // TODO: handle exception }finally{ lock.unlock(); } }}登录后复制
Synchronized和lock锁的区别
1.synchronized是内置的java关键字,lock是一个Java类
2.synchronized无法判断获取锁的状态,lock可以判断是否获取到了锁
3.synchronized会自动释放锁(a–),lock必须要手动释放锁!如果不释放锁,会导致死锁
4.Synchronized线程1(获得锁,阻塞),线程2(等待,傻傻的等)
lock.tryLock()尝试获取锁,不一定会一直等待下去
5.Synchronized可重入锁,不可以中断的,非公平锁。Lock,可重入锁,可以判断锁,公平与非公平可以自己设置(可以自己设置)
6.synchronized适合少量的代码同步问题,lock锁适合锁大量的同步代码
synchornized锁对象和同步代码块方法
传统的生产者和消费者问题
传统的生产者和消费者是基于Object类的wait、notify方法和synchronized关键字来实现的。
在面试的时候,手写生产者消费者代码是很常见的事情。
面试笔试经典问题:
单例模式+排序算法+生产者消费者+死锁
生产者消费者问题synchronized版
线程之间的通信问题:生产者和消费者问题 等待唤醒,通知唤醒
线程交替执行 A B 操作同一个变量number=0
A num+1
B num-1
注意:加锁的方法中,执行的思路是判断等待+业务+通知
package testConcurrent;/* 线程之间的通信问题:生产者和消费者问题 等待唤醒,通知唤醒 线程交替执行 A B 操作同一个变量number=0 A num+1 B num-1 * */public class A { public static void main(String[] args) { Data data =new Data(); new Thread(()->{ for(int i=0;i
- {
for(int i=0;i<10 number=0; if(number!=0){>"+number);
//通知其他线程,我+1完毕了
this.notify();
}
//-1
public synchronized void decrement() throws InterruptedException{
if(number==0){
//等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知其他线程,我-1完毕了
this.notify();
}} 登录后复制
如图,基本可以实现所要求的功能,但是这样还会出现问题,如果此时我再加上了两个线程,则
new Thread(()->{ for(int i=0;i
- {
for(int i=0;i
- 登录后复制
这里结果中出现了2,输出结果出现了问题。为什么呢?
为什么if判断会出现问题:
if判断只判断一次。因为if判断了之后,就已经进入了代码的等待那一行,这时,在wait下的线程可能有多个,甚至包括生产者和消费者。有可能某个生产者执行完了之后,唤醒的是另一个生产者。
在我们的官方文档中就给出了解释
public final void wait(long timeout) throws InterruptedException登录后复制
导致当前线程等待,直到另一个线程调用此对象的notify()方法或notifyAll()方法,或指定的时间已过。
线程也可以唤醒,而不会被通知,中断或超时,即所谓的虚假唤醒 。 虽然这在实践中很少会发生,但应用程序必须通过测试应该使线程被唤醒的条件来防范,并且如果条件不满足则继续等待。 换句话说,等待应该总是出现在循环中,就像这样:
synchronized (obj) { while (登录后复制) obj.wait(timeout); ... // Perform action appropriate to condition }
注意点:防止虚假唤醒问题。
我们代码中用的是if判断,而应该用while判断
package testConcurrent;/* 线程之间的通信问题:生产者和消费者问题 等待唤醒,通知唤醒 线程交替执行 A B 操作同一个变量number=0 A num+1 B num-1 * */public class A { public static void main(String[] args) { Data data =new Data(); new Thread(()->{ for(int i=0;i
- {
for(int i=0;i
- {
for(int i=0;i
- {
for(int i=0;i<10 number=0; while(number!=0){>"+number);
//通知其他线程,我+1完毕了
this.notify();
}
//-1
public synchronized void decrement() throws InterruptedException{
while(number==0){
//等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知其他线程,我-1完毕了
this.notify();
}} 登录后复制
Lock版的生产者和消费者问题
在synchronized版本中,我们使用了wait和notify来实现线程之间的同步
在lock中,
此时synchronized被lock替换了,那么wait和notify用什么来替换呢?
我们在官方文档java.util.concurrent.locks 中,找到Lock类,然后在底部找到了
Condition newCondition()
返回一个新Condition绑定到该实例Lock实例。
在等待条件之前,锁必须由当前线程保持。 呼叫Condition.await()将在等待之前将原子释放锁,并在等待返回之前重新获取锁。
然后我们再来了解Condition类
Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。
一个Condition实例本质上绑定到一个锁。 要获得特定Condition实例的Condition实例,请使用其newCondition()方法。
我们可以看到,使用的时候new一个Condition对象。然后用await替代wait,signal替换notify
代码实现
//判断等待+业务+通知
class Data2{ //数字。资源类 private int number=0; Lock lock=new ReentrantLock(); Condition condition=lock.newCondition(); //+1 public void increment() throws InterruptedException{ try { lock.lock(); //业务代码 while(number!=0){ //等待 condition.await(); } number++; System.out.println(Thread.currentThread().getName()+"=>"+number); condition.signalAll(); //通知 } catch (Exception e) { // TODO: handle exception }finally{ lock.unlock(); } } //-1 public void decrement() throws InterruptedException{ try { lock.lock(); //业务代码 while(number!=1){ //等待 condition.await(); } number--; System.out.println(Thread.currentThread().getName()+"=>"+number); condition.signalAll(); //通知 } catch (Exception e) { // TODO: handle exception }finally{ lock.unlock(); } } }登录后复制
注意:主函数部分于最上面的代码一样。
这时候虽然说是正确的,但是它是一个随机分布的状态,现在我们希望它有序执行,即A执行完了执行B,B执行C,C完了执行D。即精准通知。
Condition实现精准通知唤醒
Condition实现精准的通知和唤醒
我们构造三个线程,要求A执行完了执行B,B执行完了执行C,C执行完了执行D.
代码思想:
//加多个监视器,通过监视器来判断唤醒的是哪一个人
//设置多个同步监视器,每个监视器监视一个线程
//实例:生产线,下单->支付->交易->物流
package testConcurrent;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;/* A执行完调用B,B执行完调用C,C执行完调用A * */public class C { public static void main(String[] args) { Data3 data=new Data3(); new Thread(()->{ for(int i=0;i
- {
for(int i=0;i
- {
for(int i=0;i<10 lock=new>支付->交易->物流
private Condition condition1=lock.newCondition();
private Condition condition2=lock.newCondition();
private Condition condition3=lock.newCondition();
private int number=1; //1A 2B 3C
public void printA(){
lock.lock();
try {
//业务,判断->执行->通知
while(number!=1){
//等待
condition1.await();
}
System.out.println(Thread.currentThread().getName()+"=>AAAAAAA");
//唤醒,唤醒指定的人,B
number=2; //精准唤醒
condition2.signal();
} catch (Exception e) {
// TODO: handle exception
}finally{
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
//业务,判断->执行->通知
while(number!=2){
//等待
condition2.await();
}
System.out.println(Thread.currentThread().getName()+"=>BBBBBBB");
//唤醒,唤醒指定的人,C
number=3; //精准唤醒
condition3.signal();
} catch (Exception e) {
// TODO: handle exception
}finally{
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
//业务,判断->执行->通知
while(number!=3){
//等待
condition3.await();
}
System.out.println(Thread.currentThread().getName()+"=>CCCCCCC");
//唤醒,唤醒指定的人,A
number=1; //精准唤醒
condition1.signal();
} catch (Exception e) {
// TODO: handle exception
}finally{
lock.unlock();
}
}} 登录后复制
相关学习推荐:java基础
以上就是Java介绍Lock锁与生产者消费者问题的详细内容,更多请关注慧达安全导航其它相关文章!
免责 声明
1、本网站名称:慧达安全导航
2、本站永久网址:https//www.huida178.com/
3、本站所有资源来源于网友投稿和高价购买,所有资源仅对编程人员及源代码爱好者开放下载做参考和研究及学习,本站不提供任何技术服务!
4、本站所有资源的属示图片和信息不代表本站的立场!本站只是储蓄平台及搬运
5、下载者禁止在服务器和虚拟机下进行搭建运营,本站所有资源不支持联网运行!只允许调试,参考和研究!!!!
6、未经原版权作者许可禁止用于任何商业环境,任何人不得擅作它用,下载者不得用于违反国家法律,否则发生的一切法律后果自行承担!
7、为尊重作者版权,请在下载24小时内删除!请购买原版授权作品,支持你喜欢的作者,谢谢!
8.若资源侵犯了您的合法权益,请持 您的版权证书和相关原作品信息来信通知我们!QQ:1247526623我们会及时删除,给您带来的不便,我们深表歉意!
9、如下载链接失效、广告或者压缩包问题请联系站长处理
10、如果你也有好源码或者教程,可以发布到网站,分享有金币奖励和额外收入!
11、本站资源售价只是赞助,收取费用仅维持本站的日常运营所需
12、因源码具有可复制性,一经赞助,不得以任何形式退款。
13、本文内容由网友自发贡献和站长收集,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系1247526623@qq.com
转载请注明出处: 慧达安全导航 » Java介绍Lock锁与生产者消费者问题
发表评论 取消回复