问:如何控制某个方法被并发访问的个数?
答:可以使用 Semaphore,其有两个核心方法如下:
semaphore.acquire():
用来请求一个信号量,该方法使信号量个数减 1;一旦没有可使用的信号量,即信号量个数变为负数时,再次调用该方法请求时就会阻塞,直到其他线程释放了信号量。semaphore.release():
用来释放一个信号量,该方法使信号量个数加 1。
所以本题最简单的实现方案代码如下(同时最多可以 5 个并发访问):
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 都能够实现线程之间的等待,只不过它们侧重点不同。
CountDownLatch 一般用于某个线程 A 等待若干个其他线程执行完任务之后,它才执行。
CyclicBarrier 一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行。
此外,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()+"正在执行");
Thread.sleep(3000);
System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
};
}.start();
new Thread(){
public void run() {
try {
System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
Thread.sleep(3000);
System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
};
}.start();
try {
System.out.println("等待2个子线程执行完毕...");
latch.await();
System.out.println("2个子线程已经执行完毕");
System.out.println("继续执行主线程");
} 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()+"正在写入数据...");
try {
Thread.sleep(5000); //以睡眠来模拟写入数据操作
System.out.println("线程"+Thread.currentThread().getName()+"写入数据完毕,等待其他线程写入完毕");
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
}catch(BrokenBarrierException e){
e.printStackTrace();
}
System.out.println("所有线程写入完毕,继续处理其他任务...");
}
}
}