JUC并发编程学习笔记 (2)

synchronized在假设有两个线程去获取锁时,一个线程获取到锁但阻塞了,第二个线程尝试获取锁时会一直等待;而Lock锁不一定会,因为Lock锁可以使用tryLock方法尝试获取锁。

synchronized可重入,不可中断,非公平锁;而Lock是可重入,可响应中断,可设置是否公平锁。

读写锁——ReadWriteLock

实现类ReentrantReadWriteLock——读写分离思想,主要用在读多写少的并发场景下

操作 操作 结果
    共存  
    不共存,互斥  
    不共存,互斥  

写锁——独占锁——一次只能被一个线程占有

读锁——共享锁——多个线程可以同时占有

//示例桶Lock锁

线程通信问题——生产者消费者问题 步骤

判断等待

业务逻辑

通知其他线程

虚假唤醒

多个线程,即超过2个线程通信时的虚假唤醒问题,这时候不能用if判断,而要用while循环判断。

synchronized版线程通信 public class WaitNotifyTest { public static void main(String[] args) { Bowl bowl = new Bowl(); new Thread(() -> { for (int i = 0; i < 10; i++) { try { bowl.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "一乐大叔").start(); new Thread(() -> { for (int i = 0; i < 5; i++) { try { bowl.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "佐助").start(); new Thread(() -> { for (int i = 0; i < 5; i++) { try { bowl.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "鸣人").start(); } } class Bowl { //注意如果用Integer包装类的话则要使用构造函数等方法给number赋初值,否则默认为null会导致错误 private int number; public synchronized void increment() throws InterruptedException { //注意多个线程,即超过2个线程通信时的虚假唤醒问题,这时候不能用if判断,而要用while循环判断 /* if (number != 0) { this.wait(); }*/ while (number != 0) { this.wait(); } number++; System.out.println(Thread.currentThread().getName() + "生产了" + number + "碗拉面"); this.notifyAll(); } public synchronized void decrement() throws InterruptedException { while (number == 0) { this.wait(); } number--; System.out.println(Thread.currentThread().getName() + "消费了1碗拉面,还剩" + number); this.notifyAll(); } /**输出 * 一乐大叔生产了1碗拉面 * 佐助消费了1碗拉面,还剩0 * 一乐大叔生产了1碗拉面 * 佐助消费了1碗拉面,还剩0 * 一乐大叔生产了1碗拉面 * 佐助消费了1碗拉面,还剩0 * 一乐大叔生产了1碗拉面 * 佐助消费了1碗拉面,还剩0 * 一乐大叔生产了1碗拉面 * 鸣人消费了1碗拉面,还剩0 * 一乐大叔生产了1碗拉面 * 佐助消费了1碗拉面,还剩0 * 一乐大叔生产了1碗拉面 * 鸣人消费了1碗拉面,还剩0 * 一乐大叔生产了1碗拉面 * 鸣人消费了1碗拉面,还剩0 * 一乐大叔生产了1碗拉面 * 鸣人消费了1碗拉面,还剩0 * 一乐大叔生产了1碗拉面 * 鸣人消费了1碗拉面,还剩0 */ } Lock版线程通信 public class ConditionTest { public static void main(String[] args) { BigBowl bowl = new BigBowl(); new Thread(() -> { for (int i = 0; i < 10; i++) { try { bowl.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "一乐大叔").start(); new Thread(() -> { for (int i = 0; i < 5; i++) { try { bowl.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "佐助").start(); new Thread(() -> { for (int i = 0; i < 5; i++) { try { bowl.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } }, "鸣人").start(); } } class BigBowl { //注意如果用Integer包装类的话则要使用构造函数等方法给number赋初值,否则默认为null会导致错误 private int number; private Lock lock = new ReentrantLock(); private Condition condition = lock.newCondition(); public void increment() throws InterruptedException { /** * 在使用阻塞等待获取锁的方式中,必须在try代码块之外,并且在加锁方法与try代码块之间没有任何可能抛出异常的方法调用,避免加锁成功后,在finally中无法解锁。 说明一:如果在lock方法与try代码块之间的方法调用抛出异常,那么无法解锁,造成其它线程无法成功获取锁。 说明二:如果lock方法在try代码块之内,可能由于其它方法抛出异常,导致在finally代码块中,unlock对未加锁的对象解锁,它会调用AQS的tryRelease方法(取决于具体实现类),抛出IllegalMonitorStateException异常。 说明三:在Lock对象的lock方法实现中可能抛出unchecked异常,产生的后果与说明二相同。 java.concurrent.LockShouldWithTryFinallyRule.rule.desc Positive example: Lock lock = new XxxLock(); // ... lock.lock(); try { doSomething(); doOthers(); } finally { lock.unlock(); } Negative example: Lock lock = new XxxLock(); // ... try { // If an exception is thrown here, the finally block is executed directly doSomething(); // The finally block executes regardless of whether the lock is successful or not lock.lock(); doOthers(); } finally { lock.unlock(); } */ lock.lock(); try { //注意多个线程,即超过2个线程通信时的虚假唤醒问题,这时候不能用if判断,而要用while循环判断 while (number != 0) { condition.await(); } number++; System.out.println(Thread.currentThread().getName() + "生产了" + number + "碗拉面"); condition.signalAll(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } public void decrement() throws InterruptedException { lock.lock(); try { while (number == 0) { condition.await(); } number--; System.out.println(Thread.currentThread().getName() + "消费了1碗拉面,还剩" + number); condition.signalAll(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } } Condition——顺序打印ABC

Condition用于精准地通知和唤醒线程。

注意:任何一种新的技术,绝对不是仅仅覆盖了原有的技术,还包含对原有技术的补充和增加优势改善。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyxsxy.html