執行緒併發五:執行緒安全之重入鎖
阿新 • • 發佈:2019-02-16
重入鎖簡單介紹
之前介紹的synchronized關鍵字是一種最簡單的控制方法。下面說一說執行緒安全的另一種實現方式——–重入鎖
重入鎖使用java.util.concurrent.loks.ReentrantLock 類來實現
a. lock() //獲得鎖,如果鎖已被佔用,則等待
b. lockInterruptibly() //獲得鎖,但優先響應中斷
c. tryLock() //嘗試獲得鎖,如果成功返回true.失敗返回false.
d. tryLock(long time, TimeUnit nuit) //在指定時間內嘗試獲取鎖
e. unlock() //釋放鎖
f. isHeldByCurrentThread() //檢測當前執行緒是否持有鎖。
import java.util.Date;
import java.util.concurrent.locks.ReentrantLock;
public class ReenterLockSimpleTest implements Runnable {
public static ReentrantLock lock = new ReentrantLock();
public void sayHello(){
lock.lock();
lock.lock();
try {
System.out.println("[" + new Date().getTime() + "]" + Thread.currentThread().getName() + ": say hello!!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} finally {
lock.unlock();
lock.unlock();
}
}
public void run(){
while(true){
sayHello();
}
}
public static void main(String[] args){
Thread t1 = new Thread(new ReenterLockSimpleTest(),"test1");
Thread t2 = new Thread(new ReenterLockSimpleTest(),"test2");
t1.start();
t2.start();
}
}
從列印可以看出,1秒只打印一條,兩個執行緒相互交替。重入鎖有著明顯的操作痕跡,何時加鎖,何時釋放鎖。也正是這樣,重入鎖對邏輯控制的靈活性要遠遠高於synchronized,重入鎖之所以叫重入鎖,那是因為這種鎖可以反覆進入的,當然這裡的反覆進入僅僅侷限於同一個執行緒。並且,釋放所得次數要是獲取鎖的次數一樣。
2.中斷響應
對於synchronized來說,如果一個執行緒在等待鎖,只有兩種情況,要麼得到鎖,要麼繼續等待,而重入鎖提供另一種可能,那就是執行緒中斷。
import java.util.concurrent.locks.ReentrantLock;
public class InterruptedReenterLock implements Runnable {
public static ReentrantLock lock1 = new ReentrantLock();
public static ReentrantLock lock2 = new ReentrantLock();
@Override
public void run() {
// TODO Auto-generated method stub
try {
if(!Thread.currentThread().isInterrupted()){
if("test1".equals(Thread.currentThread().getName())){
lock1.lockInterruptibly();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO: handle exception
}
lock2.lockInterruptibly();
}else {
lock2.lockInterruptibly();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO: handle exception
}
lock1.lockInterruptibly();
}
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if(lock1.isHeldByCurrentThread()){
lock1.unlock();
}
if(lock2.isHeldByCurrentThread()){
lock2.unlock();
}
System.out.println(Thread.currentThread().getName() + " 執行緒退出!");
}
}
public static void main(String[] args) throws InterruptedException{
Thread t1 = new Thread(new InterruptedReenterLock(),"test1");
Thread t2 = new Thread(new InterruptedReenterLock(),"test2");
t1.start();
t2.start();
Thread.sleep(2000);
t2.interrupt();
}
}
如上程式碼會出現兩個執行緒相互等待的情況,當執行緒2發生中斷後,釋放所有的鎖,執行緒1線上程2釋放鎖之後獲得鎖,兩個執行緒先後結束。
3.限時申請鎖
tryLock() 方法限時等待,有兩種方式, 一種為限時申請鎖,需要設定時間, 一種為嘗試獲取鎖,無論成功還是失敗直接返回,不等待。
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class TryLockTiem implements Runnable {
public static ReentrantLock lock = new ReentrantLock();
public void sayHello(){
try {
if(lock.tryLock(4, TimeUnit.SECONDS)){
System.out.println("[" + new Date().getTime() + "]" + Thread.currentThread().getName() + ": get lock!!");
Thread.sleep(6000);
}else{
System.out.println("[" + new Date().getTime() + "]" + Thread.currentThread().getName() + ": get nothing!!");
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if(lock.isHeldByCurrentThread()){
lock.unlock();
}
}
}
public void run(){
while(true){
sayHello();
}
}
public static void main(String[] args){
Thread t1 = new Thread(new TryLockTiem(),"test1");
Thread t2 = new Thread(new TryLockTiem(),"test2");
t1.start();
t2.start();
}
}
import java.util.Date;
import java.util.concurrent.locks.ReentrantLock;
public class TryLockTiem implements Runnable {
public static ReentrantLock lock = new ReentrantLock();
public void sayHello(){
try {
if(lock.tryLock()){
System.out.println("[" + new Date().getTime() + "]" + Thread.currentThread().getName() + ": get lock!!");
Thread.sleep(2000);
}else{
System.out.println("[" + new Date().getTime() + "]" + Thread.currentThread().getName() + ": get nothing!!");
Thread.sleep(1000);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if(lock.isHeldByCurrentThread()){
System.out.println("[" + new Date().getTime() + "]" + Thread.currentThread().getName() + ": release lock!!");
lock.unlock();
}
}
}
public void run(){
while(true){
sayHello();
}
}
public static void main(String[] args){
Thread t1 = new Thread(new TryLockTiem(),"test1");
Thread t2 = new Thread(new TryLockTiem(),"test2");
t1.start();
t2.start();
}
}