1、回憶 synchronized 同步的方式
首先回憶一下 synchronized 同步對象的方式
當一個線程佔用 synchronized 同步對象,其他線程就不能佔用了,直到釋放這個同步對象爲止
與synchronized類似的,lock也能夠達到同步的效果
package multiplethread;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TestThread {
public static String now(){
return new SimpleDateFormat("HH:mm:ss").format(new Date());
}
public static void main(String[] args) {
final Object someObject = new Object();
Thread t1 = new Thread(){
public void run(){
try {
System.out.println( now()+" t1 線程已經運行");
System.out.println( now()+this.getName()+ " 試圖佔有對象:someObject");
synchronized (someObject) {
System.out.println( now()+this.getName()+ " 佔有對象:someObject");
Thread.sleep(5000);
System.out.println( now()+this.getName()+ " 釋放對象:someObject");
}
System.out.println(now()+" t1 線程結束");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
t1.setName(" t1");
t1.start();
Thread t2 = new Thread(){
public void run(){
try {
System.out.println( now()+" t2 線程已經運行");
System.out.println( now()+this.getName()+ " 試圖佔有對象:someObject");
synchronized (someObject) {
System.out.println( now()+this.getName()+ " 佔有對象:someObject");
Thread.sleep(5000);
System.out.println( now()+this.getName()+ " 釋放對象:someObject");
}
System.out.println(now()+" t2 線程結束");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
t2.setName(" t2");
t2.start();
}
}
2、使用Lock對象實現同步效果
Lock是一個接口,爲了使用一個Lock對象,需要用到
Lock lock = new ReentrantLock();
與 synchronized (someObject) 類似的,lock()方法,表示當前線程佔用lock對象,一旦佔用,其他線程就不能佔用了。
與 synchronized 不同的是,一旦synchronized 快結束,就會自動釋放對someObject的佔用。 lock卻必須調用unlock方法進行手動釋放,爲了保證釋放的執行,往往會把unlock() 放在finally中進行。
package multiplethread;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TestThread {
public static String now() {
return new SimpleDateFormat("HH:mm:ss").format(new Date());
}
public static void log(String msg) {
System.out.printf("%s %s %s %n", now() , Thread.currentThread().getName() , msg);
}
public static void main(String[] args) {
Lock lock = new ReentrantLock();
Thread t1 = new Thread() {
public void run() {
try {
log("線程啓動");
log("試圖佔有對象:lock");
lock.lock();
log("佔有對象:lock");
log("進行5秒的業務操作");
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
log("釋放對象:lock");
lock.unlock();
}
log("線程結束");
}
};
t1.setName("t1");
t1.start();
try {
//先讓t1飛2秒
Thread.sleep(2000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
Thread t2 = new Thread() {
public void run() {
try {
log("線程啓動");
log("試圖佔有對象:lock");
lock.lock();
log("佔有對象:lock");
log("進行5秒的業務操作");
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
log("釋放對象:lock");
lock.unlock();
}
log("線程結束");
}
};
t2.setName("t2");
t2.start();
}
}
3、trylock方法
synchronized 是不佔用到手不罷休的,會一直試圖佔用下去。
與 synchronized 的鑽牛角尖不一樣,Lock接口還提供了一個trylock方法。
trylock會在指定時間範圍內試圖佔用,佔成功了,就啪啪啪。 如果時間到了,還佔用不成功,扭頭就走~
注意: 因爲使用trylock有可能成功,有可能失敗,所以後面unlock釋放鎖的時候,需要判斷是否佔用成功了,如果沒佔用成功也unlock,就會拋出異常
package multiplethread;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TestThread {
public static String now() {
return new SimpleDateFormat("HH:mm:ss").format(new Date());
}
public static void log(String msg) {
System.out.printf("%s %s %s %n", now() , Thread.currentThread().getName() , msg);
}
public static void main(String[] args) {
Lock lock = new ReentrantLock();
Thread t1 = new Thread() {
public void run() {
boolean locked = false;
try {
log("線程啓動");
log("試圖佔有對象:lock");
locked = lock.tryLock(1,TimeUnit.SECONDS);
if(locked){
log("佔有對象:lock");
log("進行5秒的業務操作");
Thread.sleep(5000);
}
else{
log("經過1秒鐘的努力,還沒有佔有對象,放棄佔有");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if(locked){
log("釋放對象:lock");
lock.unlock();
}
}
log("線程結束");
}
};
t1.setName("t1");
t1.start();
try {
//先讓t1飛2秒
Thread.sleep(2000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
Thread t2 = new Thread() {
public void run() {
boolean locked = false;
try {
log("線程啓動");
log("試圖佔有對象:lock");
locked = lock.tryLock(1,TimeUnit.SECONDS);
if(locked){
log("佔有對象:lock");
log("進行5秒的業務操作");
Thread.sleep(5000);
}
else{
log("經過1秒鐘的努力,還沒有佔有對象,放棄佔有");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if(locked){
log("釋放對象:lock");
lock.unlock();
}
}
log("線程結束");
}
};
t2.setName("t2");
t2.start();
}
}
4、總結Lock和synchronized的區別
-
Lock是一個接口,而synchronized是Java中的關鍵字,synchronized是內置的語言實現,Lock是代碼層面的實現。
-
Lock可以選擇性的獲取鎖,如果一段時間獲取不到,可以放棄。synchronized不行,會一根筋一直獲取下去。 藉助Lock的這個特性,就能夠規避死鎖,synchronized必須通過謹慎和良好的設計,才能減少死鎖的發生。
-
synchronized在發生異常和同步快結束的時候,會自動釋放鎖。而Lock必須手動釋放, 所以如果忘記了釋放鎖,一樣會造成死鎖。