Java Multithreading 06 - JUC Concurrency Package 02

1 Thread synchronization tool ​CountDownLatch​class

Synchronization​CountDownLatch​ 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. Every ​countDown()​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 after ​await()​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

​​ is​CyclicBarrier​ 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, use​cyclicBarrier.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 is​Semaphore​ 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

Guess you like

Origin blog.csdn.net/QQ156881887/article/details/128987036