您的位置:首页 > 编程语言 > Java开发

(七) Java多线程详解之常用线程同步工具类

2017-05-22 22:41 176 查看

线程同步工具类

信号灯(Semaphore)

可以维护当前访问自身的线程个数并提供了同步机制,使用Semaphore可以控制同时访问资源的线程个数,示例代码如下:

public class ThreadExample16 {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
final Semaphore sp = new Semaphore(3);
for (int i = 0; i < 10; i++) {
Runnable runnable = new Runnable() {
public void run() {
try {
sp.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程" + Thread.currentThread().getName() + "进入,当前已有" + (3 - sp.availablePermits()) + "个并发");
try {
Thread.sleep((long) (Math.random() * 10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程" + Thread.currentThread().getName() + "即将离开");
sp.release();
// 下面代码有时候执行不准确,因为其没有和上面的代码合成原子单元
System.out.println("线程" + Thread.currentThread().getName() + "已离开,当前已有" + (3 - sp.availablePermits()) + "个并发");
}
};
service.execute(runnable);
}
}
}


该段代码通过信号灯保证同时运行的线程只能有三个

障碍器(CyclicBarrier)

可以实现多个线程执行完毕后再执行某段逻辑,例如一个部门出去聚餐要等待所有人都到齐后再一起出发,以下为示例代码:

public class ThreadExample17 {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
final CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
for (int i = 0; i < 3; i++) {
Runnable runnable = new Runnable() {
public void run() {
try {
Thread.sleep((long) (Math.random() * 10000));
System.out.println("线程" + Thread.currentThread().getName() + "即将到达集合地点1,当前已有" + (cyclicBarrier.getNumberWaiting() + 1) + "个已经到达," + (cyclicBarrier.getNumberWaiting() == 2 ? "都到齐了,继续走啊" : "正在等候"));
cyclicBarrier.await();

Thread.sleep((long) (Math.random() * 10000));
System.out.println("线程" + Thread.currentThread().getName() + "即将到达集合地点2,当前已有" + (cyclicBarrier.getNumberWaiting() + 1) + "个已经到达," + (cyclicBarrier.getNumberWaiting() == 2 ? "都到齐了,继续走啊" : "正在等候"));
cyclicBarrier.await();

Thread.sleep((long) (Math.random() * 10000));
System.out.println("线程" + Thread.currentThread().getName() + "即将到达集合地点3,当前已有" + (cyclicBarrier.getNumberWaiting() + 1) + "个已经到达," + (cyclicBarrier.getNumberWaiting() == 2 ? "都到齐了,继续走啊" : "正在等候"));
cyclicBarrier.await();
} catch (Exception e) {
e.printStackTrace();
}
}
};
service.execute(runnable);
}
service.shutdown();
}
}


同步计数器(CountDownLatch)

犹如倒计时计数器调用CountDownLatch对象的countDown方法就将计数器减1,当计数到达0时则所有等待者或单个等待者开始执行,示例代码如下:

public class ThreadExample18 {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
final CountDownLatch cdOrder = new CountDownLatch(1);
final CountDownLatch cdAnswer = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
Runnable runnable = new Runnable() {
public void run() {
try {
System.out.println("线程" + Thread.currentThread().getName() + "正准备接受命令");
cdOrder.await();
System.out.println("线程" + Thread.currentThread().getName() + "已接受命令");
Thread.sleep((long) (Math.random() * 10000));
System.out.println("线程" + Thread.currentThread().getName() + "回应命令处理结果");
cdAnswer.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
};
service.execute(runnable);
}
try {
Thread.sleep((long) (Math.random() * 10000));
System.out.println("线程" + Thread.currentThread().getName() + "即将发布命令");
cdOrder.countDown();
System.out.println("线程" + Thread.currentThread().getName() + "已发送命令,正在等待结果");
cdAnswer.await();
System.out.println("线程" + Thread.currentThread().getName() + "已收到所有响应结果");
} catch (Exception e) {
e.printStackTrace();
}
service.shutdown();
}
}


线程间数据交换(Exchanger)

用于实现两个线程之间的数据交换,每个线程在完成一定的事务后想与对方交换数据,第一个先拿出数据的线程将一直等待第二个线程拿着数据到来时才能彼此交换数据,示例代码如下:

public class ThreadExample19 {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
final Exchanger exchanger = new Exchanger();
service.execute(new Runnable() {
public void run() {
try {
String data1 = "线程一的数据";
System.out.println("线程" + Thread.currentThread().getName() + "正在把数据" + data1 + "换出去");
Thread.sleep((long) (Math.random() * 10000));
String data2 = (String) exchanger.exchange(data1);
System.out.println("线程" + Thread.currentThread().getName() + "换回的数据为" + data2);
} catch (Exception e) {
e.printStackTrace();
}
}
});

service.execute(new Runnable() {
public void run() {
try {
String data1 = "线程二的数据";
System.out.println("线程" + Thread.currentThread().getName() + "正在把数据" + data1 + "换出去");
Thread.sleep((long) (Math.random() * 10000));
String data2 = (String) exchanger.exchange(data1);
System.out.println("线程" + Thread.currentThread().getName() + "换回的数据为" + data2);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}


以上几种工具类在适当的场景下会给开发带来很大的便利,平时不需要记得具体怎么运行只需要在遇到具体需求时知道有这么个解决方案
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息