synchronized是java中的一個關鍵字,也就是說是Java語言內置的特性。那么為什么會出現Lock呢?
如果一個代碼塊被synchronized修飾了,當一個線程獲取了對應的鎖,并執行該代碼塊時,其他線程便只能一直等待,等待獲取鎖的線程釋放鎖,而這里獲取鎖的線程釋放鎖會有三種情況:
1)獲取鎖的線程執行完了該代碼塊,然后線程釋放對鎖的占有;
2)線程執行發生異常,此時JVM會讓線程自動釋放鎖。
3)這個主要是在等待喚醒機制里面的wait()方法,//在等待的時候立即釋放鎖,方便其他的線程使用鎖。而且被喚醒時,就在此處喚醒,
那么如果這個獲取鎖的線程由于要等待IO或者其他原因(比如調用sleep方法)被阻塞了,但是又沒有釋放鎖,其他線程便只能干巴巴地等待,試想一下,這多么影響程序執行效率。因此我們需要不論程序的代碼塊執行的如何最終都將鎖對象進行釋放,方便其他線程的執行。(此處后面有一個簡單的demo起始就是將鎖對象人工的釋放而且是在finally里面的執行)
雖然我們可以理解同步代碼塊和同步方法的鎖對象問題,但是我們并沒有直接看到在哪里加上了鎖,在哪里釋放了鎖,同時為了更好地釋放鎖。 為了更清晰的表達如何加鎖和釋放鎖,JDK5以后提供了一個新的鎖對象Lock。
另外,通過Lock可以知道線程有沒有成功獲取到鎖。這個是synchronized無法辦到的。
總結一下,也就是說Lock提供了比synchronized更多的功能。但是要注意以下幾點:
1)Lock不是Java語言內置的,synchronized是Java語言的關鍵字,因此是內置特性。Lock是一個類,通過這個類可以實現同步訪問;
2)synchronized是在JVM層面上實現的,不但可以通過一些監控工具監控synchronized的鎖定,而且在代碼執行時出現異常,JVM會自動釋放鎖定,但是使用Lock則不行,lock是通過代碼實現的,要保證鎖定一定會被釋放,就必須將unLock()放到finally{}中
3)在資源競爭不是很激烈的情況下,Synchronized的性能要優于ReetrantLock,但是在資源競爭很激烈的情況下,Synchronized的性能會下降幾十倍,
但是ReetrantLock的性能能維持常態;
一、首先給出一個簡單的自鎖案例,主要是用于體會自鎖的發生即可
MyLock
package thread.lock.DieLockDemo;
public class MyLock {
// 創建兩把鎖對象
public static final Object objA = new Object();
public static final Object objB = new Object();
}
// 發生死鎖的線程
package thread.lock.DieLockDemo;
public class DieLock extends Thread {
private boolean flag;
public DieLock(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if (flag) {
synchronized (MyLock.objA) {
System.out.println("if objA");
synchronized (MyLock.objB) {
System.out.println("if objB");
}
}
} else {
synchronized (MyLock.objB) {
System.out.println("else objB");
synchronized (MyLock.objA) {
System.out.println("else objA");
}
}
}
}
}
測試代碼:
/*
* 同步的弊端:
* A:效率低
* B:容易產生死鎖
*
* 死鎖:
* 兩個或兩個以上的線程在爭奪資源的過程中,發生的一種相互等待的現象。
*
* 舉例:
* 小明和小強的自行車都有兩把鎖一人一把鑰匙案例。
* 正常情況:
* 小明: 兩把鎖的鑰匙都有;
* 小強: 兩把鎖的鑰匙都有。
* 現在:
* 小明:有其中一把鎖的兩把鑰匙;
* 小強:有另一把鎖的兩把鑰匙。
* 結局兩個人都不能打開鎖。。。。一直等待朔夜起不到自行車
*/
public class DieLockDemo {
public static void main(String[] args) {
DieLock dl1 = new DieLock(true);
DieLock dl2 = new DieLock(false);
dl1.start();
dl2.start();
}
}
運行結果
結果一: 卡死
else objB
if objA
結果二,正常結束:
if objA
if objB
else objB
else objA
死鎖不是每一次都產生,如果某一個線程先結束,則不會產生死鎖,正常結束、
二、Lock鎖的簡單使用
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SellTicket implements Runnable {
// 定義票
private int tickets = 100;
// 定義鎖對象
private Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
// 加鎖
lock.lock();
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ "正在出售第" + (tickets--) + "張票");
}
} finally {
// 釋放鎖
lock.unlock();
}
}
}
}
synchronized 方式實現縣城
@Override
public void run() {
while (true){
try {
//上鎖
synchronized (this) {
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在售出第" + (tickets--) + "張票");
} else {
break;
}
}
}catch (Exception e){
e.printStackTrace();
}
}
}