1. 程式人生 > >java-jdk1.5新增的併發包內容

java-jdk1.5新增的併發包內容

1.各種同步控制工具的使用:

1.1 ReentrantLock

  1.1.1 可重入

    ReentrantLock和Synchronized比較:

       ReentrantLock:需要在finally進行釋放鎖,且可以重入,即可以反覆得到相同的一把鎖,它有一個與鎖相關的計數器,如果擁有鎖的某個執行緒再次得到這把鎖,這個計數器將會+1,然後鎖需要釋放兩次才需要真正釋放。

       Synchronized:釋放鎖是由JVM實現的。當這把鎖被一個執行緒所佔據,其他執行緒只能等待這把鎖被釋放,才可以進入。

      相比之下,ReentrantLock比Synchronized效能更好,如果只是簡單的實現,建議使用Synchronized,不要刻意使用ReentrantLock。

   下圖是使用Synchronized的程式碼及效果展示:

package test;

public class TestTest1 implements Runnable {

	public synchronized void method2() {
		System.out.println("method2=" + Thread.currentThread().getId());
		method1();
	}

	public synchronized void method1() {
		System.out.println("method1=" + Thread.currentThread().getId());
	}

	@Override
	public void run() {
		method2();
	}

	public static void main(String[] args) {
		TestTest1 test = new TestTest1();
		Thread thread1=new Thread(test);
		thread1.start();
		Thread thread2=new Thread(test);
		thread2.start();
		Thread thread3=new Thread(test);
		thread3.start();
	}

}

輸出如下圖所示:
           

使用ReentrantLock的程式碼及效果展示:

package test;

import java.util.concurrent.locks.ReentrantLock;

public class TestTest2 implements Runnable {
	ReentrantLock lock = new ReentrantLock();

	public void get() {
		lock.lock();
		System.out.println("get=" + Thread.currentThread().getId());
		set();
		//lock.unlock();
	}

	public void set() {
		lock.lock();
		System.out.println("set=" + Thread.currentThread().getId());
		lock.unlock();
	}

	@Override
	public void run() {
		get();
	}

	public static void main(String[] args) {
		TestTest2 test = new TestTest2();
		Thread thread1 = new Thread(test);
		thread1.start();
		Thread thread2 = new Thread(test);
		thread2.start();
		Thread thread3 = new Thread(test);
		thread3.start();
	}

}

從結果可以看出,使用ReentrantLock,需要手動釋放鎖,否則就會等待。 


將程式碼註釋去掉,就等同於Synchronized,結果如下:

也可以在finally塊中加入lock.unlock();

   1.1.2 可中斷

ReentrantLock中的lock.lockInterruptibly(),可以捕捉到中斷的異常,而lock.lock()則不能,示例如下:

package test;

import java.util.concurrent.locks.ReentrantLock;

public class TestTest3 implements Runnable {
	static ReentrantLock lock = new ReentrantLock();

	@Override
	public void run() {
		lock.lock();
//		try {
//			lock.lockInterruptibly();
//		} catch (InterruptedException e) {
//			e.printStackTrace();
//		}
		System.out.println(Thread.currentThread().getName() + " interrupted");
	}

	public static void main(String[] args) {
		lock = new ReentrantLock();
		lock.lock();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
		TestTest3 test = new TestTest3();
		try {
			Thread thread1 = new Thread(test);
			thread1.start();
			Thread.sleep(1000);
			thread1.interrupt();
			Thread.sleep(10000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

	}

}

結果顯示沒有報異常,只是一直在休眠。

而把lock.lock()註釋掉,使用lock.lockInterruptibly(),可以捕捉到異常,如下圖所示:


這就證明了其可中斷性。

總結:

1. 執行緒A和B都要獲取物件O的鎖定,假設A獲取了物件O鎖,B將等待A釋放對O的鎖定,
    如果使用 synchronized ,如果A不釋放,B將一直等下去,不能被中斷
    如果 使用ReentrantLock,如果A不釋放,可以使B在等待了足夠長的時間以後,中斷等待,而幹別的事情

2.ReentrantLock獲取鎖定與三種方式:
    
a) lock(), 如果獲取了鎖立即返回,如果別的執行緒持有鎖,當前執行緒則一直處於休眠狀態,直到獲取鎖
    
b) tryLock(), 如果獲取了鎖立即返回true,如果別的執行緒正持有鎖,立即返回false;
    
c)tryLock(long timeout,TimeUnit unit), 如果獲取了鎖定立即返回true,如果別的執行緒正持有鎖,會等待引數給定的時間,在等待的過程中,如果獲取了鎖定,就返回true,如果等待超時,返回false;
    
d) lockInterruptibly:如果獲取了鎖定立即返回,如果沒有獲取鎖定,當前執行緒處於休眠狀態,直到或者鎖定,或者當前執行緒被別的執行緒中斷

3.synchronized是在JVM層面上實現的,不但可以通過一些監控工具監控synchronized的鎖定,而且在程式碼執行時出現異常,JVM會自動釋放鎖定,但是使用Lock則不行,lock是通過程式碼實現的,要保證鎖定一定會被釋放,就必須將unLock()放到finally{}中