轉載自:http://blog.csdn.net/ymeng_bupt/article/details/6826936
在併發環境下,解決共享資源衝突問題時,可以考慮使用鎖機制。
1. 對象鎖
所有對象都自動含有單一的鎖。
JVM負責跟蹤對象被加鎖的次數。如果一個對象被解鎖,其計數變爲0。在任務(線程)第一次給對象加鎖的時候,計數變爲1。每當這個相同的任務(線程)在此對象上獲得鎖時,計數會遞增。
只有首先獲得鎖的任務(線程)才能繼續獲取該對象上的多個鎖。
每當任務離開一個synchronized方法,計數遞減,當計數爲0的時候,鎖被完全釋放,此時別的任務就可以使用此資源。
2. 類鎖
對於同步靜態方法/靜態變量互斥體,由於一個class不論被實例化多少次,其中的靜態方法和靜態變量在內存中都只由一份。所以,一旦一個靜態的方法被申明爲synchronized。此類所有的實例化對象在調用此方法,共用同一把鎖,我們稱之爲類鎖。一旦一個靜態變量被作爲synchronized block的互斥體。進入此同步區域時,都要先獲得此靜態變量的對象鎖。
由上述同步靜態方法引申出一個概念,那就是類鎖。其實系統中並不存在什麼類鎖。當一個同步靜態方法被調用時,系統獲取的其實就是代表該類的類對象的對象鎖
可以嘗試用以下方式獲取類鎖
3. synchronized同步塊
3.1. 同步到單一對象鎖
當使用同步塊時,如果方法下的同步塊都同步到一個對象上的鎖,則所有的任務(線程)只能互斥的進入這些同步塊。
Resource1.java演示了三個線程(包括main線程)試圖進入某個類的三個不同的方法的同步塊中,雖然這些同步塊處在不同的方法中,但由於是同步到同一個對象(當前對象 synchronized (this)),所以對它們的方法依然是互斥的。
Resource1.java
- package com.zj.lock;
- import java.util.concurrent.TimeUnit;
- public class Resource1 {
- public void f() {
- // other operations should not be locked…
- System.out.println(Thread.currentThread().getName()
- + ”:not synchronized in f()”);
- synchronized (this) {
- for (int i = 0; i < 5; i++) {
- System.out.println(Thread.currentThread().getName()
- + ”:synchronized in f()”);
- try {
- TimeUnit.SECONDS.sleep(3);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
- public void g() {
- // other operations should not be locked…
- System.out.println(Thread.currentThread().getName()
- + ”:not synchronized in g()”);
- synchronized (this) {
- for (int i = 0; i < 5; i++) {
- System.out.println(Thread.currentThread().getName()
- + ”:synchronized in g()”);
- try {
- TimeUnit.SECONDS.sleep(3);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
- public void h() {
- // other operations should not be locked…
- System.out.println(Thread.currentThread().getName()
- + ”:not synchronized in h()”);
- synchronized (this) {
- for (int i = 0; i < 5; i++) {
- System.out.println(Thread.currentThread().getName()
- + ”:synchronized in h()”);
- try {
- TimeUnit.SECONDS.sleep(3);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
- public static void main(String[] args) {
- final Resource1 rs = new Resource1();
- new Thread() {
- public void run() {
- rs.f();
- }
- }.start();
- new Thread() {
- public void run() {
- rs.g();
- }
- }.start();
- rs.h();
- }
- }
Thread-0:not synchronized in f()
Thread-0:synchronized in f()
main:not synchronized in h()
Thread-1:not synchronized in g()
Thread-0:synchronized in f()
Thread-0:synchronized in f()
Thread-0:synchronized in f()
Thread-0:synchronized in f()
Thread-1:synchronized in g()
Thread-1:synchronized in g()
Thread-1:synchronized in g()
Thread-1:synchronized in g()
Thread-1:synchronized in g()
main:synchronized in h()
main:synchronized in h()
main:synchronized in h()
main:synchronized in h()
main:synchronized in h()
3.2. 同步到多個對象鎖
Resource1.java演示了三個線程(包括main線程)試圖進入某個類的三個不同的方法的同步塊中,這些同步塊處在不同的方法中,並且是同步到三個不同的對象(synchronized (this),synchronized(syncObject1),synchronized (syncObject2)),所以對它們的方法中的臨界資源訪問是獨立的。
Resource2.java
- package com.zj.lock;
- import java.util.concurrent.TimeUnit;
- public class Resource2 {
- private Object syncObject1 = new Object();
- private Object syncObject2 = new Object();
- public void f() {
- // other operations should not be locked…
- System.out.println(Thread.currentThread().getName()
- + ”:not synchronized in f()”);
- synchronized (this) {
- for (int i = 0; i < 5; i++) {
- System.out.println(Thread.currentThread().getName()
- + ”:synchronized in f()”);
- try {
- TimeUnit.SECONDS.sleep(3);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
- public void g() {
- // other operations should not be locked…
- System.out.println(Thread.currentThread().getName()
- + ”:not synchronized in g()”);
- synchronized (syncObject1) {
- for (int i = 0; i < 5; i++) {
- System.out.println(Thread.currentThread().getName()
- + ”:synchronized in g()”);
- try {
- TimeUnit.SECONDS.sleep(3);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
- public void h() {
- // other operations should not be locked…
- System.out.println(Thread.currentThread().getName()
- + ”:not synchronized in h()”);
- synchronized (syncObject2) {
- for (int i = 0; i < 5; i++) {
- System.out.println(Thread.currentThread().getName()
- + ”:synchronized in h()”);
- try {
- TimeUnit.SECONDS.sleep(3);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
- public static void main(String[] args) {
- final Resource2 rs = new Resource2();
- new Thread() {
- public void run() {
- rs.f();
- }
- }.start();
- new Thread() {
- public void run() {
- rs.g();
- }
- }.start();
- rs.h();
- }
- }
Thread-0:not synchronized in f()
Thread-0:synchronized in f()
main:not synchronized in h()
main:synchronized in h()
Thread-1:not synchronized in g()
Thread-1:synchronized in g()
Thread-0:synchronized in f()
main:synchronized in h()
Thread-1:synchronized in g()
Thread-0:synchronized in f()
main:synchronized in h()
Thread-1:synchronized in g()
Thread-0:synchronized in f()
main:synchronized in h()
Thread-1:synchronized in g()
Thread-0:synchronized in f()
main:synchronized in h()
Thread-1:synchronized in g()
4. Lock對象鎖
除了使用synchronized外,還可以使用Lock對象來創建臨界區。Resource3.java的演示效果同Resource1.java;Resource4.java的演示效果同Resource2.java。
Resource3.java
- package com.zj.lock;
- import java.util.concurrent.TimeUnit;
- import java.util.concurrent.locks.Lock;
- import java.util.concurrent.locks.ReentrantLock;
- public class Resource3 {
- private Lock lock = new ReentrantLock();
- public void f() {
- // other operations should not be locked…
- System.out.println(Thread.currentThread().getName()
- + ”:not synchronized in f()”);
- lock.lock();
- try {
- for (int i = 0; i < 5; i++) {
- System.out.println(Thread.currentThread().getName()
- + ”:synchronized in f()”);
- try {
- TimeUnit.SECONDS.sleep(3);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- } finally {
- lock.unlock();
- }
- }
- public void g() {
- // other operations should not be locked…
- System.out.println(Thread.currentThread().getName()
- + ”:not synchronized in g()”);
- lock.lock();
- try {
- for (int i = 0; i < 5; i++) {
- System.out.println(Thread.currentThread().getName()
- + ”:synchronized in g()”);
- try {
- TimeUnit.SECONDS.sleep(3);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- } finally {
- lock.unlock();
- }
- }
- public void h() {
- // other operations should not be locked…
- System.out.println(Thread.currentThread().getName()
- + ”:not synchronized in h()”);
- lock.lock();
- try {
- for (int i = 0; i < 5; i++) {
- System.out.println(Thread.currentThread().getName()
- + ”:synchronized in h()”);
- try {
- TimeUnit.SECONDS.sleep(3);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- } finally {
- lock.unlock();
- }
- }
- public static void main(String[] args) {
- final Resource3 rs = new Resource3();
- new Thread() {
- public void run() {
- rs.f();
- }
- }.start();
- new Thread() {
- public void run() {
- rs.g();
- }
- }.start();
- rs.h();
- }
Thread-0:not synchronized in f()
Thread-0:synchronized in f()
main:not synchronized in h()
Thread-1:not synchronized in g()
Thread-0:synchronized in f()
Thread-0:synchronized in f()
Thread-0:synchronized in f()
Thread-0:synchronized in f()
main:synchronized in h()
main:synchronized in h()
main:synchronized in h()
main:synchronized in h()
main:synchronized in h()
Thread-1:synchronized in g()
Thread-1:synchronized in g()
Thread-1:synchronized in g()
Thread-1:synchronized in g()
Thread-1:synchronized in g()
Resource4.java
- package com.zj.lock;
- import java.util.concurrent.TimeUnit;
- import java.util.concurrent.locks.Lock;
- import java.util.concurrent.locks.ReentrantLock;
- public class Resource4 {
- private Lock lock1 = new ReentrantLock();
- private Lock lock2 = new ReentrantLock();
- private Lock lock3 = new ReentrantLock();
- public void f() {
- // other operations should not be locked…
- System.out.println(Thread.currentThread().getName()
- + ”:not synchronized in f()”);
- lock1.lock();
- try {
- for (int i = 0; i < 5; i++) {
- System.out.println(Thread.currentThread().getName()
- + ”:synchronized in f()”);
- try {
- TimeUnit.SECONDS.sleep(3);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- } finally {
- lock1.unlock();
- }
- }
- public void g() {
- // other operations should not be locked…
- System.out.println(Thread.currentThread().getName()
- + ”:not synchronized in g()”);
- lock2.lock();
- try {
- for (int i = 0; i < 5; i++) {
- System.out.println(Thread.currentThread().getName()
- + ”:synchronized in g()”);
- try {
- TimeUnit.SECONDS.sleep(3);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- } finally {
- lock2.unlock();
- }
- }
- public void h() {
- // other operations should not be locked…
- System.out.println(Thread.currentThread().getName()
- + ”:not synchronized in h()”);
- lock3.lock();
- try {
- for (int i = 0; i < 5; i++) {
- System.out.println(Thread.currentThread().getName()
- + ”:synchronized in h()”);
- try {
- TimeUnit.SECONDS.sleep(3);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- } finally {
- lock3.unlock();
- }
- }
- public static void main(String[] args) {
- final Resource4 rs = new Resource4();
- new Thread() {
- public void run() {
- rs.f();
- }
- }.start();
- new Thread() {
- public void run() {
- rs.g();
- }
- }.start();
- rs.h();
- }
- }
Thread-0:not synchronized in f()
Thread-0:synchronized in f()
main:not synchronized in h()
main:synchronized in h()
Thread-1:not synchronized in g()
Thread-1:synchronized in g()
Thread-0:synchronized in f()
main:synchronized in h()
Thread-1:synchronized in g()
Thread-0:synchronized in f()
main:synchronized in h()
Thread-1:synchronized in g()
Thread-0:synchronized in f()
main:synchronized in h()
Thread-1:synchronized in g()
Thread-0:synchronized in f()
main:synchronized in h()
Thread-1:synchronized in g()
另外,ReentrantLock可定時和可輪詢的鎖獲取模式由tryLock方法實現。
- public boolean tryLock(); //等同於tryLock(0, TimeUnit.SECONDS),不停詢問是否可獲取鎖
- public boolean tryLock(long timeout,
- TimeUnit unit)
- throws InterruptedException //timeout - 等待鎖的時間,unit - timeout 參數的時間單位
5. synchronized和lock的區別:
Lock 的鎖定是通過代碼實現的,而 synchronized 是在 JVM 層面上實現的。
synchronized 在鎖定時如果方法塊拋出異常,JVM 會自動將鎖釋放掉,不會因爲出了異常沒有釋放鎖造成線程死鎖。但是 Lock 的話就享受不到 JVM 帶來自動的功能,出現異常時必須在 finally 將鎖釋放掉,否則將會引起死鎖。
在資源競爭不是很激烈的情況下,偶爾會有同步的情形下,synchronized是很合適的。原因在於,編譯程序通常會儘可能的進行優化synchronize,另外可讀性非常好,不管用沒用過5.0多線程包的程序員都能理解。
ReentrantLock:
ReentrantLock提供了多樣化的同步,比如有時間限制的同步,可以被Interrupt的同步(synchronized的同步是不能Interrupt的)等。在資源競爭不激烈的情形下,性能稍微比synchronized差點點。但是當同步非常激烈的時候,synchronized的性能一下子能下降好幾十倍。而ReentrantLock確還能維持常態。
Atomic:
和上面的類似,不激烈情況下,性能比synchronized略遜,而激烈的時候,也能維持常態。激烈的時候,Atomic的性能會優於ReentrantLock一倍左右。但是其有一個缺點,就是隻能同步一個值,一段代碼中只能出現一個Atomic的變量,多於一個同步無效。因爲他不能在多個Atomic之間同步。
關於synchronized和lock的詳細區別請看http://www.ibm.com/developerworks/cn/java/j-jtp10264/index.html