問:如何控制某個方法被并發(fā)訪問的個數(shù)?
答:可以使用 Semaphore,其有兩個核心方法如下:
semaphore.acquire():用來請求一個信號量,該方法使信號量個數(shù)減 1;一旦沒有可使用的信號量,即信號量個數(shù)變?yōu)樨摂?shù)時,再次調(diào)用該方法請求時就會阻塞,直到其他線程釋放了信號量。semaphore.release():用來釋放一個信號量,該方法使信號量個數(shù)加 1。
所以本題最簡單的實現(xiàn)方案代碼如下(同時最多可以 5 個并發(fā)訪問):
public class SemaphoreTest {
private Semaphore mSemaphore = new Semaphore(5);
public void testRun() {
for(int i=0; i< 20; i++){
new Thread(new Runnable() {
@Override
public void run() {
test();
}
}).start();
}
}
private void test(){
try {
mSemaphore.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " enter...");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " exit...");
mSemaphore.release();
}
}
輸出如下:
Thread-1 enter...
Thread-2 enter...
Thread-3 enter...
Thread-4 enter...
Thread-5 enter...
Thread-1 exit...
Thread-6 enter...
Thread-2 exit...
Thread-7 enter...
Thread-3 exit...
Thread-8 enter...
Thread-4 exit...
Thread-9 enter...
Thread-5 exit...
Thread-10 enter...
Thread-7 exit...
Thread-6 exit...
Thread-11 enter...
Thread-12 enter...
Thread-8 exit...
Thread-13 enter...
Thread-10 exit...
Thread-14 enter...
Thread-9 exit...
Thread-15 enter...
Thread-11 exit...
Thread-12 exit...
Thread-16 enter...
Thread-17 enter...
Thread-15 exit...
Thread-18 enter...
Thread-14 exit...
Thread-19 enter...
Thread-13 exit...
Thread-20 enter...
Thread-16 exit...
Thread-19 exit...
Thread-17 exit...
Thread-18 exit...
Thread-20 exit...
問:簡單說說 Java 中 CyclicBarrier 和 CountDownLatch 有什么不同?
答:CountDownLatch 和 CyclicBarrier 都能夠?qū)崿F(xiàn)線程之間的等待,只不過它們側(cè)重點不同。
CountDownLatch 一般用于某個線程 A 等待若干個其他線程執(zhí)行完任務(wù)之后,它才執(zhí)行。
CyclicBarrier 一般用于一組線程互相等待至某個狀態(tài),然后這一組線程再同時執(zhí)行。
此外,CountDownLatch 是不能夠重用的,而 CyclicBarrier 是可以重用的。
如下是 CountDownLatch 使用樣例:
public class Test {
public static void main(String[] args) {
final CountDownLatch latch = new CountDownLatch(2);
new Thread(){
public void run() {
try {
System.out.println("子線程"+Thread.currentThread().getName()+"正在執(zhí)行");
Thread.sleep(3000);
System.out.println("子線程"+Thread.currentThread().getName()+"執(zhí)行完畢");
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
};
}.start();
new Thread(){
public void run() {
try {
System.out.println("子線程"+Thread.currentThread().getName()+"正在執(zhí)行");
Thread.sleep(3000);
System.out.println("子線程"+Thread.currentThread().getName()+"執(zhí)行完畢");
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
};
}.start();
try {
System.out.println("等待2個子線程執(zhí)行完畢...");
latch.await();
System.out.println("2個子線程已經(jīng)執(zhí)行完畢");
System.out.println("繼續(xù)執(zhí)行主線程");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
如下是 CyclicBarrier 使用樣例:
public class Test {
public static void main(String[] args) {
int N = 4;
CyclicBarrier barrier = new CyclicBarrier(N,new Runnable() {
@Override
public void run() {
System.out.println("當前線程"+Thread.currentThread().getName());
}
});
for(int i=0;i<N;i++) {
new Writer(barrier).start();
}
}
static class Writer extends Thread{
private CyclicBarrier cyclicBarrier;
public Writer(CyclicBarrier cyclicBarrier) {
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
System.out.println("線程"+Thread.currentThread().getName()+"正在寫入數(shù)據(jù)...");
try {
Thread.sleep(5000); //以睡眠來模擬寫入數(shù)據(jù)操作
System.out.println("線程"+Thread.currentThread().getName()+"寫入數(shù)據(jù)完畢,等待其他線程寫入完畢");
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
}catch(BrokenBarrierException e){
e.printStackTrace();
}
System.out.println("所有線程寫入完畢,繼續(xù)處理其他任務(wù)...");
}
}
}
本文參考自 Semaphore、CyclicBarrier、CountDownLatch 神器相關(guān)的面試題解析