1 Lock基本使用
Lock能實現代碼同步,它比synchronized更具靈活性,什麼時候鎖住,什麼時候釋放鎖等都是看得見的,使用時必須使用try{}finally{},意思是萬一發生異常或者錯誤都可以釋放鎖。
try{
}finally{
//釋放鎖
}
- 使用示例
public class SaleTicket implements Runnable {
private int ticket = 10 * 100000;
private Lock mLock = new ReentrantLock();
@Override
public void run() {
try {
while (ticket > 0) {
mLock.lock();
if(ticket>0){
if (ticket % 100000 == 0) {
System.out.println("名稱:" + Thread.currentThread().getName() + "窗口賣出第" + (ticket / 100000) + "張票");
}
ticket--;
}
}
} finally {
mLock.unlock();
}
}
}
Runnable saleTicket = new SaleTicket();
Thread thread1 = new Thread(saleTicket);
Thread thread2 = new Thread(saleTicket);
Thread thread3 = new Thread(saleTicket);
Thread thread4 = new Thread(saleTicket);
thread1.start();
thread2.start();
thread3.start();
thread4.start();
結果:
2 ReentrantLock
ReentrantLock有兩種鎖機制:忽略中斷鎖和響應中斷鎖。如:如果A、B兩個線程去競爭鎖,A獲得鎖,B等待,但A線程要做的事情太多,一直不返回鎖,B線程就想先中斷自己不再等待這個鎖,轉而去處理其他事情。這時候ReentrantLock提供了2種機制,第一種,B線程中斷自己(或者別的線程中斷它),但是ReentrantLock不去響應,繼續讓B線程等待,這就是忽略中斷鎖(lock());第二種,B線程中斷自己(或者別的線程中斷它),ReentrantLock處理了這個中斷,並且不再等待這個鎖的到來,完全放棄,這就是響應中斷鎖(lockInterruptibly())。
- 響應中斷鎖示例:
public class BufferInterruptibly {
private ReentrantLock mLock = new ReentrantLock();
public void write(){
mLock.lock();
try {
long startTime = System.currentTimeMillis();
System.out.println("開始往這個buff寫入數據…");
for (; ; ) {
if (System.currentTimeMillis() - startTime > Integer.MAX_VALUE) {
break;
}
}
System.out.println("終於寫完了");
}finally {
mLock.unlock();
}
}
public void read() throws InterruptedException{
mLock.lockInterruptibly();
try{
System.out.println("從這個buff讀數據");
}finally {
mLock.unlock();
}
}
}
final BufferInterruptibly buff = new BufferInterruptibly();
Thread write = new Thread(new Runnable() {
@Override
public void run() {
buff.write();
}
});
final Thread read = new Thread(new Runnable() {
@Override
public void run() {
try{
Thread.sleep(500);
buff.read();
}catch (Exception e){
System.out.println("我不讀了");
}
System.out.println("讀結束");
}
});
write.start();
read.start();
new Thread(new Runnable() {
@Override
public void run() {
long start = System.currentTimeMillis();
for (;;){
if (System.currentTimeMillis()-start>500){
System.out.println("不等了,嘗試中斷");
read.interrupt();
break;
}
}
}
}).start();
結果:
ReentrantLock叫做重入鎖,意思是外層方法獲取到鎖後,內層方法會自動獲取到鎖。synchronized也是可重入鎖。我們先看一下synchronized鎖的可重入性
class SychronizedReentrant implements Runnable{
private Object object = new Object();
/**
* 方法一調用方法二
*/
public void method1(){
synchronized (object){
System.out.println(Thread.currentThread().getName()+ " method1 ");
method2();
}
}
/**
方法二,打印獲取obj鎖
如果是同一線程,鎖不可以重入,method2需要等待method1釋放鎖
*/
public void method2(){
synchronized (object){
System.out.println(Thread.currentThread().getName()+ " method2 ");
}
}
@Override
public void run() {
method1();
}
}
結果
Thread-0 method1
Thread-0 method2
method1有一個同步塊,同步塊中調用了method2,而method2中也有個同步塊,一般來說,synchronized塊裏面的內容執行完纔會釋放鎖,其它synchronized塊才能競爭到鎖,而向上述調用步驟,明顯是外部方法的synchronized還沒有釋放鎖,內部方法的synchronized就可以得到鎖,這就是重入鎖。
例子(摘抄網上):
package tags;
import java.util.Calendar;
public class TestLock {
private ReentrantLock lock = null;
public int data = 100; // 用於線程同步訪問的共享數據
public TestLock() {
lock = new ReentrantLock(); // 創建一個自由競爭的可重入鎖
}
public ReentrantLock getLock() {
return lock;
}
public void testReentry() {
lock.lock();
Calendar now = Calendar.getInstance();
System.out.println(now.getTime() + " " + Thread.currentThread() + " get lock.");
}
public static void main(String[] args) {
TestLock tester = new TestLock();
//1、測試可重入
tester.testReentry();
tester.testReentry(); // 能執行到這裏而不阻塞,表示鎖可重入
tester.testReentry(); // 再次重入
// 釋放重入測試的鎖,要按重入的數量解鎖,否則其他線程無法獲取該鎖。
tester.getLock().unlock();
tester.getLock().unlock();
tester.getLock().unlock();
//2、測試互斥
// 啓動3個線程測試在鎖保護下的共享數據data的訪問
new Thread(new workerThread(tester)).start();
new Thread(new workerThread(tester)).start();
new Thread(new workerThread(tester)).start();
}
// 線程調用的方法
public void testRun() throws Exception {
lock.lock();
Calendar now = Calendar.getInstance();
try {
// 獲取鎖後顯示 當前時間 當前調用線程 共享數據的值(並使共享數據 + 1)
System.out.println(now.getTime() + " " + Thread.currentThread()+ " accesses the data " + data++);
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
// 工作線程,調用TestServer.testRun
class workerThread implements Runnable {
private TestLock tester = null;
public workerThread(TestLock testLock) {
this.tester = testLock;
}
public void run() {
try {
tester.testRun();
} catch (Exception e) {
e.printStackTrace();
}
}
}