synchronized 和 Lock的區別
- synchronized是java中的一個關鍵字,也就是說是Java語言內置的特性。
- Lock不是Java語言內置的,Lock是一個接口。
- Lock和synchronized有一點非常大的不同,採用synchronized不需要用戶去手動釋放鎖,當synchronized方法或者synchronized代碼塊執行完之後,系統會自動讓線程釋放對鎖的佔用;而Lock則必須要用戶去手動釋放鎖,如果沒有主動釋放鎖,就有可能導致出現死鎖現象。
synchronized 鎖方法, 這個方法將不能被同時訪問。
synchronized 鎖對象,這個對象將不能被同時訪問。
當synchronized方法或者synchronized代碼塊執行完之後,系統會自動讓線程釋放對鎖的佔用
synchronized 搶單的實現
- 首先對於搶單我們不能鎖住整個方法,我們應該鎖住的是單號。單號是字符串,存在於常量池。所以我們可以鎖住相同的單號。
// 訂單類
class OrderDemo {
private String name;
private String orderNum;
public OrderDemo(String name, String orderNum) {
this.name = name;
this.orderNum = orderNum;
}
public String getOrderNum() {
return orderNum;
}
public String getName() {
return name;
}
}
// 業務方法
public static void test(OrderDemo orderDemo) {
synchronized (orderDemo.getOrderNum()){
System.out.println(orderDemo.getName() + "獲得了鎖");
System.out.println(orderDemo.getName() + "釋放了鎖");
}
}
// 測試方法
public static void main(String[] a){
OrderDemo orderDemo = new OrderDemo("線程1","001002003");
Thread thread = new Thread(() -> test(orderDemo));
OrderDemo orderDemo2 = new OrderDemo("線程2","001002003");
Thread thread2 = new Thread(() -> test(orderDemo2));
OrderDemo orderDemo3 = new OrderDemo("線程3","001002003");
Thread thread3 = new Thread(() -> test(orderDemo3));
OrderDemo orderDemo4 = new OrderDemo("線程4","001002004");
Thread thread4 = new Thread(() -> test(orderDemo4));
thread.start();
thread2.start();
thread3.start();
thread4.start();
}
打印結果如下
線程1獲得了鎖
線程4獲得了鎖
線程4釋放了鎖
線程1釋放了鎖
線程3獲得了鎖
線程3釋放了鎖
線程2獲得了鎖
線程2釋放了鎖
- 可以看到,對於相同訂單號(001002003)的訂單,會鎖住訂單號,一直等到鎖釋放。下一個訂單纔可以進行處理。
- 對於訂單號不同的訂單會並行執行。
Lock
方法 | 說明 | 參數說明 |
---|---|---|
lock() | 獲得鎖 | |
tryLock() | 返回boolean, ture代表鎖沒有被佔用,獲得鎖。false代表鎖被佔用 | |
tryLock(long time, TimeUnit unit) | 返回boolean, 等待time時長後,鎖還沒有被釋放。返回false。time時長內釋放了返回true, 並獲得鎖 | time:時長, unit:時間類型 |
lockInterruptibly() | 獲得鎖,和lock()不同,舉例:當A線程處於等待時(對執行中的線程無效),其餘的線程可調用A線程的interrupt()來中斷A線程,A線程直接返回,並拋出InterruptException異常。如果是lock()的話,A線程還是會繼續等待並執行,只是給A線程做了箇中斷標誌 | |
unlock() | 釋放鎖 |
lock()
// 業務測試
public static void test2(OrderDemo orderDemo) {
Lock lock = map.get(orderDemo.getOrderNum());
lock.lock();
try {
System.out.println(orderDemo.getName()+"得到了鎖");
Thread.sleep(1000);
}catch (Exception e){
e.printStackTrace();
}finally {
System.out.println(orderDemo.getName()+"釋放了鎖");
lock.unlock();
}
}
// 測試
public static void main(String[] a){
Lock lock1 = new ReentrantLock();
map.put("001002003", lock1);
Lock lock2 = new ReentrantLock();
map.put("001002004", lock2);
OrderDemo orderDemo = new OrderDemo("線程1","001002003");
Thread thread = new Thread(() -> test2(orderDemo));
OrderDemo orderDemo2 = new OrderDemo("線程2","001002003");
Thread thread2 = new Thread(() -> test2(orderDemo2));
OrderDemo orderDemo3 = new OrderDemo("線程3","001002003");
Thread thread3 = new Thread(() -> test2(orderDemo3));
OrderDemo orderDemo4 = new OrderDemo("線程4","001002004");
Thread thread4 = new Thread(() -> test2(orderDemo4));
OrderDemo orderDemo5= new OrderDemo("線程5","001002004");
Thread thread5 = new Thread(() -> test2(orderDemo5));
thread.start();
thread2.start();
thread3.start();
thread4.start();
thread5.start();
}
打印如下
線程1得到了鎖
線程5得到了鎖
線程5釋放了鎖
線程4得到了鎖
線程1釋放了鎖
線程3得到了鎖
線程3釋放了鎖
線程4釋放了鎖
線程2得到了鎖
線程2釋放了鎖
1. 將訂單號和Lock 進行綁定。確保相同的單號共享一個鎖。
2. 可以看到,對於相同訂單號的訂單,會鎖住訂單號,一直等到鎖釋放。下一個訂單纔可以進行處理。
3. 對於訂單號不同的訂單會並行執行。
tryLock()
// 業務方法
public static void test3(OrderDemo orderDemo) {
Lock lock = map.get(orderDemo.getOrderNum());
if (lock.tryLock()) {
try {
System.out.println(orderDemo.getName() + "得到了鎖");
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println(orderDemo.getName() + "釋放了鎖");
lock.unlock();
}
} else {
System.out.println(orderDemo.getName() + "獲取鎖失敗, 鎖已被佔用");
}
}
// 測試方法
public static void main(String[] a){
Lock lock1 = new ReentrantLock();
map.put("001002003", lock1);
Lock lock2 = new ReentrantLock();
map.put("001002004", lock2);
OrderDemo orderDemo = new OrderDemo("線程1","001002003");
Thread thread = new Thread(() -> test3(orderDemo));
OrderDemo orderDemo2 = new OrderDemo("線程2","001002003");
Thread thread2 = new Thread(() -> test3(orderDemo2));
OrderDemo orderDemo3 = new OrderDemo("線程3","001002003");
Thread thread3 = new Thread(() -> test3(orderDemo3));
OrderDemo orderDemo4 = new OrderDemo("線程4","001002004");
Thread thread4 = new Thread(() -> test3(orderDemo4));
OrderDemo orderDemo5= new OrderDemo("線程5","001002004");
Thread thread5 = new Thread(() -> test3(orderDemo5));
thread.start();
thread2.start();
thread3.start();
thread4.start();
thread5.start();
}
打印如下
線程1得到了鎖
線程4得到了鎖
線程2獲取鎖失敗, 鎖已被佔用
線程5獲取鎖失敗, 鎖已被佔用
線程3獲取鎖失敗, 鎖已被佔用
線程4釋放了鎖
線程1釋放了鎖
我們可以看到線程1得到了鎖,而和它共享鎖的線程2和線程3都沒有得到鎖。
線程4得到了鎖,而和它共享鎖的線程5沒有得到鎖。
tryLock(long time, TimeUnit unit)
// 業務方法
public static void test4(OrderDemo orderDemo){
Lock lock = map.get(orderDemo.getOrderNum());
try {
if (lock.tryLock(5, TimeUnit.SECONDS)) {
try {
System.out.println(orderDemo.getName() + "得到了鎖");
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println(orderDemo.getName() + "釋放了鎖");
lock.unlock();
}
} else {
System.out.println(orderDemo.getName() + "獲取鎖失敗, 鎖已被佔用");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 測試方法
public static void main(String[] a){
Lock lock1 = new ReentrantLock();
map.put("001002003", lock1);
Lock lock2 = new ReentrantLock();
map.put("001002004", lock2);
OrderDemo orderDemo = new OrderDemo("線程1","001002003");
Thread thread = new Thread(() -> test4(orderDemo));
OrderDemo orderDemo2 = new OrderDemo("線程2","001002003");
Thread thread2 = new Thread(() -> test4(orderDemo2));
OrderDemo orderDemo3 = new OrderDemo("線程3","001002003");
Thread thread3 = new Thread(() -> test4(orderDemo3));
OrderDemo orderDemo4 = new OrderDemo("線程4","001002004");
Thread thread4 = new Thread(() -> test4(orderDemo4));
OrderDemo orderDemo5= new OrderDemo("線程5","001002004");
Thread thread5 = new Thread(() -> test4(orderDemo5));
thread.start();
thread2.start();
thread3.start();
thread4.start();
thread5.start();
}
打印
線程1得到了鎖
線程4得到了鎖
線程4釋放了鎖
線程1釋放了鎖
線程5得到了鎖
線程2得到了鎖
線程3獲取鎖失敗, 鎖已被佔用
線程5釋放了鎖
線程2釋放了鎖
1. 程序中寫了每個線程最少會鎖3秒的時間。但線程只會等待五秒。現在線程(1,2,3)共享一個鎖,線程(4,5)共享一個鎖。可以猜到,線程(1,2,3)會只有兩個會獲取到鎖,而線程(4,5)會都獲得鎖。而打印也證實了我們的猜測。
lockInterruptibly()
// 業務
public static void test5(OrderDemo orderDemo, Thread thread) throws InterruptedException {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
Lock lock = map.get(orderDemo.getOrderNum());
lock.lockInterruptibly();
System.out.println(dateFormat.format(new Date()) + ":" + orderDemo.getName() + "得到了鎖");
try {
// 判斷線程是否被中斷
if (!thread.isInterrupted()) {
Thread.sleep(2000);
} else {
System.out.println(dateFormat.format(new Date()) + ":" + orderDemo.getName() + "被中斷");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println(dateFormat.format(new Date()) + ":" + orderDemo.getName() + "釋放了鎖");
lock.unlock();
}
}
// 測試方法
public static void main(String[] a) {
Lock lock1 = new ReentrantLock();
map.put("001002003", lock1);
OrderDemo orderDemo = new OrderDemo("線程1", "001002003");
Thread thread = new Thread(() -> {
try {
test5(orderDemo, Thread.currentThread());
} catch (InterruptedException e) {
e.printStackTrace();
}
});
OrderDemo orderDemo2 = new OrderDemo("線程2", "001002003");
Thread thread2 = new Thread(() -> {
try {
test5(orderDemo2, Thread.currentThread());
} catch (InterruptedException e) {
e.printStackTrace();
}
});
OrderDemo orderDemo3 = new OrderDemo("線程3", "001002003");
Thread thread3 = new Thread(() -> {
try {
test5(orderDemo3, Thread.currentThread());
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.start();
thread2.start();
thread3.start();
try {
Thread.sleep(1000);
thread3.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
打印
2019年08月28日 18:06:29:線程1得到了鎖
java.lang.InterruptedException
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
at com.lzq.collection.SynchronizedDemo.test5(SynchronizedDemo.java:130)
at com.lzq.collection.SynchronizedDemo.lambda$main$2(SynchronizedDemo.java:47)
at java.lang.Thread.run(Thread.java:745)
2019年08月28日 18:06:31:線程1釋放了鎖
2019年08月28日 18:06:31:線程2得到了鎖
2019年08月28日 18:06:33:線程2釋放了鎖
此代碼實現在1秒後中斷線程3的線程。使用lockInterruptibly獲取鎖的話,線程3會直接中斷返回並拋出InterruptedException異常。線程不會繼續等待。
如果 lock.lockInterruptibly() 換成 lock.lock() 線程3不會被立即中斷。還是會繼續等待並執行。只是對線程3做了箇中斷狀態。
換成lock.lock()打印如下
2019年08月28日 18:08:32:線程1得到了鎖
2019年08月28日 18:08:34:線程1釋放了鎖
2019年08月28日 18:08:34:線程2得到了鎖
2019年08月28日 18:08:36:線程2釋放了鎖
2019年08月28日 18:08:36:線程3得到了鎖
2019年08月28日 18:08:36:線程3被中斷
2019年08月28日 18:08:36:線程3釋放了鎖
可以看出線程3還是繼續等待並執行,只是做了箇中斷狀態
隨堂代碼
package com.lzq.collection;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 開發公司:個人
* 版權:個人
* <p>
* ListDemo
*
* @author 劉志強
* @created Create Time: 2019/8/26
*/
public class SynchronizedDemo {
static Map<String,Lock> map = new HashMap<>();
// public static void main(String[] a){
// Lock lock1 = new ReentrantLock();
// map.put("001002003", lock1);
// Lock lock2 = new ReentrantLock();
// map.put("001002004", lock2);
// OrderDemo orderDemo = new OrderDemo("線程1","001002003");
// Thread thread = new Thread(() -> test3(orderDemo));
// OrderDemo orderDemo2 = new OrderDemo("線程2","001002003");
// Thread thread2 = new Thread(() -> test3(orderDemo2));
// OrderDemo orderDemo3 = new OrderDemo("線程3","001002003");
// Thread thread3 = new Thread(() -> test3(orderDemo3));
// OrderDemo orderDemo4 = new OrderDemo("線程4","001002004");
// Thread thread4 = new Thread(() -> test3(orderDemo4));
// OrderDemo orderDemo5= new OrderDemo("線程5","001002004");
// Thread thread5 = new Thread(() -> test3(orderDemo5));
// thread.start();
// thread2.start();
// thread3.start();
// thread4.start();
// thread5.start();
// }
public static void main(String[] a) {
Lock lock1 = new ReentrantLock();
map.put("001002003", lock1);
OrderDemo orderDemo = new OrderDemo("線程1", "001002003");
Thread thread = new Thread(() -> {
try {
test5(orderDemo, Thread.currentThread());
} catch (InterruptedException e) {
e.printStackTrace();
}
});
OrderDemo orderDemo2 = new OrderDemo("線程2", "001002003");
Thread thread2 = new Thread(() -> {
try {
test5(orderDemo2, Thread.currentThread());
} catch (InterruptedException e) {
e.printStackTrace();
}
});
OrderDemo orderDemo3 = new OrderDemo("線程3", "001002003");
Thread thread3 = new Thread(() -> {
try {
test5(orderDemo3, Thread.currentThread());
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.start();
thread2.start();
thread3.start();
try {
Thread.sleep(1000);
thread3.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void test(OrderDemo orderDemo) {
synchronized (orderDemo.getOrderNum()){
System.out.println(orderDemo.getName() + "獲得了鎖");
System.out.println(orderDemo.getName() + "釋放了鎖");
}
}
public static void test2(OrderDemo orderDemo) {
Lock lock = map.get(orderDemo.getOrderNum());
lock.lock();
try {
System.out.println(orderDemo.getName()+"得到了鎖");
Thread.sleep(1000);
}catch (Exception e){
e.printStackTrace();
}finally {
System.out.println(orderDemo.getName()+"釋放了鎖");
lock.unlock();
}
}
public static void test3(OrderDemo orderDemo) {
Lock lock = map.get(orderDemo.getOrderNum());
if (lock.tryLock()) {
try {
System.out.println(orderDemo.getName() + "得到了鎖");
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println(orderDemo.getName() + "釋放了鎖");
lock.unlock();
}
} else {
System.out.println(orderDemo.getName() + "獲取鎖失敗, 鎖已被佔用");
}
}
public static void test4(OrderDemo orderDemo){
Lock lock = map.get(orderDemo.getOrderNum());
try {
if (lock.tryLock(5, TimeUnit.SECONDS)) {
try {
System.out.println(orderDemo.getName() + "得到了鎖");
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println(orderDemo.getName() + "釋放了鎖");
lock.unlock();
}
} else {
System.out.println(orderDemo.getName() + "獲取鎖失敗, 鎖已被佔用");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void test5(OrderDemo orderDemo, Thread thread) throws InterruptedException {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
Lock lock = map.get(orderDemo.getOrderNum());
lock.lock();
System.out.println(dateFormat.format(new Date()) + ":" + orderDemo.getName() + "得到了鎖");
try {
// 判斷線程是否被中斷
if (!thread.isInterrupted()) {
Thread.sleep(2000);
} else {
System.out.println(dateFormat.format(new Date()) + ":" + orderDemo.getName() + "被中斷");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println(dateFormat.format(new Date()) + ":" + orderDemo.getName() + "釋放了鎖");
lock.unlock();
}
}
}
class OrderDemo {
private String name;
private String orderNum;
public OrderDemo(String name, String orderNum) {
this.name = name;
this.orderNum = orderNum;
}
public String getOrderNum() {
return orderNum;
}
public String getName() {
return name;
}
}