本文主要介紹一些Java中常用的同步工具類
ReentrantLock
可重入鎖,可代替synchronized,它比synchronized更加的靈活,提供了更多的方法,但在使用上需要手動的加鎖和釋放鎖;底層使用CAS來實現。使用synchronized鎖定的話如果遇到異常,jvm會自動釋放鎖,但是lock必須手動釋放鎖,因此經常在finally中進行鎖的釋放.
常用方法:
Boolean tryLock() 嘗試獲取鎖,不會阻塞等待 還可加入參數自旋一定時間來獲取鎖
lockinterruptibly() 使用該方法獲取鎖可以讓線程對interrupt() 做出反應。
在創建時可傳遞參數指定爲公平鎖(新線程來了是進入等待隊列還是和已在隊列中的線程一起搶鎖)
具體的使用方法參考 Java API
private Lock lock = new ReentrantLock();
private Condition producer = lock.newCondition();
private Condition consumer = lock.newCondition();
await() signalAll()
ReentrantLock還支持上面這種用法,newCondition(可以這麼理解,每new一個Condition都相當於多一個等待隊列 ,通過signal() 可以喚醒特定隊列中等待的線程 );在生產者和消費者模式裏,可以利用Condition這一特點,利用signalAll() 來只喚醒對應的生產者 或消費者隊列裏的線程。
CountDownLatch
表面翻譯爲倒數門栓,相當於一個同步計數器(倒數的);創建的時候指定計數器的值,每調用一次countDown() 方法 計數器的值減一,直到計數器值變爲0 在CountDownLatch上等待的線程纔會執行(其他線程做countDown() 等待的線程通過調用await()方法來等待);CountDownLatch是一次性的,計算器的值只能在構造方法中初始化一次。
private static void usingCountDownLatch() {
Thread[] threads = new Thread[100];
CountDownLatch latch = new CountDownLatch(threads.length);
for(int i=0; i<threads.length; i++) {
threads[i] = new Thread(()->{
int result = 0;
for(int j=0; j<10000; j++) result += j;
latch.countDown();
});
}
for (int i = 0; i < threads.length; i++) {
threads[i].start();
}
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("end latch");
}
CyclicBarrier
翻譯爲循環障礙,可和CountDownLatch對比理解。它是循環的,可多次使用,是一個循環的障礙。
public static void main(String[] args) {
//CyclicBarrier barrier = new CyclicBarrier(20);
CyclicBarrier barrier = new CyclicBarrier(20, () -> System.out.println("通過"));
/*CyclicBarrier barrier = new CyclicBarrier(20, new Runnable() {
@Override
public void run() {
System.out.println("通過");
}
});*/
for(int i=0; i<100; i++) {
new Thread(()->{
try {
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
ReadWriteLock
讀寫鎖,爲了提升效率,在讀的時候加讀鎖(共享鎖)多個線程可同時讀,在寫的時候加寫鎖(排它鎖)一次只能有一個線程在寫。
具體的使用如下代碼
public class TestReadWriteLock{
static Lock lock = new ReentrantLock();
private static int value;
static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
static Lock readLock = readWriteLock.readLock();
static Lock writeLock = readWriteLock.writeLock();
public static void read(Lock lock) {
try {
lock.lock();
Thread.sleep(1000);
System.out.println("read over!");
//模擬讀取操作
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void write(Lock lock, int v) {
try {
lock.lock();
Thread.sleep(1000);
value = v;
System.out.println("write over!");
//模擬寫操作
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
//Runnable readR = ()-> read(lock);
Runnable readR = ()-> read(readLock);
//Runnable writeR = ()->write(lock, new Random().nextInt());
Runnable writeR = ()->write(writeLock, new Random().nextInt());
for(int i=0; i<18; i++) new Thread(readR).start();
for(int i=0; i<2; i++) new Thread(writeR).start();
}
}
Phaser
分階段的同步工具, 適用於分階段的任務執行上 第一階段達成目標 然後執行第二階段 第二階段達成目標後 依次繼續 ;使用上需要先繼承Phaser 重寫onAdvance()方法,有一個目標,只有當目標達成纔會執行後續的階段。
具體適用參考如下代碼:
public class TestPhaser {
static Random r = new Random();
static MarriagePhaser phaser = new MarriagePhaser();
static void milliSleep(int milli) {
try {
TimeUnit.MILLISECONDS.sleep(milli);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
// 本例中的目標
phaser.bulkRegister(5);
for(int i=0; i<5; i++) {
final int nameIndex = i;
new Thread(()->{
Person p = new Person("person " + nameIndex);
p.arrive();
//本例中沒調用一次register多1 只有達到5纔會執行後面的操作
phaser.arriveAndAwaitAdvance();
p.eat();
phaser.arriveAndAwaitAdvance();
p.leave();
phaser.arriveAndAwaitAdvance();
}).start();
}
}
static class MarriagePhaser extends Phaser {
@Override
protected boolean onAdvance(int phase, int registeredParties) {
//自定義的階段,本例分3個階段,第一次達到執行 case 0 依次類推
switch (phase) {
case 0:
System.out.println("所有人到齊了!");
return false;
case 1:
System.out.println("所有人吃完了!");
return false;
case 2:
System.out.println("所有人離開了!");
System.out.println("婚禮結束!");
return true;
default:
return true;
}
}
}
static class Person {
String name;
public Person(String name) {
this.name = name;
}
public void arrive() {
milliSleep(r.nextInt(1000));
System.out.printf("%s 到達現場!\n", name);
}
public void eat() {
milliSleep(r.nextInt(1000));
System.out.printf("%s 吃完!\n", name);
}
public void leave() {
milliSleep(r.nextInt(1000));
System.out.printf("%s 離開!\n", name);
}
}
}
semaphore
信號量,相當於一個令牌,持令牌者可執行,令牌可申請,用完之後需要釋放。
具體代碼如下:
public class TestSemaphore {
public static void main(String[] args) {
//創建時初始化兩個令牌,公平申請模式 默認非公平
Semaphore s = new Semaphore(2, true);
//允許一個線程同時執行
//Semaphore s = new Semaphore(1);
new Thread(()->{
try {
s.acquire();
System.out.println("T1 running...");
Thread.sleep(200);
System.out.println("T1 running...");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
s.release();
}
}).start();
new Thread(()->{
try {
//申請
s.acquire();
System.out.println("T2 running...");
Thread.sleep(200);
System.out.println("T2 running...");
//釋放
s.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
Exchanger
交換器,用於兩個線程交換數據,可這樣理解,Exchange相當於一個容器,一個線程調用exchange方法時會阻塞等待,當另一個線程也掉了exchange方法後,進入該容器交換二者數據,然後返回。
具體使用參考如下:
public class TestExchanger {
static Exchanger<String> exchanger = new Exchanger<>();
public static void main(String[] args) {
new Thread(()->{
String s = "data1";
try {
s = exchanger.exchange(s);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + s);
}, "t1").start();
new Thread(()->{
String s = "data2";
try {
s = exchanger.exchange(s);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + s);
}, "t2").start();
}
}
LockSupport
該工具類可以指定特定線程停止與開始。
具體使用參考如下:
public class T13_TestLockSupport {
public static void main(String[] args) {
Thread t = new Thread(()->{
for (int i = 0; i < 10; i++) {
System.out.println(i);
if(i == 5) {
//停止當前線程的執行
LockSupport.park();
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
//剛開始就執行恢復執行,會使得線程不會停止,相當於作廢停止
LockSupport.unpark(t);
/*
try {
TimeUnit.SECONDS.sleep(8);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("after 8 senconds!");
//讓停止的t線程恢復執行
LockSupport.unpark(t);
*/
}
}