zoukankan      html  css  js  c++  java
  • Java笔记(十八)同步和协作工具类

    同步和协作工具类

    一、读写锁ReentrantReadWriteLock

    ReadWriteLock接口的定义为:

    public interface ReadWriteLock {
        Lock readLock();
        Lock writeLock();
    }

    读操作使用读锁,写操作使用写锁。只有"读-读"操作是可以并行的,"读-写"和"写-写"都不行。

    始终只有一个线程能进行写操作,在获取写锁时,只有没有任何线程持有任何锁才可以获取到,

    在持有写锁时,其他任何线程都获取不到任何锁。在没有其他线程持有写锁的情况下,多个线程可以获取和持有读锁。

    ReentrantReadWriteLock的两个构造方法:

    public ReentrantReadWriteLock() {
            this(false);
    }
    public ReentrantReadWriteLock(boolean fair) {
            sync = fair ? new FairSync() : new NonfairSync();
            readerLock = new ReadLock(this);
            writerLock = new WriteLock(this);
    }

    其中fair表示是否公平。

    二、信号量Semaphore

    信号量类Semaphore类用来限制对资源并发访问的线程数,构造方法:

    //permits表示许可数量
    public Semaphore(int permits)
    //fair表示是否公平
    public Semaphore(int permits, boolean fair)

    Semaphore方法与锁类似,主要有两类方法,获取许可和释放许可:

    //阻塞获取许可
    public void acquire() throws InterruptedException
    //阻塞获取许可,不响应中断
    public void acquireUninterruptibly()
    //批量获取多个许可
    public void acquire(int permits) throws InterruptedException
    public void acquireUninterruptibly(int permits)
    //尝试获取
    public boolean tryAcquire()
    //限定等待时间获取
    public boolean tryAcquire(int permits, long timeout,
    TimeUnit unit) throws InterruptedException
    //释放许可
    public void release()

    限制并发访问数量不超过100的例子:

    public class AccessControlService {
        public static class ConcurrentLimitException extends RuntimeException {
        }
        private static final int MAX_PERMITS = 100;
        private Semaphore permits = new Semaphore(MAX_PERMITS, true);
        public boolean login(String name, String password) {
            //每次acquire都会消耗一个许可
            if (!permits.tryAcquire()) {
                throw new ConcurrentLimitException();
            }
            return true;
        }
        public void logout(String name) {
            permits.release();
        }
    }
    Semaphore permits = new Semaphore(1);
    permits.acquire();
    //程序会阻塞在第二个acquire调用
    permits.acquire();
    System.out.println("acquired");

    信号量也是基于AQS实现的。

    三、倒计时门栓CountDownLatch

    用于需要线程同步的情景。该类相当于一个门栓,一开始是关闭的,所有希望通过该门的线程都需要等待,

    然后开始倒计时,倒计时变为0的时候,门栓打开,所有线程通过,它是一次性的,打开后不能关闭。构造函数:

    public CountDownLatch(int count)

    与多个线程的协作方法:

    //检查计数是否为0如果大于0就等待。await可以被中断,也可以设置最长等待时间
    public void await() throws InterruptedException
    public boolean await(long timeout, TimeUnit unit) throws InterruptedException
    //countDown检查计数,如果已经为0,直接返回,否则减少计数,如果新的计数变为0,
    //则唤醒所有线程
    public void countDown()
    public class RacerWithCountDownLatch {
        static class Racer extends Thread {
            CountDownLatch latch;
            public Racer(CountDownLatch latch) {
                this.latch = latch;
            }
            @Override
            public void run() {
                try{
                    this.latch.await();
                    System.out.println(getName() + " start run "
                            + System.currentTimeMillis());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        public static void main(String[] args) throws InterruptedException {
            int num = 10;
            CountDownLatch latch = new CountDownLatch(1);
            Thread[] threads = new Thread[num];
            for (int i = 0; i < num; i++) {
                threads[i] = new Racer(latch);
                threads[i].start();
            }
            Thread.sleep(1000);
            latch.countDown();
            /*Thread-0 start run 1545714108398
            Thread-3 start run 1545714108398
            Thread-4 start run 1545714108398
            Thread-5 start run 1545714108398
            Thread-6 start run 1545714108398
            Thread-7 start run 1545714108399
            Thread-8 start run 1545714108399
            Thread-9 start run 1545714108399*/
        }
    }

    四、循环栅栏CyclicBarrier

    所有线程在到达栅栏后都需要等待其他线程,等所有线程都到达后再一起通过,

    它是循环的,可以用作重复的同步。构造方法:

    //parties参与线程个数
    public CyclicBarrier(int parties)
    //barrierAction表示所有线程到达栅栏后,所有线程执行下一步动作前,
    //运行参数中的动作,这个动作由最后一个到达栅栏的线程执行
    public CyclicBarrier(int parties, Runnable barrierAction)

    主要方法:

    //等待其他线程到达栅栏,调用await后表示自己已经到达,如果是最后一个到达的,就执行可选命令,执行完毕后,唤醒所有等待的线程,然后重置内部的同步计数
    public int await() throws InterruptedException, BrokenBarrierException
    public int await(long timeout, TimeUnit unit) throws InterruptedException,
    BrokenBarrierException, TimeoutException

    注意:在CyclicBarrier中,参与的线程是互相影响的,只要有其中的一个线程在调用await时被中断或者超时了,

    栅栏就会被破坏。此外,如果栅栏动作抛出了异常,栅栏也会被破坏。被破坏后,所有在调用的await线程就会退出,

    抛出BrokenBarrierException。

    五、ThreadLocal

    1.基本概念和用法 

    线程本地变量:每个线程都有同一个变量的独特拷贝。ThreadLocal是一个泛型类,

    接受一个类型参数T,它只有一个空的构造方法,有两个主要的public方法:

    //获取值
    public T get()
    //设置值
    public void set(T value)
    public class ThreadLocalBasic {
        static ThreadLocal<Integer> local = new ThreadLocal<Integer>();
        public static void main(String[] args) throws InterruptedException {
            Thread child = new Thread(){
                @Override
                public void run() {
                    System.out.println("child thread initial " + local.get()); //null
                    local.set(200);
                    System.out.println("child thread final: " + local.get()); //200
                }
            };
            local.set(100);
            child.start();
            child.join();
            System.out.println("Main thread final : " + local.get()); //100
        }
    }

    从上面的例子可以看出,一个线程本地变量,在每个线程都有自己的独立值。

    ThreadLocal的其他方法:

    //用于提供初始值
    protected T initialValue()
    //删除当前线程的对应值,删掉后再次调用get就会获取初始值
    public void remove()

    应用:是实现线程安全、减少竞争的一种方案。

  • 相关阅读:
    HL 7.19 FFT多项式乘法
    HL 7.18 杂题整理 随笔
    BZOj 3208 食物 生成函数+广义二项式定理
    HL 7.14 整理杂题 随笔
    AC自动机 后缀数组 随笔
    Python 之time时间模块
    Python 之sys系统模块
    Uni-app 之猿产地项目
    Tkinter 之socket聊天室
    Python 之logging日志模块
  • 原文地址:https://www.cnblogs.com/Shadowplay/p/10173829.html
Copyright © 2011-2022 走看看