题目一:synchronized和Lock有什么区别?用新的Lock有什么好处?请举例说明。 |
1、原始构成:
synchronized:属于JVM层面,它是java的关键字
底层是通过monitor对象来完成的,其实wait/notify等方法也依赖于monitor对象,所以只有在synchronized同步方法或者同步代码块中才能调用wait/notify等方法;
lock:是具体类(java.util.concurrent.locks.Lock)是api层面的锁;
2、使用方法:
synchronized:不需要用户手动释放锁,当synchronized代码执行完后系统会自动让线程释放对锁的占用;
ReentrantLock:则需要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象;(且上锁几次,必须解锁几次)
3、等待是否可中断:
synchronized:不可中断,除非抛出异常或者正常运行完成;
ReentrantLock:可中断:
a. 设置超时方法 tryLock(Long timeout, TimeUnit unit);
b. LockInterruptibly()放代码块中,调用interrupt()方法可中断;
4、加锁是否公平:
synchronized:是非公平锁;
ReentrantLock:虽然默认也是非公平锁,但是在new构造的时候,可以通过传入true,构造成公平锁;
5、锁绑定多个条件condition:
synchronized:没有condition这种说法,要么随机唤醒一个,要么全部唤醒;
ReentrantLock:用来实现分组唤醒需要唤醒的线程们,而且可以精确唤醒,而不是像synchronized那样;
题目二、多线程之间按顺序调用,实现A——>B——>C三个线程启动,要求如下: |
AA打印5次,BB打印10次,CC打印15次 紧接着 AA打印5次,BB打印10次,CC打印15次 循环10轮; |
分析:这种精确唤醒的问题,必须得使用lock方式解决;
实现代码:
package com.jiguiquan.www; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class SyncAndReentrantLockDemo { public static void main(String[] args) { //线程操纵资源类 ShareResource shareResource = new ShareResource(); new Thread(() -> { for (int i = 1; i <= 10; i++) { shareResource.print5(); } },"AA").start(); new Thread(() -> { for (int i = 1; i <= 10; i++) { shareResource.print10(); } },"BB").start(); new Thread(() -> { for (int i = 1; i <= 10; i++) { shareResource.print15(); } },"CC").start(); } } class ShareResource { private int number = 1; private Lock lock = new ReentrantLock(); private Condition c1 = lock.newCondition(); private Condition c2 = lock.newCondition(); private Condition c3 = lock.newCondition(); public void print5() { lock.lock(); try { //1、判断 while (number != 1) { c1.await(); } //2、干活 for (int i = 1; i <= 5; i++) { System.out.println(Thread.currentThread().getName()+"\t "+i); } //3、唤醒通知 number = 2; c2.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void print10() { lock.lock(); try { //1、判断 while (number != 2) { c2.await(); } //2、干活 for (int i = 1; i <= 10; i++) { System.out.println(Thread.currentThread().getName()+"\t "+i); } //3、唤醒通知 number = 3; c3.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void print15() { lock.lock(); try { //1、判断 while (number != 3) { c3.await(); } //2、干活 for (int i = 1; i <= 15; i++) { System.out.println(Thread.currentThread().getName()+"\t "+i); } //3、唤醒通知 number = 1; c1.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }
执行结果:量太大,只截取第一轮和第二轮的部分结果
AA 1 AA 2 AA 3 AA 4 AA 5 BB 1 BB 2 BB 3 BB 4 BB 5 BB 6 BB 7 BB 8 BB 9 BB 10 CC 1 CC 2 CC 3 CC 4 CC 5 CC 6 CC 7 CC 8 CC 9 CC 10 CC 11 CC 12 CC 13 CC 14 CC 15 AA 1 AA 2 AA 3 AA 4 AA 5 BB 1 BB 2
结果显而易见,程序按着需求在正常运行;