前言:
本文内容:Lock锁、传统生产者消费者问题、防止虚假唤醒、Lock锁的生产者消费者问题
推荐免费JUC并发编程视频:【狂神说Java】JUC并发编程最新版通俗易懂_哔哩哔哩_bilibili
Lock锁
概述
从Java5开始,Java提供了一种功能更强大的线程同步机制——通过显式定义同步锁对象来实现同步,在这种机制下,同步锁由Lock对象充当
公平锁于非公平锁
公平锁:多个线程按照申请锁的顺序来获取锁
非公平锁(默认选择):多线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取到锁,在高并发的情况下,有可能造成优先级反转或者饥饿现象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| package com.jokerdig;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;
public class SaleTicketDemo02 { public static void main(String[] args) { Ticket2 ticket = new Ticket2(); new Thread(() -> { for (int i = 1; i < 40; i++) ticket.sale(); }, "A").start(); new Thread(() -> { for (int i = 1; i < 40; i++) ticket.sale(); }, "B").start(); new Thread(() -> { for (int i = 1; i < 40; i++) ticket.sale(); }, "C").start(); } }
class Ticket2{ private int number = 30; Lock lock = new ReentrantLock();
public void sale(){ lock.lock();
try { if(number>0){ System.out.println(Thread.currentThread().getName()+"卖出了第"+(number--)+"票,剩余:"+number); } } catch (Exception e) { e.printStackTrace(); }finally { lock.unlock(); } } }
|
运行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| A卖出了第30票,剩余:29 A卖出了第29票,剩余:28 A卖出了第28票,剩余:27 A卖出了第27票,剩余:26 A卖出了第26票,剩余:25 A卖出了第25票,剩余:24 C卖出了第24票,剩余:23 C卖出了第23票,剩余:22 C卖出了第22票,剩余:21 C卖出了第21票,剩余:20 C卖出了第20票,剩余:19 C卖出了第19票,剩余:18 C卖出了第18票,剩余:17 C卖出了第17票,剩余:16 C卖出了第16票,剩余:15 C卖出了第15票,剩余:14 C卖出了第14票,剩余:13 C卖出了第13票,剩余:12 C卖出了第12票,剩余:11 C卖出了第11票,剩余:10 C卖出了第10票,剩余:9 C卖出了第9票,剩余:8 C卖出了第8票,剩余:7 C卖出了第7票,剩余:6 C卖出了第6票,剩余:5 C卖出了第5票,剩余:4 C卖出了第4票,剩余:3 C卖出了第3票,剩余:2 C卖出了第2票,剩余:1 C卖出了第1票,剩余:0
Process finished with exit code 0
|
Synchronized和Lock的区别
- Synchronized是内置的关键字,Lock是一个Java类
- Lock允许实现更灵活的结构,可以具有差别很大的属性,并且支持多个相关的Condition对象
- synchronized无法判断锁的状态,Lock可以判断是否获取到了锁
- synchronized会自动释放锁,lock必须要手动释放,不释放会产生死锁
- synchronized线程1(获得锁,假如获取阻塞),线程2(等待,会一直等待);Lock锁就不一定会等待
- synchronized是可重入锁,不可以中断,非公平;Lock锁是可重入锁,可以判断锁,非公平(可以自定义)
- synchronized适合锁少量同步代码,Lock适合锁大量的同步代码
传统生产者消费者问题、防止虚假唤醒
面试常考:单例模式、排序算法、生产者和消费者、死锁
传统生产者消费者问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| package com.jokerdig.pc;
public class A { public static void main(String[] args) { Data data = new Data();
new Thread(()->{ for(int i = 0;i<10;i++){ try { data.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } },"A").start();
new Thread(()->{ for(int i = 0;i<10;i++){ try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },"B").start(); }
}
class Data{ private int num = 0;
public synchronized void increment() throws InterruptedException { if(num!=0){ this.wait(); } num++; System.out.println(Thread.currentThread().getName()+"->"+num); this.notifyAll(); } public synchronized void decrement() throws InterruptedException { if(num==0){ this.wait(); } num--; System.out.println(Thread.currentThread().getName()+"->"+num); this.notifyAll(); } }
|
运行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| A->1 B->0 A->1 B->0 A->1 B->0 A->1 B->0 A->1 B->0 A->1 B->0 A->1 B->0 A->1 B->0 A->1 B->0 A->1 B->0
Process finished with exit code 0
|
防止虚假唤醒
问题:假设存在ABCD四个线程,运行会出现问题(我们使用了if)
线程也可以唤醒,而不会被通知,中断或超时,即虚假唤醒。虽然实际开发中很少发生,但应用程序必须通过测试应该使线程被唤醒的条件来防范,并且如果条件不满足则继续等待。等待应该总是出现在循环中。
解决办法:将if判断改为while即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| package com.jokerdig.pc;
public class A { public static void main(String[] args) { Data data = new Data();
new Thread(()->{ for(int i = 0;i<10;i++){ try { data.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } },"A").start();
new Thread(()->{ for(int i = 0;i<10;i++){ try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },"B").start(); new Thread(()->{ for(int i = 0;i<10;i++){ try { data.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } },"C").start();
new Thread(()->{ for(int i = 0;i<10;i++){ try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },"D").start(); }
}
class Data{ private int num = 0;
public synchronized void increment() throws InterruptedException { while(num!=0){ this.wait(); } num++; System.out.println(Thread.currentThread().getName()+"->"+num); this.notifyAll(); } public synchronized void decrement() throws InterruptedException { while(num==0){ this.wait(); } num--; System.out.println(Thread.currentThread().getName()+"->"+num); this.notifyAll(); } }
|
Lock锁的生产者消费者问题
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
| package com.jokerdig.pc;
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;
public class B { public static void main(String[] args) { Data1 data = new Data1();
new Thread(() -> { for (int i = 0; i < 10; i++) { try { data.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "A").start();
new Thread(() -> { for (int i = 0; i < 10; i++) { try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "B").start();
new Thread(() -> { for (int i = 0; i < 10; i++) { try { data.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "C").start();
new Thread(() -> { for (int i = 0; i < 10; i++) { try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "D").start(); } }
class Data1{ private int num = 0; Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); public void increment() throws InterruptedException { try { lock.lock(); while(num!=0){ condition.await(); } num++; System.out.println(Thread.currentThread().getName()+"->"+num); condition.signalAll(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void decrement() throws InterruptedException { try { lock.lock(); while(num==0){ condition.await(); } num--; System.out.println(Thread.currentThread().getName()+"->"+num); condition.signalAll(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }
|
存在问题:现在执行都是随机的
需求:按照顺序执行,精准的通知和唤醒