Synchronized和Lock的区别
1.前言
早期的时候我们对线程的主要操作为:synchronized wati notify
然后后面出现了替代方案:lock await singal
2.synchronized 和 lock 有什么区别?用新的lock有什么好处?举例说明
1)synchronized属于JVM层面,属于java的关键字
- monitorenter(底层是通过monitor对象来完成,其实wait/notify等方法也依赖于monitor对象 只能在同步块或者方法中才能调用 wait/ notify等方法)
- Lock是具体类(java.util.concurrent.locks.Lock)是api层面的锁
2)使用方法:
- synchronized:不需要用户去手动释放锁,当synchronized代码执行后,系统会自动让线程释放对锁的占用
- ReentrantLock:则需要用户去手动释放锁,若没有主动释放锁,就有可能出现死锁的现象,需要lock() 和 unlock() 配置try catch语句来完成
3)等待是否中断
- synchronized:不可中断,除非抛出异常或者正常运行完成
- ReentrantLock:可中断,可以设置超时方法
- 设置超时方法,trylock(long timeout, TimeUnit unit)
- lockInterrupible() 放代码块中,调用interrupt() 方法可以中断
4)加锁是否公平
- synchronized:非公平锁
- ReentrantLock:默认非公平锁,构造函数可以传递boolean值,true为公平锁,false为非公平锁
5)锁绑定多个条件Condition
- synchronized:没有,要么随机,要么全部唤醒
- ReentrantLock:用来实现分组唤醒需要唤醒的线程,可以精确唤醒,而不是像synchronized那样,要么随机,要么全部唤醒
lock使用
1.案例一
当A线程执行完后,B线程才能执行,然后B线程执行完成后,C线程才执行
首先我们需要创建一个 重入锁
1 2
| // 创建一个重入锁 private Lock lock = new ReentrantLock();
|
然后定义 三个条件,也可以称为锁的钥匙,通过它就可以获取到锁,进入到方法里面
1 2 3 4
| // 这三个相当于备用钥匙 private Condition condition1 = lock.newCondition(); private Condition condition2 = lock.newCondition(); private Condition condition3 = lock.newCondition();
|
然后开始记住锁的三部曲: 判断 干活 唤醒
这里的判断,为了避免虚假唤醒,一定要 采用 while
干活就是把需要的内容,打印出来
唤醒的话,就是修改资源类的值,然后精准唤醒线程进行干活:这里A 唤醒B, B唤醒C,C又唤醒A(链式调用)
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 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
| 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 = 0; i < 10; i++) { shareResource.print5(); } }, "A").start();
new Thread(() -> { for (int i = 0; i < 10; i++) { shareResource.print10(); } }, "B").start();
new Thread(() -> { for (int i = 0; i < 10; i++) { shareResource.print15(); } }, "C").start(); } }
class ShareResource { private int number = 1; private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition(); private Condition condition2 = lock.newCondition(); private Condition condition3 = lock.newCondition();
public void print5() { lock.lock(); try { while(number != 1) { condition1.await(); }
for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + "\t " + number + "\t" + i); }
number = 2; condition2.signal();
} catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } }
public void print10() { lock.lock(); try { while(number != 2) { condition2.await(); }
for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "\t " + number + "\t" + i); }
number = 3; condition3.signal();
} catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } }
public void print15() { lock.lock(); try { while(number != 3) { condition3.await(); }
for (int i = 0; i < 15; i++) { System.out.println(Thread.currentThread().getName() + "\t " + number + "\t" + i); }
number = 1; condition1.signal();
} 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
| A 1 0 A 1 1 A 1 2 A 1 3 A 1 4 B 2 0 B 2 1 B 2 2 B 2 3 B 2 4 B 2 5 B 2 6 B 2 7 B 2 8 B 2 9 C 3 0 C 3 1 .....
|
synchronized使用
1.同步代码块传参变量对象 (锁住的是变量对象)
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
|
public class SynchronizedTest { public static Integer lockObject;
public SynchronizedTest(Integer lockObject) { SynchronizedTest.lockObject = lockObject; }
public void test3() { synchronized (lockObject) { try { System.out.println((Thread.currentThread().getName() + " test3 进入了同步块")); Thread.sleep(500); System.out.println((Thread.currentThread().getName() + " test3 休眠结束" + --SynchronizedTest.lockObject)); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { SynchronizedTest st = new SynchronizedTest(127); SynchronizedTest st2 = new SynchronizedTest(120); new Thread(() -> { System.out.println((Thread.currentThread().getName() + " test 准备进入")); st.test3(); }).start(); new Thread(() -> { System.out.println((Thread.currentThread().getName() + " test 准备进入")); st2.test3(); }).start();
}
}
|
输出
1 2 3 4 5 6
| Thread-0 test 准备进入 Thread-0 test3 进入了同步块 Thread-1 test 准备进入 Thread-0 test3 休眠结束119 Thread-1 test3 进入了同步块 Thread-1 test3 休眠结束118
|
2.锁静态方法(全局锁):所有调用该方法的线程都会实现同步
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
|
public class SynchronizedMethedTest {
public synchronized static void test5() { try { System.out.println(Thread.currentThread().getName() + " test5 进入了同步块"); Thread.sleep(500); System.out.println(Thread.currentThread().getName() + " test5 退出了同步块"); } catch (InterruptedException e) { e.printStackTrace(); } }
public static void main(String[] args) { new Thread(() -> { System.out.println(Thread.currentThread().getName() + " test 准备进入"); test5(); }).start(); new Thread(() -> { System.out.println(Thread.currentThread().getName() + " test 准备进入"); test5(); }).start(); new Thread(() -> { System.out.println(Thread.currentThread().getName() + " test 准备进入"); SynchronizedMethedTest.test5(); }).start(); } }
|
输出
1 2 3 4 5 6 7 8 9
| Thread-0 test 准备进入 Thread-0 test5 进入了同步块 Thread-1 test 准备进入 Thread-2 test 准备进入 Thread-0 test5 退出了同步块 Thread-2 test5 进入了同步块 Thread-2 test5 退出了同步块 Thread-1 test5 进入了同步块 Thread-1 test5 退出了同步块
|
3.同步代码块传参class对象(全局锁)
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
|
public class SynchronizedClassTest {
public void test4() { synchronized (SynchronizedClassTest.class) { try { System.out.println(Thread.currentThread().getName() + " test4 进入了同步块"); Thread.sleep(500); System.out.println(Thread.currentThread().getName() + " test4 退出了同步块"); } catch (InterruptedException e) { e.printStackTrace(); } } }
public static void main(String[] args) { SynchronizedClassTest st = new SynchronizedClassTest(); SynchronizedClassTest st2 = new SynchronizedClassTest(); new Thread(() -> { System.out.println(Thread.currentThread().getName() + " test 准备进入"); st.test4(); }).start(); new Thread(() -> { System.out.println(Thread.currentThread().getName() + " test 准备进入"); st2.test4(); }).start(); } }
|
输出
1 2 3 4 5 6
| Thread-0 test 准备进入 Thread-1 test 准备进入 Thread-0 test4 进入了同步块 Thread-0 test4 退出了同步块 Thread-1 test4 进入了同步块 Thread-1 test4 退出了同步块
|