Common tool classes of Concurrent.util

Common tool classes of Concurrent.util

1.CountDownLatch uses:

He is often used to monitor some initialization operations, and after the initialization is completed, notify the main thread to continue working: Mr. Bai gave an example of zookeeper, calling services, and only after all initialization operations are completed, can the function be used

代码案例:
    public class UseCountDownLatch {
        public static void main(String[] args) {
            // 在线程内部要使用,所以要声明为final(如果不是final,那么是用不了的)
            final CountDownLatch countDown = new CountDownLatch(2);


            Thread t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println("进入t1线程,等待其他线程处理完成...");
                        countDown.await();
                        System.out.println("t1线程继续执行...");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },"t1");

            Thread t2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println("t2线程进行初始化操作");
                        Thread.sleep(3000);
                        System.out.println("t2线程初始化完毕,通知t1线程继续");
                        countDown.countDown();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });

            Thread t3 = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println("t3线程进行初始化操作");
                        Thread.sleep(4000);
                        System.out.println("t3线程初始化完毕,通知t1线程继续");
                        countDown.countDown();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });

            t1.start();
            t2.start();
            t3.start();
        }
    }

The number initialized by CountDownLatch in this code is 2, so the waiting thread can continue to execute after both CountDownLatch are released.

CyclicBarrier use

Suppose there is such a scenario: each thread represents a runner, and when the runners are ready, they start together, and as long as one person is not ready, everyone waits.

