Concurrent包工具类使用

一。读写锁

  传统的同步锁就是独占式锁,当线程使用资源时候保持独占,无论读写。当人们发现请求队列(假设)中相邻请求为读-读的时候,阻塞是一种浪费资源的操作。比如公告板,所有路过的人(请求)都是读操作,并没有因为你和他在读的时候对内容造成了改变,所以在模型中,读与读操作不需要阻塞。而读写相邻则需要进行独占式操作了,因为写未完成的时候,信息是不完整的,此时读出来的信息有可能是错误的,所以写必然要保持独占式操作。而在应用程序中,读的频率是写的好几倍,也就是说如果读-读是不阻塞的,那么对性能来说是毋庸置疑的提升。

  Java中存在一种锁,名曰:ReentrantReadWriteLock。他可以实现内存中对资源操作的读写锁,读与读是不阻塞的。

import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * Created by MacBook on 2018/3/10.
 */
public class ReadWriteLockDemo {
    private static Lock relock = new ReentrantLock();
    private static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    private static Lock readLock = readWriteLock.readLock();
    private static Lock writeLock = readWriteLock.writeLock();
    private int value;
    public Object handleRead(Lock lock) throws Exception{
        try{
            lock.lock();
            Thread.sleep(1000);
            return value;
        }finally {
            lock.unlock();
        }
    }
    public void handleWrite(Lock lock,int index) throws Exception{
        try{
            lock.lock();
            Thread.sleep(1000);
            value = index;
        }finally {
            lock.unlock();
        }
    }
    public static void main(String[] args){
        ReadWriteLockDemo demo = new ReadWriteLockDemo();
        Runnable readThread = new Runnable() {
            @Override
            public void run() {
                try{
                    System.out.println("read:"+demo.handleRead(readLock));
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        };
        Runnable writeThread = new Runnable() {
            @Override
            public void run() {
                try{
//                    demo.handleWrite(relock,new Random().nextInt());
                    demo.handleWrite(writeLock,new Random().nextInt());
                    System.out.println("id:"+Thread.currentThread().getId()+" done!");
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        };
        for(int i=0;i<18;i++){
            new Thread(readThread).start();
        }
        for(int i=0;i<18;i++){
            new Thread(writeThread).start();
        }
    }

}

    此demo使用了重入锁和读写锁的对比,在主程序中分别新建18个读写操作,如果使用了读操作,则打印的读操作是连续的;如果使用了重入锁,则可能的情况是读写相邻打印,并且都是阻塞的,读者可以自行测试体会。

二。对象监视器Condition

  在JDK实现了Lock来简化synchronized之后,Condition作为简化监视器而存在。Condition的await方法和signal方法对应对象的wait和signal。

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by MacBook on 2018/3/10.
 */
public class ConditionAndLock implements Runnable{
    static ReentrantLock lock = new ReentrantLock();
    static Condition condition = lock.newCondition();

    public void run(){
        try{
            lock.lock();
            condition.await();
            System.out.println("thread is running");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public static void main(String[] args){
        ConditionAndLock c = new ConditionAndLock();
        Thread t = new Thread(c);
        t.start();
        lock.lock();
        System.out.println("signal all");
        condition.signalAll();
        lock.unlock();
    }
}

三。倒计时器CountDownLatch

  多线程中,需要知道这批线程的最大完成任务时间,也就是从第一个任务开始到最后返回这段时间的时长,那么倒计时器是必不可少的。就像各项资源准备完毕才进行下一步操作的模型一样,CountDownLatch就是这样的多线程模型。等到所有任务调用了计数器,并且计数器总数到达某个数量时候,它才会将阻塞代码放开,让主线程往下走。

import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 倒计时器
 * Created by MacBook on 2018/3/10.
 */
public class CountDownLatchDemo implements Runnable{
    static CountDownLatch end = new CountDownLatch(10);
    static CountDownLatchDemo demo = new CountDownLatchDemo();
    public void run(){
        try{
            Thread.sleep(new Random().nextInt(10)*1000);
            System.out.println(Thread.currentThread().getId()+" check complete!");
            end.countDown();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    public static void main(String[] args) throws Exception{
        ExecutorService service = Executors.newFixedThreadPool(10);
        for(int i=0;i<10;i++){
            service.submit(demo);
        }
        end.await();
        System.out.println("fire");
        service.shutdown();
    }
}

    await方法是阻塞倒计时器所在线程的方法,等到线程池service中调用countDown方法到达一定的数量(此处是10)之后,主线程的await方法才会过去。

四。信号量

  信号量这个东西就比较玄乎了,有点像准入许可,拿到信号准入的时候才往下执行。就像是有一批人拿号,只有号码区间在某个范围的人能进去办事,然后办完事就会让资源释放,号码区间往后移。然而在信号量中应该算是复用类型的,归还了key值,将key值返回给下一个申请者。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

/**
 * Created by MacBook on 2018/3/10.
 */
public class SemapDemo implements Runnable{
    final Semaphore semp = new Semaphore(5);
    public void run(){
        try{
            semp.acquire();
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getId()+" done!");
            semp.release();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    public static void main(String[] args){
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        SemapDemo semapDemo = new SemapDemo();
        for(int i=0;i<20;i++){
            executorService.submit(semapDemo);
        }
        executorService.shutdown();
    }
}

  在acquire获得key之后,操作读写,之后release。

五。栅栏

  栅栏和倒计时器很像,就是拦住一堆线程,等到线程数达到某个设定值之后同时把它们放出去。但是不同的是,它可以每次设定值达成时候运行定制线程中的run方法。就像是每次一个栏,够数就放。

import java.util.Random;
import java.util.concurrent.CyclicBarrier;

/**
 * Created by MacBook on 2018/3/10.
 */
public class CylicBarrierDemo {
    public static class Soldier implements Runnable{
        private String soldier;
        private final CyclicBarrier cyclicBarrier;
        Soldier(String soldier,CyclicBarrier cyclicBarrier){
            this.soldier = soldier;
            this.cyclicBarrier = cyclicBarrier;
        }
        public void run(){
            try{
                cyclicBarrier.await();
                doWork();
                cyclicBarrier.await();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        public void doWork(){
            try{
                Thread.sleep(Math.abs(new Random().nextInt()%10000));
            }catch (Exception e){
                e.printStackTrace();
            }
            System.out.println(soldier + " done!");
        }
    }
    public static class BarrierRun implements Runnable{
        boolean flag;
        int n;
        public BarrierRun(boolean flag,int n){
            this.flag = flag;
            this.n = n;
        }

        public void run(){
            if(flag){
                System.out.println("士兵:"+n+"个 done!");
            }else {
                System.out.println("士兵:"+n+"个 集合完毕!");
                flag = true;
            }
        }
    }
    public static void main(String[] args){
        final int n = 10;
        Thread[] allSoldier = new Thread[n];
        boolean flag = false;
        CyclicBarrier cyclic = new CyclicBarrier(n,new BarrierRun(flag,n));
        System.out.println("集合");
        for(int i =0; i < n ; i++){
            System.out.println("士兵 "+i+" 报道");
            allSoldier[i] = new Thread(new Soldier("士兵"+i,cyclic));
            allSoldier[i].start();
        }
    }
}

  例中CyclicBarrier有两个参数,前一个就是提到的设定值,后一个就是定制线程了。每当到达设定值的时候会触发定制线程。

  每个阶段完成都会调用一下定制线程。

六。LockSupport提供线程挂起操作的支持类

  正如Condition使得原有的Object监视器封装成了新类,LockSupport提供使线程park和unpark之类的操作。

import java.util.concurrent.locks.LockSupport;

/**
 * Created by MacBook on 2018/3/10.
 */
public class LockSupportDemo {
    public static Object u = new Object();
    static ChangeObjectThread t1 = new ChangeObjectThread("t1");
    static ChangeObjectThread t2 = new ChangeObjectThread("t2");
    public static class ChangeObjectThread extends Thread{
        public ChangeObjectThread(String name){
            super.setName(name);
        }
        public void run(){
            synchronized (u){
                System.out.println("in "+getName());
                LockSupport.park();
            }
        }
    }
    public static void main(String[] args) throws Exception{
        t1.start();
        Thread.sleep(100);
        t2.start();
        LockSupport.unpark(t1);
        LockSupport.unpark(t2);
        t1.join();
        t2.join();
    }

}

  它在park时候线程会变成wait状态,而不是runnable。

 

  来自《Java高并发程序设计》的读书笔记

 

posted @ 2018-03-10 17:32  天目山电鳗  阅读(1135)  评论(0编辑  收藏  举报