>同步工具 Semaphore
这个类代表一个计数信号量。可以控制某个资源可被同时访问的个数,acquire() 获得一个许可,如果没有就等待,release() 释放一个许可。Semaphore维护了当前访问的个数,提供同步访问机制,控制同时访问的个数。
例子:控制线程两个两个的运行
public class TestSemaphore {
public static void main(String[] args) {
new TestSemaphore().go();
}
public void go() {
// 这就像是一个“许可”“信号灯”
Semaphore semaphore = new Semaphore(2);
Dosth dosth = new Dosth();
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
semaphore.acquire();
dosth.work();
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "线程" + i).start();
}
}
class Dosth {
public void work() {
System.out.println(Thread.currentThread().getName() + "线程work");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
>同步工具 CountDownLatch
可以把它看作是一个“计数器”,只不过同时只能有一个线程操作这个计数器,也就是同时只能有一个线程去减这个计数器里面的值。在完成一组其他线程中的操作之前,它允许一个或多个线程一直等待。
你可以在CountDownLatch对象设置一个初始值,作为计数值,任何调用这个对象上await() 方法都将阻塞,直到这个计数器的计数值被其他线程减到零为止。一般典型应用场景:一个任务想要进行,必须等待其他线程完成执行完成后再往下执行。
方法说明,很好理解:
void await():使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。
void countDown():递减锁存器的计数,如果计数到达零,则释放所有等待的线程。
long getCount():返回当前计数。
public class TestCountDownLatch {
public void go() {
final Random r = new Random();
final CountDownLatch latch = new CountDownLatch(3);
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("子线程1正在工作");
try {
Thread.sleep(r.nextInt(3000));
} catch (InterruptedException e) {
// TODO Auto-generated catch blo
e.printStackTrace();
}
System.out.println("子线程1工作完毕");
latch.countDown();
}
}, "子线程1").start();
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("子线程2正在工作");
try {
Thread.sleep(r.nextInt(3000));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("子线程2工作完毕");
latch.countDown();
}
}, "子线程2").start();
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("子线程3正在工作");
try {
Thread.sleep(r.nextInt(3000));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("子线程3工作完毕");
latch.countDown();
}
}, "子线程3").start();
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("主线程等待子线程全部完毕……");
try {
latch.await();
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
System.out.println("所有子线程工作完毕,主线程开始工作……");
}
}, "主线程").start();
}
public static void main(String[] args) {
TestCountDownLatch test = new TestCountDownLatch();
test.go();
}
}
>同步工具CyclicBarrier(公共屏障点)
允许一组线程彼此互相等待,直到达到一个屏障点。
CyclicBarrier类似于CountDownLatch,也是用来计数的。不同的是CyclicBarrier数的是调用了CyclicBarrier.await()进入等待的线程数,当等待的线程数达到了CyclicBarrier初始时规定的数目时,所有进入等待状态的线程被唤醒并继续。CyclicBarrier就象它名字的意思一样,可看成是个障碍,所有的线程必须到齐后才能一起通过这个障碍。
方法说明:
await():在所有参与者线程都已经在此 barrier 上调用 await 方法之前,将一直等待。
await(long timeout,TimeUnit unit): 在所有参与者线程都已经在此屏障上调用 await 方法之前将一直等待,或者超出了指定的等待时间。
getNumberWaiting():返回当前在屏障处等待的参与者线程的数目。
getParties():返回要求启动此 barrier 的参与者线程的数目。
isBroken():查询此屏障是否处于损坏状态。
reset():将屏障重置为其初始状态。
public class CyclicBarrierTest {
public static void main(String[] args) {
final CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
ExecutorService service = Executors.newFixedThreadPool(10);
for (int i = 0; i < 3; i++) {
service.execute(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(new Random().nextInt(5000));
System.out.print(Thread.currentThread().getName() + "已到达集合点" + (i + 1) + ",现在共有"
+ (cyclicBarrier.getNumberWaiting() + 1) + "个线程到达,");
if (cyclicBarrier.getNumberWaiting() + 1 == 3) {
System.out.println("所有线程都到齐,进行下一操作……");
} else {
System.out.println("正在等待其他线程……");
}
cyclicBarrier.await();
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
}
service.shutdown();
}
}
Hadoop的MapReduce的思想,举个例子,并行计算:
一个三维数组,开三个线程,各自求一维之后,再将三个结果操作。
public class TestCyclicBarrier {
int[][] rect = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
int[] rowResult = new int[rect.length];
Calc calc = new Calc();
public static void main(String[] args) {
TestCyclicBarrier test = new TestCyclicBarrier();
test.go();
}
private void go() {
CyclicBarrier barrier = new CyclicBarrier(3, new Runnable() {
public void run() {
System.out.println("所有计算线程执行完毕,这里开始汇总");
int total = calc.add(rowResult);
System.out.println(total);
}
});
for (int i = 0; i < rect.length; i++) {
final int row = i;
new RowThread(barrier, row).start();
}
}
class RowThread extends Thread {
Random r = new Random();
CyclicBarrier barrier;// 计数器
int row;
public RowThread(CyclicBarrier barrier, int row) {
this.barrier = barrier;
this.row = row;
}
@Override
public void run() {
rowResult[row] = calc.add(rect[row]);
try {
Thread.sleep(r.nextInt(4000));
System.out.println(Thread.currentThread().getName() + "计算完毕,开始等待");
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
class Calc {
public int add(int[] row) {
int total = 0;
for (int i : row) {
total += i;
}
return total;
}
}
}
代码的主要功能是:给出一个二维数组,然后根据二维数组的长度开启若干个线程,每一个线程计算二维数组中每一个“一维数组”的元素之和。然后当每一个一维数组的元素和计算完毕后,再由一个线程汇总前面的每一个结果,最终得到的就是整个二维数组元素之和。正如程序给出的二维数组是int[][] rect = {{1,2,3},{4,5,6},{7,8,9}};这个数组长度是3,每一个元素又是一个一维数组,然后程序开启三个线程,第一个线程计算{1,2,3}的和,就是1+2+3=6,第二个线程计算4,5,6的和,第三个线程计算7,8,9的和,最后再由一个线程计算前面三个线程的和进行相加,就得到了这个二维数组所有元素的和。这有点类似Map/Reduce的思想。什么是Map/Reduce?感兴趣的读者可以google一下。
这里需要注意一点,和上一个例子有所不同,这里创建的CyclicBarrier对象时,不仅给出了屏障集结的线程数量,还给出了当满足线程数量到达屏障点后继续运行的线程(也就是那个汇总线程)。创建的时候先创建CyclicBarrier对象,然后用这个对象到每个具体线程中进行await即可。
>同步工具Exchanger(线程间交换数据)
多个线程按先后运行先后顺序,两两交换
public class TestEx {
public static void main(String[] args) {
new TestEx().go();
}
private void go() {
final Exchanger<Integer> exchanger = new Exchanger<>();
ExecutorService service = Executors.newCachedThreadPool();
service.execute(new Runnable() {
@Override
public void run() {
int data = 123;
System.out.println("线程" + Thread.currentThread().getName() + "要交换数据" + data + ",等待...");
try {
Thread.sleep(2000);
Integer exchange = exchanger.exchange(data);
System.out.println("线程" + Thread.currentThread().getName() + "换得数据" + exchange );
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
service.execute(new Runnable() {
@Override
public void run() {
int data = 456;
System.out.println("线程" + Thread.currentThread().getName() + "要交换数据" + data + ",等待...");
try {
Thread.sleep(2000);
Integer exchange = exchanger.exchange(data);
System.out.println("线程" + Thread.currentThread().getName() + "换得数据" + exchange );
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
来源:CSDN
作者:发面团
链接:https://blog.csdn.net/AhaQianxun/article/details/103484679