代码案例:
    public class UseCyclicBarrier {
        //静态内部类  (与外部类是没有引用关系的)
        static class Runner implements Runnable {
            private CyclicBarrier barrier;
            private String name;
            public Runner(CyclicBarrier barrier, String name) {
                this.barrier = barrier;
                this.name = name;
            }

            @Override
            public void run() {
                try {
                    Thread.sleep((new Random()).nextInt(5)*1000);
                    System.out.println(name + " 准备OK!");
                    barrier.await(); // 在等着
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
                System.out.println(name + "GO!");
            }

        }

        public static void main(String[] args) {
            CyclicBarrier barrier = new CyclicBarrier(3);  //如果传入的参数大于3的话,那么这三个线程都不会继续往下执行
            ExecutorService executor = Executors.newFixedThreadPool(3);

            executor.execute(new Thread(new Runner(barrier, "zhangsan")));
            executor.execute(new Thread(new Runner(barrier, "lisi")));
            executor.execute(new Thread(new Runner(barrier, "wangwu")));

            executor.shutdown();
        }
    }

Three threads are opened, and after all three threads are ready, these three threads will continue to execute, otherwise they will all stop at the barrier.awit() method

CyclicBarrier:

一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地
互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。

Scenario of use: When multiple machines do the same operation, it is required to perform the operation at one point, so it is possible to use this.

Note the difference between CountDownLatch and CyclicBarrier:

CountDownLatch是属于一个线程等待,其他的n个线程去给我发出通知,然后,我这一个线程执行,只是针对于一个线程.
CyclicBarrier却是中几个线程都是参与阻塞的,这几个线程之后都要在同一点去执行继续的任务

Use of Callable and Future:

This example is actually the Future mode we implemented before. JDK gives us an implementation package, which is very simple to use.

Future模式非常适合在处理很耗时很长的业务逻辑时进行使用,可以有效地减少系统的响应时间,提高系统的吞吐量

Callable Interface: The Callable interface is similar to Runnable, both are designed for classes whose instances may be executed by another thread. But Runnable doesn't return results and cannot throw checked exceptions.

老师的案例代码:
    public class UseFuture implements Callable<String>{

        private String param;
        public UseFuture(String param) {
            this.param = param;
        }

        /* 这里是真实的业务逻辑,其执行可能很慢  */
        @Override
        public String call() throws Exception {
            //模拟执行耗时操作
            Thread.sleep(5000);
            /*System.out.println(1/0);*/
            String result = this.param + " | " + "处理完毕";
            return result;
        }

        public static void main(String[] args) throws InterruptedException, ExecutionException {
            String queryStr = "query";
            //构造FutureTask,并且传入需要真正进行业务逻辑处理的类,该类一定是实现了Callable接口的类
            FutureTask<String> futureTask1 = new FutureTask<String>(new UseFuture("query"));

            FutureTask<String> futureTask2 = new FutureTask<String>(new UseFuture("QUERY"));

            //创建一个固定线程的线程池
            ExecutorService executor = Executors.newFixedThreadPool(2);
            //submit和execute的区别: 第一点是submit可以传入实现Callable接口的实例对象, 第二点是submit方法有返回值
            Future<?> s1 = executor.submit(futureTask1);   //  任务被提交了
            Future<?> s2 = executor.submit(futureTask2);
            System.out.println("请求完毕"); 
            //单独启动两个前程去执行任务
            System.out.println(s1.get());   //  一定要等FutureTask处理完请求之后才能调用get()方法获取
            System.out.println(s2.get());   //  返回值若为空,代表处理完成

            new Thread(()->{
                try {
                    // 要获取自己的线程执行的结果,是异步地区等待结果的返回  (主线程该怎么走就这么走,不影响)
                    System.out.println("数据1:" + futureTask1.get());  //get()方法是异步去获得数据的
                    System.out.println("数据2:" + futureTask2.get());
                } catch (Exception e) {
                    e.printStackTrace();
                }   //这个get()方法是异步获取的
            }).start();

            try {
                //这里可以做额外的数操作,也就是主程序执行其他的而业务逻辑
                System.out.println("处理实际的业务逻辑....");
                Thread.sleep(7000);
                System.out.println("处理实际的业务逻辑  结束....");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            executor.shutdown();
        }
    }

Carefully analyze this small program and compare the Future mode we wrote before

signal:

The Semaphore semaphore is very suitable for high concurrent access. Before the system goes online, it is necessary to evaluate the access volume of the system. Of course, this value is definitely not just slapped on the head. It is based on past experience, data, and the number of visits over the years , and make a reasonable evaluation of the promotion efforts. Of course, the evaluation standard cannot be too large or too small. If it is too large, the invested resources will not achieve the actual effect, which is a pure waste of resources. If it is too small, a peak visit at a certain time point will occur. The amount can directly overwhelm the system

PV(page view) :

网站的总访问量,页面浏览量或点击量,用户没刷新一个就会被记录一次.

UV(unique Visitor) :

访问网站的电脑客户端为一个访客,一般来讲,时间上以00:00-24:00之内相同的ip客户端只记录一次.

QPS(query per second) :

即每秒查询数,qps很大程度上代表了系统业务上的繁忙程度,每次请求的背后,可能对应着多次磁盘I/O,多次网络请求,多个CPU时间片段等,我们通过qps可以非常
直观地了解当前系统业务情况,一旦当前qps超过所设定的预警阀值,可以考虑增加机器,对集群扩容,以免压力过大导致宕机,可以根据前期的压力测试得到的估值,
再结合后期综合运维情况,估算出阀值.

RT(response time) :

即请求的相应时间,这个指标非常关键,直接说明前端用户的体验,因此任何系统设计师都想降低rt时间.

Of course, it also involves cpu, memory, network, disk, etc. There are many more detailed issues, such as database-level statistics such as select, update, delete/ps (ps: the number of accesses per second)

How to solve high concurrency?

1. On the network side.

2. At the server level.

3. The most important thing to solve the high concurrency security problem is not the technology but the business. Strictly modularize the business.

4. Restrict traffic at the java level (generally, redis is used to limit traffic more often)

案例代码:
    public class UseSemaphore {
        public static void main(String[] args) {
            ExecutorService executor = Executors.newCachedThreadPool();
            //只能5个线程同时访问
            final Semaphore semp = new Semaphore(5);
            //模拟20个客户端访问
            for(int index = 0; index < 20; index++) {
                final int NO = index;
                Runnable run = new Runnable() {
                    @Override
                    public void run() {
                        try {
                            //获取许可
                            semp.acquire();
                            System.out.println("Accessiong:" + NO + "-----" + semp.getQueueLength());
                            //模拟实际业务逻辑
                            Thread.sleep((long) (Math.random()*10000));
                            //f访问完之后,释放
                            semp.release();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                };
                executor.execute(run);
            }

            //退出线程池
            executor.shutdown();
        }
    }

See the printout:

    Accessiong:1-----0
    Accessiong:4-----0
    Accessiong:0-----0
    Accessiong:2-----0
    Accessiong:3-----0
    Accessiong:6-----14
    Accessiong:7-----13
    Accessiong:10-----12
    Accessiong:11-----11
    Accessiong:5-----10
    Accessiong:8-----9
    Accessiong:9-----8
    Accessiong:12-----7
    Accessiong:13-----6
    Accessiong:14-----5
    Accessiong:15-----4
    Accessiong:16-----3
    Accessiong:17-----2
    Accessiong:18-----1
    Accessiong:19-----0

At the beginning, 5 threads can be executed at the same time. After that, after the execution of that thread is completed, the waiting thread grabs the time slice and executes it.

Capacity Assessment:

一般来说通过开发,运维,测试,以及业务等相关人员,综合处系统的一系列阀值,然后我们根据阀值,如qps,rt等,对系统进行有效的的变更.
一般来讲,我们进行多轮压力测试以后,可以对系统进行峰值评估,采用所谓的80/20原则,即80%的访问请求将在20%的时间内达到.这样我们可以根据系统对应的PV
计算出峰值qps.
峰值qps=(总pv*80%)/(60*60*24*20%)

Lock:

在java多线程中,我们知道可以使用synchronized关键字来实现线程间的同步互斥工作,那么其实还有一个更优秀的机制去完成这个"同步互斥"工作,他就是
Lock对象,我们主要学习两种锁,重入锁和读写锁.他们具有比synchronized更为强大的功能,并且有嗅探锁定,多路分支等功能.

Reentrant lock:

Add a lock to the part of the code that needs to be synchronized, but don't forget to release the lock at the end, otherwise the lock will never be released, and other threads will never be able to get in.
Generally the lock must have the lock at the bottom of the finally CopyOnWrite class. realized

案例代码:
    public class UseReentrantLock {
        private Lock lock = new ReentrantLock();

        public void method01() {
            try {
                lock.lock();
                System.out.println("当前线程:" + Thread.currentThread().getName() + "进入method01");
                Thread.sleep(1000);
                System.out.println("当前线程:" + Thread.currentThread().getName() + "退出method01");
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                //必须要有unLock()
                lock.unlock();
            }
        }
        public void method02() {
            try {
                lock.lock();
                System.out.println("当前线程:" + Thread.currentThread().getName() + "进入method02");
                Thread.sleep(2000);
                System.out.println("当前线程:" + Thread.currentThread().getName() + "退出method02");
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                //必须要有unLock()
                lock.unlock();
            }
        }

        public static void main(String[] args) {

            final UseReentrantLock url = new UseReentrantLock();

            // 方法里面使用了Lock锁,一次就只能有一个线程进行操作
            Thread t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    url.method01();
                    //url.method02();
                }
            },"t1");
            Thread t2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    //url.method01();
                    url.method02();
                }
            },"t2");

            t1.start();
            t2.start();
        }
    }

