1 Thread synchronization tool CountDownLatch
class
SynchronizationCountDownLatch
helper class that allows one or more threads to wait until a set of operations being performed in other threads is completed.
- The class
CountDownLatch
is a synchronous counter. The int parameter is passed in during construction. This parameter is the initial value of the counter. EverycountDown()
time the method counter is decremented by 1. When the counter is greater than 0,await()
the method will block the current thread to continue. implement. - Since
countDown()
the method ,await()
the method will block until the current count reaches zero. After that, all waiting threads will be released, and all subsequent calls afterawait()
the method will return immediately. This phenomenon only occurs once, and the count cannot be reset. One thread or multiple threads wait for another N threads to complete something before executing it.
Create thread class
import java.util.concurrent.CountDownLatch;
public class UserThread1 extends Thread {
private int sum1 = 0;
private CountDownLatch cd;
public UserThread1(CountDownLatch cd){
this.cd = cd;
}
@Override
public void run() {
for(int i=0;i<=50;i++){
sum1 += i;
}
cd.countDown();
}
public int getSum1(){
return sum1;
}
}
import java.util.concurrent.CountDownLatch;
public class UserThread2 extends Thread {
private int sum2 = 0;
private CountDownLatch cd;
public UserThread2(CountDownLatch cd){
this.cd = cd;
}
@Override
public void run() {
for(int i=51;i<=100;i++){
sum2 += i;
}
cd.countDown();
}
public int getSum2(){
return sum2;
}
}
countDown()
The method is used in the thread class to update the counter.
Create test class
import java.util.concurrent.CountDownLatch;
public class Test {
public static void main(String[] args) {
CountDownLatch cd = new CountDownLatch(2);
UserThread1 thread1 = new UserThread1(cd);
UserThread2 thread2 = new UserThread2(cd);
thread1.start();
thread2.start();
try {
cd.await();
int sum = thread1.getSum1() + thread2.getSum2();
System.out.println("1~100 的和是:" + sum);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
The output is:
The sum of 1~100 is: 5050
The await() method is called in the test class to block the current thread until the counter when CountDownLatch is initialized becomes 0.
Notice:
- The initial value of CountDownLatch should be equal to the number of countDown() in all threads.
- If the above test class is initialized to 1 and the initial value is less than the countDown() number, that is, after
CountDownLatch cd = new CountDownLatch(1);
running the program, the output result may be
The sum of 1~100 is: 1275
- If the above test class is initialized to 3 and the initial value is greater than the countDown() number, that is, after
CountDownLatch cd = new CountDownLatch(3);
running the program, the program will be suspended and cannot exit the blocking state.
2 Thread synchronization tool CyclicBarrier
class
isCyclicBarrier
a synchronization helper class that allows a group of threads to wait for each other until a common barrier point is reached. Similar to a meeting point.
Because the barrier can be reused after the waiting thread is released, it is called a cyclic barrier.
2.1 Create resource classes called by threads
public class TimeCount {
private int count1;
private int count2;
private int sum;
public int getCount1() {
return count1;
}
public void setCount1(int count1) {
this.count1 = count1;
}
public int getCount2() {
return count2;
}
public void setCount2(int count2) {
this.count2 = count2;
}
public int getSum() {
return this.count1 + this.count2;
}
public void setSum(int sum) {
this.sum = sum;
}
}
2.2 Create thread class
After the task execution is completed, usecyclicBarrier.await();
to add a blocking point.
import java.util.concurrent.CyclicBarrier;
public class UserRunn implements Runnable{
private TimeCount tc;
private String name;
private CyclicBarrier cyclicBarrier;
public UserRunn(TimeCount tc, String name, CyclicBarrier cyclicBarrier){
this.tc = tc;
this.name = name;
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
if(name.equals("看书")){
try {
Thread.sleep(4000);
tc.setCount1(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else if(name.equals("写字")){
try {
Thread.sleep(2000);
tc.setCount2(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//阻塞点,等所有线程运行完毕,自动解锁
try {
cyclicBarrier.await();
System.out.println("------------" + name + " end-----------");
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.3 Create test class
Initialize CyclicBarrier, because we need two threads to reach the blocking point at the same time, so new CyclicBarrier(2, new Runnable())
the first parties
is set to 2.
import java.util.concurrent.CyclicBarrier;
public class Test {
public static void main(String[] args) {
TimeCount tc = new TimeCount();
CyclicBarrier cyclicBarrier = new CyclicBarrier(2, new Runnable() {
@Override
public void run() {
int sum = tc.getSum();
System.out.println("功能总耗时:" + sum);
}
});
new Thread(new UserRunn(tc, "看书", cyclicBarrier)).start();
new Thread(new UserRunn(tc, "写字", cyclicBarrier)).start();
}
}
2.4 Output results
Total function time: 6000
------------Reading end-----------
------------Writing end-----------
The "reading" thread needs to execute for 4 seconds, while the "writing" thread needs to execute for 2 seconds. When reaching the blocking point, the thread that takes a short time to execute will wait for the thread that takes a long time to execute. When the number of threads reaching the blocking point is created in Section 2.3 When the CyclicBarrier object is initialized with the parties value, all threads are released simultaneously.
Notice:
The CyclicBarrier object created should be initialized with the parties value that is the same as the number of threads we want to achieve reaching the barrier point.
If parties are smaller than the expected value, the convergence effect will not be achieved;
If parties are larger than expected, the thread will remain blocked.
3 Thread synchronization tool Semaphore
class
It isSemaphore
a counting semaphore. Its essence is a shared lock, which is implemented based on AQS and is shared through state variables.
By calling the acquire method, the state value is subtracted by one, and when release is called, the state value is increased by one.
When the state variable is less than 0, it blocks and waits in the AQS queue.
3.1 Create restaurant class
declare object
In the restaurant class, declare Semaphore
the semaphore object and the maximum acceptable number of threads new Semaphore(num)
.
Lock
Lock before the operation that requires current limiting acquire();
. When the number of threads entering this position reaches the maximum acceptable value, other threads will enter the waiting state.
release lock
After the current limiting operation is completed, the lock is released release();
, and subsequent threads will enter acquire();
the statement.
import java.util.concurrent.Semaphore;
public class Restaurant {
//通过引入Semaphore,实现餐厅限流
private final Semaphore semaphore;
public Restaurant(int num){
//设置最大可用的并行信号量
this.semaphore = new Semaphore(num);
}
public void eatConsumers(){
try {
//加锁
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "进入餐厅");
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + "离开餐厅");
//释放锁
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
3.2 Create consumer class
public class Consumer extends Thread {
private Restaurant restaurant;
public Consumer(Restaurant restaurant){
this.restaurant = restaurant;
}
@Override
public void run() {
restaurant.eatConsumers();
}
}
3.3 Create test class
Set the maximum number of threads for current limiting to 2
public class Test {
public static void main(String[] args) {
Restaurant restaurant = new Restaurant(2);
for(int i=0;i<5;i++) {
new Consumer(restaurant).start();
}
}
}
3.4 Output results
Thread-1 enters the restaurant
Thread-4 enters the restaurant
Thread-1 leaves the restaurant
Thread-4 leaves the restaurant
Thread-2 enters the restaurant
Thread-3 enters the restaurant
Thread-2 leaves the restaurant
Thread-0 enters the restaurant
Thread-3 leaves the restaurant
Thread-0 leaves the restaurant
4 Thread exchange Exchanger
class
Exchanger is a tool class used for collaboration between threads. Exchanger is used for data exchange between threads.
It provides a synchronization point where two threads can exchange each other's data.
The two threads exchange data through the exchange() method.
If the first thread executes the exchange() method first, it will wait for the second thread to also execute the exchange() method. When both threads reach the synchronization point, the two threads can exchange data and generate data from this thread. The outgoing data is passed to the other party.
import java.util.concurrent.Exchanger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test {
private static final Exchanger<String> exchanger = new Exchanger<>();
private static ExecutorService executorService = Executors.newFixedThreadPool(2);
public static void main(String[] args) {
executorService.execute(new Runnable() {
@Override
public void run() {
String a = " A 银行转入";
System.out.println(Thread.currentThread().getName() + a);
try {
String b = exchanger.exchange(a);
System.out.println(Thread.currentThread().getName() + b);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
executorService.execute(new Runnable() {
@Override
public void run() {
String a = " B 银行转出";
System.out.println(Thread.currentThread().getName() + a);
try {
String b = exchanger.exchange(a);
System.out.println(Thread.currentThread().getName() + b);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
executorService.shutdown();
}
}
Execution output
pool-1-thread-1 A bank transfer
pool-1-thread-2 B bank transfer out
pool-1-thread-2 A bank transfer
pool-1-thread-1 B bank transfer out
have to be aware of is:
If the number of threads participating in data exchange is an even number , pairwise data exchange can be achieved;
If the number of threads participating in data exchange is an odd number , the program will always be blocked.
5 Fork
-Join
Mechanism of threads
The Fork/Join framework is a framework provided by JAVA7 for executing tasks in parallel.
It is a framework that divides large tasks into several small tasks, and finally summarizes the results of each small task to obtain the results of the large task.
Divide and Conquer:
Divide a large-scale problem into smaller-scale sub-problems, then divide and conquer them, and finally combine the solutions to the sub-problems to obtain the solution to the original problem.
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
public class ContTask extends RecursiveTask<Integer> {
private int start;
private int end;
//计算任务量的阈值,最小任务量范围
private static final int TASKSIZE = 30;
private static int count = 0;
public ContTask(int start, int end){
this.start = start;
this.end = end;
}
//重写compute方法,任务执行的主要计算
@Override
protected Integer compute() {
int sum = 0;
System.out.println("开启线程进行计算" + count++);
boolean state = (end - start) <= TASKSIZE;
//如果小于等于任务的阈值
if(state){
//无需拆分任务计算
for(int i=start;i<=end;i++){
sum += i;
}
}else{
//进行拆分任务计算
System.out.printf("start-%d, end-%d这个任务需要进行拆分任务计算。。。%s%n", start, end, Thread.currentThread().getName());
//分割成两个任务
int middle = (end + start) / 2;
ContTask contTask1 = new ContTask(start, middle);
ContTask contTask2 = new ContTask(middle+1, end);
//开启线程计算分布式任务
invokeAll(contTask1, contTask2);
//阻塞,直到任务完成或取消
Integer tasksum1 = contTask1.join();
Integer tasksum2 = contTask2.join();
//结果合并
sum = tasksum1 + tasksum2;
}
return sum;
}
public static void main(String[] args) {
//分布式计算池
ForkJoinPool forkJoinPool = new ForkJoinPool();
//初始化设置任务
ContTask contTask = new ContTask(1, 100);
//分布式计算任务,提交任务
ForkJoinTask forkJoinTask = forkJoinPool.submit(contTask);
//得到最终计算结果
try {
System.out.println("最终计算结果为:" + forkJoinTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
Results of the:
Start the thread for calculation 0
The tasks start-1, end-100 need to be divided into task calculations. . . ForkJoinPool-1-worker-1
Start thread for calculation 1
The tasks start-1, end-50 need to be divided into task calculations. . . ForkJoinPool-1-worker-1
Start thread for calculation 2
The tasks start-51 and end-100 need to be divided into task calculations. . . ForkJoinPool-1-worker-2
Start thread for calculation 4
Start thread for calculation 3
Start thread for calculation 6
Start thread for calculation 5
The final calculation result is: 5050