synchronized是java中的一個關鍵字,也就是說是Java語言內置的特性。那麼爲什麼會出現Lock呢?
在前面文章中,我們瞭解到如果一個代碼塊被synchronized修飾了,當一個線程獲取了對應的鎖,並執行該代碼塊時,其他線程便只能一直等待,等待獲取鎖的線程釋放鎖,而這裏獲取鎖的線程釋放鎖只會有兩種情況:
1)獲取鎖的線程執行完了該代碼塊,然後線程釋放對鎖的佔有;
2)線程執行發生異常,此時JVM會讓線程自動釋放鎖。
那麼如果這個獲取鎖的線程由於要等待IO或者其他原因(比如調用sleep方法)被阻塞了,但是又沒有釋放鎖,其他線程便只能乾巴巴地等待,試想一下,這多麼影響程序執行效率。
因此就需要有一種機制可以不讓等待的線程一直無期限地等待下去(比如只等待一定的時間或者能夠響應中斷),通過Lock就可以辦到。
再舉個例子:當有多個線程讀寫文件時,讀操作和寫操作會發生衝突現象,寫操作和寫操作會發生衝突現象,但是讀操作和讀操作不會發生衝突現象。
但是採用synchronized關鍵字來實現同步的話,就會導致一個問題:
如果多個線程都只是進行讀操作,所以當一個線程在進行讀操作時,其他線程只能等待無法進行讀操作。
因此就需要一種機制來使得多個線程都只是進行讀操作時,線程之間不會發生衝突,通過Lock就可以辦到。
另外,通過Lock可以知道線程有沒有成功獲取到鎖。這個是synchronized無法辦到的。
總結一下,也就是說Lock提供了比synchronized更多的功能。但是要注意以下幾點:
1)Lock不是Java語言內置的,synchronized是Java語言的關鍵字,因此是內置特性。Lock是一個類,通過這個類可以實現同步訪問;
2)Lock和synchronized有一點非常大的不同,採用synchronized不需要用戶去手動釋放鎖,當synchronized方法或者synchronized代碼塊執行完之後,系統會自動讓線程釋放對鎖的佔用;而Lock則必須要用戶去手動釋放鎖,如果沒有主動釋放鎖,就有可能導致出現死鎖現象。
我們先看下lock的簡單用法:
先看下不加lock的賬戶代碼:
public class Account {
public void add(BigDecimal amount , String name) {
System.out.println(" 開始充值,線程名: " + Thread.currentThread().getName());
try {
Thread.sleep(2000);
System.out.println(" 結束充值,線程名: " + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
}
}
}
看下充值代碼:
package com.ck.thread;
import java.math.BigDecimal;
public class CzThread extends Thread{
private Account account;
private String accountName;
public CzThread(Account account) {
this.account = account;
}
@Override
public void run() {
account.add(new BigDecimal("100"), accountName);
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
public String getAccountName() {
return accountName;
}
public void setAccountName(String accountName) {
this.accountName = accountName;
}
}
再看下測試類:
package com.ck.thread;
public class MainThread {
public static void main(String[] args) throws InterruptedException {
Account account = new Account();
CzThread cz1 = new CzThread(account);
cz1.setName("thread1");
cz1.start();
CzThread cz2 = new CzThread(account);
cz2.setName("thread2");
cz2.start();
}
}
運行結果:
開始充值,線程名: thread2
開始充值,線程名: thread1
結束充值,線程名: thread1
結束充值,線程名: thread2
我們通過結果可以看出來,賬戶類是不安全的,然後我們修改下賬戶類:
package com.ck.thread;
import java.math.BigDecimal;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Account {
private Lock lock = new ReentrantLock();
public void add(BigDecimal amount , String name) {
lock.lock();
System.out.println(" 開始充值,線程名: " + Thread.currentThread().getName());
try {
Thread.sleep(2000);
System.out.println(" 結束充值,線程名: " + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
我們加上了lock鎖,然後看下運行結果:
開始充值,線程名: thread1
結束充值,線程名: thread1
開始充值,線程名: thread2
結束充值,線程名: thread2
我們看到lock的第一個作用與synchronized一樣,具有保護線程安全的作用。