Before jdk1.8, the performance of synchronized is worse than that of Lock, but after jdk1.8, synchronized has made a series of optimizations, and its performance is similar to that of Lock. Lock is more flexible than synchronized

Lock and wait/notify:

还记得我们在使用synchronized的时候,如果需要多线程间进行写作工作则需要Object的wait()和notify(),notifyAll()方法进行配合工作.
那么同样,我们在使用Lock的时候,可以使用一个新的等待/通知的类,它就是Condition.这个Condition一定是针对具体   某一把锁.也就是只有在锁的基础
上才会产生Condition
之前用synchronized的时候,有时候是需要自己new一个Object()对象出来作为锁,但是这个Object()就只是一个对象,jdk就提供了一个Lock类,里面进行了优化

案例代码:
public class UseCondition {
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public void method01() {
        try {
            lock.lock();
            System.out.println("当前线程: " + Thread.currentThread().getName() + "进入等待状态...");
            Thread.sleep(3000);
            System.out.println("当前线程: " + Thread.currentThread().getName() + "释放锁...");
            condition.await();
            System.out.println("当前线程: " + Thread.currentThread().getName() + "继续执行...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void method02() {
        try {
            lock.lock();
            System.out.println("当前线程: " + Thread.currentThread().getName() + "进入等待状态...");
            Thread.sleep(3000);
            System.out.println("当前线程: " + Thread.currentThread().getName() + "发出唤醒...");
            //并不会释放锁
            condition.signal();   //  相当于  Object  的  notify    (发出唤醒,但是等等这个线程走完代码释放锁之后,另一个线程才能继续执行)
            //Thread.sleep(3000);
            System.out.println("当前线程: " + Thread.currentThread().getName() + "继续执行...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        UseCondition uc = new UseCondition();
        new Thread(new Runnable() {
            @Override
            public void run() {
                uc.method01();
            }
        },"t1").start();;
        new Thread(new Runnable() {
            @Override
            public void run() {
                uc.method02();
            }
        },"t2").start();;
    }
}

Multiple Conditions:

We can generate multiple Conditions through a Lock object for multi-thread interaction, which is very flexible. It can wake up some threads that need to be awakened, and other threads continue to wait for notifications

public class UseManyCondition {

    private ReentrantLock lock = new ReentrantLock();
    private Condition c1 = lock.newCondition();
    private Condition c2 = lock.newCondition();

    public void m1(){
        try {
            lock.lock();
            System.out.println("当前线程:" +Thread.currentThread().getName() + "进入方法m1等待..");
            c1.await();
            System.out.println("当前线程:" +Thread.currentThread().getName() + "方法m1继续..");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void m2(){
        try {
            lock.lock();
            System.out.println("当前线程:" +Thread.currentThread().getName() + "进入方法m2等待..");
            c1.await();
            System.out.println("当前线程:" +Thread.currentThread().getName() + "方法m2继续..");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void m3(){
        try {
            lock.lock();
            System.out.println("当前线程:" +Thread.currentThread().getName() + "进入方法m3等待..");
            c2.await();
            System.out.println("当前线程:" +Thread.currentThread().getName() + "方法m3继续..");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void m4(){
        try {
            lock.lock();
            System.out.println("当前线程:" +Thread.currentThread().getName() + "唤醒..");
            c1.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void m5(){
        try {
            lock.lock();
            System.out.println("当前线程:" +Thread.currentThread().getName() + "唤醒..");
            c2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {


        final UseManyCondition umc = new UseManyCondition();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                umc.m1();
            }
        },"t1");
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                umc.m2();
            }
        },"t2");
        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                umc.m3();
            }
        },"t3");
        Thread t4 = new Thread(new Runnable() {
            @Override
            public void run() {
                umc.m4();
            }
        },"t4");
        Thread t5 = new Thread(new Runnable() {
            @Override
            public void run() {
                umc.m5();
            }
        },"t5");

        t1.start(); // c1
        t2.start(); // c1
        t3.start(); // c2


        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        t4.start(); // c1
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t5.start(); // c2

    }
}

Read-write lock:

读写锁ReentrantWriteLock,其核心就是实现读写分离的锁.在高并发访问下,尤其是读多写少的情况下,性能要远高于重入锁.
之前写的synchronized,ReentrantLock时,我们知道,同一时间内只能有一个线程进行访问被锁定的代码,那么读写锁则不同,其本质是分成两个锁,即读锁,写锁.
在读锁下,多个线程可以并发地进行访问,但是在写锁的时候,只能一个一个的顺序访问.

The formula: read-read sharing, write-write mutual exclusion, and read-write mutual exclusion.

public class UseReentrantReadWriteLock {

    private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();

    private ReadLock readLock = rwLock.readLock();
    private WriteLock writeLock = rwLock.writeLock();


    public void read() {
        try {
            readLock.lock();
            System.out.println("当前线程:" + Thread.currentThread().getName() + "进入...");
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            readLock.unlock();
        }
    }

    public void write() {
        try {
            writeLock.lock();
            System.out.println("当前线程:" + Thread.currentThread().getName() + "进入...");
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            writeLock.unlock();
        }
    }

    public static void main(String[] args) {
        final UseReentrantReadWriteLock urrw = new UseReentrantReadWriteLock();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                urrw.read();
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                urrw.read();
            }
        });
        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                urrw.write();
            }
        });
        Thread t4 = new Thread(new Runnable() {
            @Override
            public void run() {
                urrw.write();
            }
        });

        //两个都是读锁  能同时运行
        /*t1.start();  
        t2.start();*/
        //一个是读锁,一个是写锁,
        /*t1.start();
        t3.start();*/
        //两个都是写锁,是互斥的
        t3.start();
        t4.start();
    }
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325861840&siteId=291194637