四. JUC工具包和Executor框架

4.1 Atomic数据类型

4.1.1 Atomic Integer

  • 线程安全

    1. volatile关键字保证可见性和禁止重排序
    2. unsafe保证原子性
    public static void main(String[] args){
        final AtomicInteger value = new AtomicInteger();
        
        Thread t1 = new Thread(){
            @Override
            public void run(){
                int x = 0;
                while(x < 500){
                    int v = value.getAndIncrement();
                    System.out.println(Thread.currentThread().getName() + ":" + v);
                    x++
                }
            }
        };
        
        Thread t2 = new Thread(){
            @Override
            public void run(){
                int x = 0;
                while(x < 500){
                    int v = value.getAndIncrement();
                    System.out.println(Thread.currentThread().getName() + ":" + v);
                    x++
                }
            }
        };
        
        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }
  • AtomicInteger的CAS原理

    • AtomicInteger通过compare and swap(CAS)算法实现线程安全机制---最快失败策略
    for(;;){
        int current = get();
        int next = current + delta;
        if(compareAndSet(current,next)){
            return current;
              }
        }
    }
    • 当存储的预期值current与内存值value相同时,才将新值next赋值给value
  • CAS

    • CompareAndSwap:JUC包中借助CAS实现了区别于synchronized同步锁的一种乐观锁,其实CAS也算是有锁的操作,只不过由CPU来触发,比Synchronized性能更好
    • 使用CAS自定义一个不阻塞的显示锁
    public static void main(String[] args){
        final CompareAndSetLock lock = new CompareAndSetLock();
        
        for(int i=0;i < 2;i++){
            new Thread(){
                @Override
                public void run(){
                    try{
                        lock.tryLock();
                        System.out.println(Thread.currentThread().getName() + "get lock")
                        Thread.sleep(10_000L);
                    }catch(InterruptedException e1){
                        e.printStackTrace();
                    }catch(GetLockException e2){
                        e.printStackTrace();
                    }finally{
                        lock.unlock();
                    }
                }
            }.start();
        }
    }
    -----------------------
    
    public class CompareAndSetLock{
        prvivate final AtomicInteger value = new AtomicInteger(0);
        
        private Thread currentThread;
        
        public void tryLock() throw GetLockException{
            boolean success = value.compareAndSet(0,1);
            if(!success){
                throw new GetLockException("try to get lock failed");
            }
            currentThread = Thread.currentThread();
        }
        
        public void unlock(){
            if(0 == value.get()){
                return;
            }
            if(lockThread == Thread.currentThread()){
                value.compareAndSet(1,0);
            }
        }
    }

4.1.2 Atomic Boolean

  • AtomicBoolean就是只有0,1的AtomicInteger

    • AtomicBoolean用于优雅的去停止线程或在多线程下去操作一个开关,就不需要给flag添加volatile关键字了
private final static AtomicBoolean flag = new AtomicBoolean(true);

public static void main(String[] args) throw InterruptedException{
    new Thread(){
        @Override
        public void run(){
            while(this.flag.get()){
                try{
                    Thread.sleep(1000L);
                    System.out.println("working...");
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
            }
            System.out.println("finished...");
        }
    }.start();
    
    Thread.sleep(5000L);
    this.flag.set(false);
}

4.1.3 Atomic Long

  • AtomicLong和AtomicInteger的区别在于,Long是64位的,而CPU可能是32位的,就需要分高低位区分数据,所以根据支持的不同所调用的方法也不同
public final boolean compareAndSet(long expect,long update){
    return unsafe.compareAndSwapLong(this.valueOffSet,expect,update);
}

4.1.4 Atomic Reference

  • CAS锁的优点: 1.原子性 2.有序性 3.可见性

    1. volatile修饰的变量,能保证后两者
    2. CAS算法,也就是CPU级别的同步指令,相当于乐观锁,可以测到其他线程对共享数据的变化情况
  • CAS锁的弊端

    • 会引入ABA的问题,尤其是Reference数据类型
  • 解决方案

    1. 像数据库一样可以增加一个乐观锁,打一个版本标签,根据版本标签来判断
    2. 使用AtomicStampedReference来添加一个版本戳

4.1.5 Atomic Stamped Reference

  • 其原理也是增加一个版本的标记

    public class AtomicStampedReference<V>{
        
        private static class ReferenceIntegerPair<T>{
            private final T reference;
            private final int integer;
            ReferenceIntegerPair(T r,int i){
                reference = r;
                integer = i;
            }
        }
    }
    private static AtomicStampedReference<Integer> atomicRef = new AtomicStampedReference<>(100,0);
    
    public static void main(String[] args){
        Thread t1 = new Thread(){
            @Override
            public void run(){
                try{
                    TimeUnit.SECONDS.sleep(1);
                    boolean success = atomicRef.compareAndSet(
                        100,101,atomicRef.getStamp(),atomicRef.getStamp() + 1);
                    System.out.println(success);//ture
                    
                    success = atomicRef.compareAndSet(
                        101,100,atomicRef.getStamp(),atomicRef.getStamp() + 1);
                    System.out.println(success);//ture
                }
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }.start();
        
        Thread t2 = new Thread(){
            @Override
            public void run(){
                try{
                    int stamp = atomicRef.getStamp();
                    System.out.println("Before:" + stamp);
                    TimeUnit.SECONDS.sleep(2);
                    boolean success = atomicRef.compareAndSet(100,101,stamp,stamp + 1);
                    System.out.println(success);//false
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
            }
        }.start();
    }

4.1.6 Atomic xxx Field Updater

  • 当一个类没有进行AtomicReference封装,并且其Field也不是Atomic时,如果想要对其属性进行原子性操作,就可以使用

    • AtomicIntegerFieldUpdater<TestMe> updater = AtomicIntegerFieldUpdater.newUpdater(TestMe.class,"i");
    public class AIFUTest{
        private volatile int i;
        //private AtomicInteger j = new AtomicInteger();
        private AtomicIntegerFieldUpdater<AIFUTest> updater = AtomicIntegerFieldUpdater.newUpdater(AIFUTest.class,"i");
        
        public void update(int newValue){
            updater.compareAndSet(this,i,newValue);
        }
        
        public void get(){
            return i;
        }
        
        public static void main(String[] args){
            AIFUTest test = new AIFUTest();
            test.update(10);
            System.out.println(test.get());
        }
    }
  • 应用场景:可以解决AtomicStampedReference占资源大的问题

    1. 想让类的属性操作具备原子性

      1. volatile
      2. 非private,protected(如果是当前类也可以是private和protected)
      3. 类型必须一致
    2. 不想使用显示锁或synchronized
    3. 大量需要原子类型修饰的对象

4.1.7 Unsafe

  • Unsafe类不能直接调用,但是可以通过反射来调用类中的方法和获取属性
public static void main(String[] args){
    //Unsafe unsafe = Unsafe.getUnsafe();
    Unsafe unsafe = getUnsafe();
    System.out.println(unsafe);
}

private static Unsafe getUnsafe(){
    try{
        Field f = Unsafe.class.getDeclaredField("theUnsafe");
        f.setAccessible(true);
        return (Unsafe)f.get(null);
    }catch(Exception e){
        e.printStackTrace();
    }
}
  • 使用Unsafe自定义CASCounter

    • ReetrantLock底层也是使用Unsafe实现的
public class CASCounter{
    private volatile long counter = 0;
    private Unsafe unsafe;
    private long offset;
    
    CASCounter() throws Exception{
        unsafe = getUnsafe();
        offset = unsafe.objectFieldOffset(CASCounter.class.getDeclaredField("counter"));
    }
    
    public void increment(){
        long current = counter;
        while(!unsafe.compareAndSwapLong(this,offset,counter,counter + 1)){
            current = counter;
        }
    }
}
  • Unsafe的其他操作---不建议使用

    1. Unsafe#allcateInstance(Class clazz):能够跳过构造方法进行实例化,对于一般获取实例独享,如:new,反射等都会进行实例化,实例化的同时会调用构造方法

      public static void main(String[] args){
          Unsafe unsafe = getUnsafe();
          Simple simple = (Simple)unsafe.allocateInstance(Simple.class);
          
      }
      
      class Simple{
          public Simple(){
              System.out.println("---------");
          }
      }
    2. Unsafe#putInt():方法能对内存中的数据进行修改

      public static void main(String[] args){
          Guard guard = new Guard();
          guard.word();
          
          Field f = guard.getClass().getDeclaredField("ACCESS_ALLOWED");
          unsafe.putInt(guard,unsafe.objectFieldOffset(f),42);
          guard.work();
      }
      
      class Guard{
          private int ACCESS_ALLOWED = 1;
          
          private boolean allow(){
              return 42 == ACCESS_ALLOWED;
          }
          
          public void work(){
              if(allow()){
                  System.out.println("allowed...")
              }
          }
      }
    3. Unsafe#defineClass():可以将从class文件中加载读取的字节数组转换位Class对象,从而通过反射调用字节码对象中的成员属性和方法----即拥有类加载器的功能

      public static void main(String[] args){
          byte[] bytes = loadClassContent();
          Class clazz = Unsafe.defineClass(null,bytes,0,bytes.length);
          int v = (Integet)clazz.getMethod("get").invoke(clazz.newInstance(),null);
          System.out.println(v);
      }

4.2 Java并发包工具

4.2.1 CountDownLatch

  • 简单的线程同步工具包,一个或多个线程去等待多个线程完成
  • 在一系列串行化过程中,如果中间某些部分是可并行化的,那就选择并行化去做,提高运行速度,然后再串行化----其实这就可以看作是wait-notify的工具包
  • 中断await()的三种方式

    1. CountDown减到0
    2. await(1000,TimeUnit.MILLSECONDS)计时结束
    3. 对wait线程调用interrupted()方法
    public class CountDownLatchExample{
        public static void main(String[] args){
            final CountDownLatch latch = new CountDownLatch(1);
            final Thread main = Thread.currentThread();
            
            new Thread(){
                @Override
                public void run(){
                    try{
                        Thread.sleep(10_000L);
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                    //latch.countDown();   方式一
                    
                    main.interrupt();   ///方式三
                }
            }.start();
            
              latch.await();
            //latch.await(1000,TimeUnit.MILLSECONDS);   方式二
            System.out.println("--------------");
        }
    }
  • 案例1:当使用线程池时,不能获取每个线程对象对其中的单个线程使用join()方法,所以做不到让main线程在其他线程执行完后再结束的操作

    1. 解决方法一:使用ExecutorService#shutdown()和ExecutorService#awaitTermination()

      • shutdown和awaitTermination为接口ExecutorService定义的两个方法,一般情况配合使用来关闭线程池

        1. shutdown方法:平滑的关闭ExecutorService,当此方法被调用时,ExecutorService停止接收到新的任务并且等待已提交的任务(包含正在执行的和提交未执行的)执行完成,当所有提交的任务执行完毕,线程池即被关闭
        2. awaitTermination方法:接收timeout和TimeUnit两个参数,用于设定超时时间及单位,当超时时间到会检测ExecutorService是否已经关闭,若关闭返回true,否则返回false,一般与shutdown组合使用
public static void main(String[] args){
    final ExecutorService executor = Executors.newFixedThreadPool(2);
    for(int i=0;i < 10;i++){
        executor.execute(new SimpleRunnable());
    }
    
    executor.shutdown();
    executor.awaitTermination(1,TimeUnit.HOURS);
    System.out.print("all of work finish...")
}
  1. 解决方法二:使用CountDownLatch,当计数器减小到0时解除阻塞

    public static void main(String[] args){
        final ExecutorService executor = Executor.newFixedThreadPool(2);
        final CountDownLatch latch = new CountDownLatch(10);
        
        for(int i=0;i < 10;i++){
            executor.execute(new SimpleRunnable(latch));
        }
        
        latch.await();
        System.out.println("all of work finish...");
        executor.shutdown()
    }
    -------------------------------------
        
    static class SimpleRunnable implement Runnable{
        private final CountDownLatch latch;
        private final Random random = new Random(System.currentTimeMills());
        
        public SimpleRunnable(CountDownLatch latch){
            this.latch = latch;
        }
        @Override
        public void run(){
            try{
                Thread.sleep(Random.nextInt(1000));
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            
            System.out.println(Thread.currentThread().getName() + "woking");
            latch.countDown();
        }
    }
  • 案例二:

    • 有一个任务分为ABC三个步骤,原本ABC串行化执行,但是AB可以同时执行,而C需要B执行完后才能开始执行,那么就可以使用CountDownLatch来作为一个wait-notify机制,A执行完后wait(),等B执行完后CountDown()来notifyA,从而接着执行C,时间由1+2+1变为2+1
    • 同时我们也可以通过线程间通信来完成,两个线程共享一个shareData,当A线程执行完后去查看变量状态,若未完成则进入wait,等待B线程完成后notify

      public static void main(String[] args){
          final CountDownLatch latch = new CountDownLatch(1);
          
          new Thread(){
              @Override
              public void run(){
                  try{
                      System.out.println("begin task a...");
                      Thread.sleep(1000L);
                      latch.await();
                      System.out.println("begin task c...");
                  }catch(InterruptedException e){
                      e.printStackTrace();
                  }
              }
          }.start();
          
          new Thread(){
              @Override
              public void run(){
                  try{
                      System.out.println("begin task b...");
                      Thread.sleep(1000L);
                  }catch(InterruptedException e){
                      e.printStackTrace();
                  }finally{
                      latch.countDown();
                  }
              }
          }.start();
          
      }

4.2.2 Cyclic Barrier

  • 在CountDownLatch上添加reset功能-----多个线程互相等待完成
  • 与CountDownLatch有点类似,但是CyclicBarrier是每个线程执行一个完阶段后互相自主等待,待全部完成后继续执行,而CountDownLatch是进入阻塞后,当全部完成一个阶段后,由CountDown减少到0后发出解除阻塞的指令
  • CountDownLatch不能reset,而CyclicBarrier可以循环使用,reset状态=begin状态=finish状态
  • 案例一:两个线程,执行过程等待同步结束,且同步后回调通知

    public static void main(String[] args){
        final CyclicBarrier barrier = new CyclicBarrier(3);
        
        new Thread(){
            @Override
            public void run(){
                try{
                    TimeUnit.SECONDS.sleep(10);
                    System.out.println(Thread.currentThread().getName() + "finished...");
                    barrier.await();
                    System.out.println(Thread.currentThread().getName() + "all...");
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
            }
        }.start();
        
        new Thread(){
            @Override
            public void run(){
                try{
                    TimeUnit.SECONDS.sleep(5);
                    System.out.println(Thread.currentThread().getName() + "finish");
                    barrier.await();
                    System.out.println(Thread.currentThread().getName() + "all...");
                }
            }
        }.start();
        
        barrier.await();
        System.out.println(Thread.currentThread().getName() + "all...");
    }
  • 案例二:使用CountDownLatch模仿CyclicBarrier实现CountDown到0后自动回调

    public static void main(String[] args){
        final MyCountDownLatch latch = new MyCountDownLatch(2,new Runnable(){
            @Override
            public void run(){
                System.out.println("call back behind finished...");
            }
        });
        
        new Thread(){
            @Override
            public void run(){
                try{
                    TimeUnit.SECONDS.sleep(1);
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
                latch.countDown();
                System.out.println(Thread.currentThread().getName() + "finished.");
            }
        }.start();
        
        new Thread(){
            @Override
            public void run(){
                try{
                    TimeUnit.SECONDS.sleep(2);
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
                latch.countDown();
                System.out.println(Thread.currentThread().getName() + "finished.");
            }
        }.start();
        
    }
    ------------------------------------------
        public class MyCountDownLatch extends CountDownLatch{
            private final Runnable runnable;
            
            public MyCountDownLatch(int count,Runnable runnable){
                super(count);
                this.runnable = runnable;
            }
            
            public void countDown(){
                super.countDown();
                if(getCount == 0){
                    this.runnable.run();
                }
            }
        }
    

4.1.3 Exchanger

  • 两个线程之间的数据信息传输
  • 用于两个线程之间的数据信息交换,只成对的使用,并且会阻塞到两个线程同时达到exchange()方法执行,否则会有线程一直处于blocked状态

    public static void main(String[] args){
        final Exchanger<String> exchanger = new Exchanger<String>();
        
        new Thread(new Runnable(){
            @Override
            public void run(){
                System.out.println(Thread.currentThread().getName() + "start...");
                try{
                    String result = exchanger.exchange("From A",5,TimeUnit.SECONDS);
                    System.out.println(Thread.currentThread().getName() + result);
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "finish");
            }
        },"Thread A").start();
        
        new Thread(new Runnable(){
            @Override
            public void run(){
                System.out.println(Thread.currentThread().getName() + "start...");
                try{
                    String result = exchanger.exchange("From B");
                    System.out.println(Thread.currentThread().getName() + result);
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "finish");
            }
        },"Thread B").start();
    }
  • 线程之间发送和接收的数据是同一内存地址的数据,有线程安全问题,所以数据使用Atomic来封装

    public static void main(String[] args){
        final Exchanger<Integer> exchanger = new Exchanger<Integer>();
        
        new Thread(){
            @Override
            public void run(){
                AtomicReference<Integer> value = new AtomicReference<>(1);
                try{
                    while(true){
                        value.set(exchanger.exchange(value.get()));
                        System.out.println(Thread.currentThread().getName() + value.get());
                        TimeUnit.SECONDS.sleep(2);
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }
        }.start();
        
        new Thread(){
            @Override
            public void run(){
                AtomicReference<Integer> value = new AtomicReference<>(2);
                try{
                    while(true){
                        value.set(exchanger.exchange(value.get()));
                        System.out.println(Thread.currentThread().getName() + value.get());
                        TimeUnit.SECONDS.sleep(3);
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }
        }.start();
    }

4.1.4 Semaphore

  • 可以同时控制进入同步的线程数,自定义锁不止同时进入一个线程
  • 就是维护了一堆的许可证,只有拿到剩余的许可证时才能通过,否则访问的话就会被blocked住
  • 案例一:使用Semaphore来制作自定义Lock

    • Semaphore自定义锁和Synchronized关键字区别

      • 在lock()阻塞过程中,synchronized每次只能通过一个线程,而Semaphore可以通过构造方法指定同时拿到锁的线程数
public static void main(String[] args){
    final SemaphoreLock lock = new SemaphoreLock(2);
    
    for(int i=0;i < 3;i++){
        new Thread(){
            @Override
            public void run(){
                try{
                    lock.lock();
                    System.out.println(Thread.currentThread().getName() + "- working");
                    TimeUnit.SECONDS(1).sleep();
                }catch(InterruptedException e){
                    e.printStackTrace();
                }finally{
                    lock.unlock();
                }
            }
        }.start();
    }
}
-----------------------------------------------------------
    public class SemaphoreLock{
        private final Semaphore semaphore;
        
        public SemaphoreLock(int number){
            semaphore = new SemaphoreLock(number);
        }
        
        public void lock(){
            semaphore.acquire();
        }
        
        public void unlock(){
            semaphore.release();
        }
    }
  • 案例二:设置线程每次获取多个permit,当剩余permit数小于获取数时进入blocked状态

    • 当将new Semaphore构造方法改为2时,可以允许多个线程同时拿到锁,可以运用在控制多个固定的连接访问的情景
    • 当将acquire()方法的参数设置为2时,就表示每个线程的访问都会获取2个permit
    public static void main(String[] args){
        final Semaphore semaphore = new Semaphore(3);
        
        for(int i=0;i < 2;i++){
            new Thread(){
                @Override
                public void run(){
                    System.out.println(Thread.currentThread().getName() + "in");
                    try{
                        semaphore.acquire(2);
                        System.out.println(Thread.currentThread().getName() + "working");
                        TimeUnit.SECONDS.sleep(1);
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }finally{
                        semaphore.release(2);
                    }
                    System.out.println(Thread.currentThread().getName() + "out");
                }
            }
        }.start();
    }

4.1.5 ReentrantLock

  • 效率很高的自旋锁,显示锁
  • 显示锁和synchronized

    private static final Lock lock = new ReentrantLock();
    
    public static void main(String[] args){
        //...
    }
    
    public static void needLock(){
        try{
            lock.lock();
            System.out.println(Thread.currentThread().getName() + "get lock");
            TimeUnit.SECONDS.sleep(1);
        }catch(InterruptedException e){//此中断异常是sleep()方法的,而不是lock()方法
            e.printStackTrace();
        }finally{
            lock.unlock();
        }
    }
    
    public static void needLockBySyn(){
        synchronized(this){
            try{
                TimeUnit.SECONDS.sleep(1);
                System.out.println(Thread.currentThread().getName() + "into syn");
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
    }
  • Lock接口中的API

    • lock():Lock接口中的lock()方法进入阻塞时是不能被interrupt的----synchronized关键字就是不能被打断的
    • lockInterruptibly():此lock方法是可以被打断的
    public static void main(String[] args){
        Thread t1 = new Thread(() -> lockInterruptibly());
        t1.start();
        
        TimeUnit.SECONDS.sleep(1);
        
        Thread t2 = new Thread(() -> lockInterruptiblu());
        t2.start();
        
        TimeUnit.SECONDS.sleep(1);
        t2.interrupt();
        System.out.println("===========");
    }
    
    public static void lockInterrupibly(){
        try{
            lock.interruptibly();
            System.out.println("lock");
            while(true){
                //empty...
            }
        }catch(InterruptedException e){
            e.printStackTrace();
        }finally{
            lock.unlock()
        }
    }
    • tryLock():此方法会立即返回是否拿到锁,可以对其结果判断来做出没有拿到锁后的操作
    public static void testTryLock(){
        try{
            if(!lock.tryLock()){
                return;
            }
            System.out.println(Thread.currentThread().getName() + "get lock");
            while(true){
                //empty...
            }
        }finally{
            lock.unlock();
        }
    }
  • 实现类ReentrantLock的API

    • getQueueLength():获取在waiting队列中的线程数
    • hasQueueThreads():waiting队列中是否存在线程
    • getHoldCount():查看当前线程获取锁的个数

4.1.6 ReadWriteLock

  • 读写分离锁,但是读悲观,读的过程写操作不能抢锁
  • 读写分离锁,在只有读的操作时是并行操作,在有写的操作时为串行,分为读锁和写锁

    private final static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);
    
    private final static Lock readLock = readWriteLock.readLock();
    private final static Lock writeLock = readWriteLock.writeLock();
    
    private final static List<Long> data = new ArrayList<>();
    
    public static void main(String[] args){
        Thread t1 = new Thread(() -> read(),"readLock");
        t1.start();
        
        TimeUnit.SECONDS.sleep(1);
        
        Thread t2 = new Thread(() -> write(),"writeLock");
        t2.start();
    }
    
    public void read(){
        try{
            readLock.lock();
            data.forEach(System.out::println);
            TimeUnit.SECONDS.sleep(5);
            System.out.println(Thread.currentThread().getName() + "finished...");
        }catch(InterruptedException e){
            e.printStackTrace();
        }finally{
            readLock.unlock();
        }
    }
    
    public void write(){
        try{
            writeLock.write();
            data.add(System.currentTimeMillis());
            TimeUnit.SECONDS.sleep(5);
            System.out.println(Thread.currentThread().getName() + "finished...");
        }catch(InterruptedException e){
            e.printStackTrace();
        }finally{
            writeLock.unlock();
        }
    }

4.1.7 Condition

  • 使用显示锁Lock时候的wait-notify机制
  • 当使用synchronized来同步时,使用当前的monitor对象来进行wait-notifyAll进行线程间的通信,当使用显示锁Lock时,则通过Lock构造出来的Condition对象进行线程间的通信

    private final static ReentrantLock lock = new ReentrantLock();
    private final static Condition condition = lock.newCondition();
    prviate static int data = 0;
    private static volatile boolean noUse = true;
    
    public static void produce(){
        try{
            lock.lock();
            while(noUse){
                condition.await();
            }
            
            data++;
            Optional.of("P:" + data).ifPresent(System.out::println);
            TimeUnit.SECONDS.sleep(1);
            noUse = true;
            condition.signalAll();
        }catch(IterruptedException e){
            e.printStackTrace();
        }finally{
            lock.unlock();
        }
    }
    
    public static void consumer(){
        try{
            lock.lock();
            while(!noUse){
                condition.await();
            }
            
            Optional.of("C:" + data).ifPresent(System.out::println);
            TimeUnit.SECONDS.sleep(1);
            noUse = false;
            
            condition.signalAll();
        }catch(InterruptedException e){
            e.printStackTrace();
        }finally{
            lock.unlock();
        }
    } 
  • 其中Condition#await()方法和synchronized中wait()一样,在wait时会释放锁,并且Condition#Await()必须和lock()一起使用,若没有lock()就会没有存储blocked对象的monitor

    private static void produce(){
        synchronized(MONITOR){
            while(noUser){
                try{
                    MONITOR.wait();
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
            }
            
            data++;
            System.out.println("P:" + data);
            noUse = true;
            MONITOR.notifyAll();
        }
    }

4.1.8 StampedLock

  • 可以在ReadWriteLock基础上实现乐观读操作
  • 可以替代ReentrantLock和ReadWriteLock

    • 但是它并没有继承Lock接口,使用的是LockSupport
    • 它可以使得读写锁优先写操作
  • 在StampedLock中对于读操作有一个乐观的机制,一般来说在拿到读锁的时候,写操作是blocked住的,这对于读是悲观的,而在StampedLock中允许在读的过程中去进行写操作,并使用戳来记录是否发生改变,若读的过程中发生改变则通过CAS进行回滚
  • 在ReadWriteLock中write锁被抢到的时候,read锁也是无法获取的,只有进行read操作时才允许并行,并write锁也是无法获取的,而在StampedLock中,read锁被获取的时候,write锁也是可以被获取的
  • 案例一:悲观读---在读的过程中只能拿到读锁,不能拿到写锁

    private final static StampedLock lock = new StampedLock();
    private final static List<Long> DATA = new ArrayList<>();
    
    public static void read(){
        long stamp = -1;
        try{
            stamp = lock.readLock();
            System.out.println(Thread.currentThread().getName() + DATA);
            TimeUnit.SECONDS.sleep(1)
        }catch(InterruptedException e){
            e.printStackTrace();
        }finally{
            lock.unlockRead();
        }
    }
    
    public static void write(){
        long stamp = -1;
        try{
            stamp = lock.writeLock();
            DATA.add(System.currentTimeMillis());
            TimeUnit.SECONDS.sleep(1);
        }catch(InterruptedException e){
            e.printStackTrace();
        }finally{
            lock.unlockWrite();
        }
    }
  • 案例二:乐观读---使用StampedLock来实现乐观读,在读的过程中其他线程也可以拿到写锁

    public static void read(){
        long stamp = lock.tryOptimisticRead();
        if(lock.validate(stamp)){
            try{
                stamp = lock.readLock();
                System.err.println(stamp + "-" + DATA);
                TimeUnit.SECONDS.sleep(1);
            }catch(InterruptedException e){
                e.printStackTrace();
            }finally{
                lock.unlockRead(stamp);
            }
        }
    }

4.1.9 ForkJoin

  • 分而治之,把线程的任务递归到子线程去完成
  • 一个人完成不了就把任务递归分解给多个人完成,一个线程完成不了就递归分解给多个任务
  • 案例:使用forkJoin完成多个数字累加,如果数字个数大于设定值,就向下递归

    • 有返回值---RecursiveTask

      private final static int MAX = 3;
      
      public static void main(String[] args){
          final ForkJoinPool pool = new ForkJoinPool();
          ForkJoinTask<Integert> future = pool.submit(new CalculatedRecursiveTask(0,10));
          try{
              Integer result = future.get();
              System.out.println(result);
          }catch(InterruptedException e){
              e.printStackTrace()
          }catch(ExecutionException e){
              e.printStackTrace();
          }
      }
      ----------------------------------------------------
          
          public class CalCulatedRecursiveTask extends RecursiveTask<Integet>{
              private final int start;
              private final int end;
              
              CalculatedRecursiveTask(int start,int end){
                  this.start = start;
                  this.end = end;
              }
              
              @Override
              protected Integer compute(){
                  if(end - start <= MAX){
                      return intStream.rangeClosed(start,end).sum();
                  }else{
                      int mid = (start + end) / 2;
                      CalculatedRecursiveTask leftTask = new CalculatedRecursiveTask(start,mid);
                      CalculatedRecursiveTask rightTask = new CalculatedRecursiveTask(mid+1,end);
                      
                      leftTask.fork();
                      rightTask.fork();
                      
                      return leftTask.join() + rightTask.join();
                  }
              }
          }
    • 无返回值---RecursiveAction:其中要获取数据需等待其计算完才能获取

      private final static int MAX = 3;
      private final static AtomicInteger sum = new AtomicInteger(0);
      
      public static void main(String[] args){
          final ForkJoinPool pool = new ForkJoinPool();
          pool.submit(new CalculatedRecursiveAction(0,10,sum));
          
          pool.awaitTermination(10,TimeUnit.SECONDS);
          Optional.of(SUM).ifPresent(System.out::println);
      }
      -------------------------------------------------------------
          
          public class CalculatedRecursiveAction extemds RecursiveAction<Integer>{
              private final int start;
              private final int end;
              private final static AtomicInteger sum;
              
              public CalculatedRecursiveAction(int start,int end){
                  this.start = start;
                  this.end = end;
                  this.sum = sum;
              }
               
              @Override
              protected void compute(){
                  if((end - start) < MAX){
                      sum.addAndGet(IntStream.rangeClosed(start,end).sum());
                  }else{
                      int mid = (start + end) / 2;
                      CalculatedRecursiveTask leftTask = new CalculatedRecursiveAction(start,mid);
                      CalculatedRecursiveTask rightTask = new CalculatedRecursiveAction(mid+1,end);
                      
                      leftTask.fork();
                      rightTask.fork();
                  }
              }
          }

4.1.10 Phaser

  • CountDownLatch和Cyclic Barrier的加强版本,是一个阶段器,能进行reset和动态操作parties
  • Phaser拥有CountDownLatch和CyclicBarrier的功能,其中CountDownLatch数据是不能重复使用的,而CyclicBarrier是可以reset复位的,但是CyclicBarrier中的parties是不能改变的,而Phaser是可以动态变化的,其中arriveAndAwaitOnAdvance()是要等待所有注册的parties完成任务
  • 案例一:类似同步屏障CyclicBarrier#await()功能,并且动态注册parties

    private final static Random random = new Random(System.currentTimeMillis());
    
    public static void main(String[] args){
        final Phaser phaser = new Phaser();
        
        IntStream.rangeClosed(1,5).map(i -> phaser).forEach(Task::new);
        
        phaser.register();
        phaser.arriveAndAwaitAdvance();
        System.out.println("working finished...")
    }
    --------------------------------------------------------
        
        public class Task extends Thread{
            private final Phaser phaser;
            
            public Task(Phaser phaser){
                this.phaser = phaser;
                this.phaser.register();
                start();
            }
            
            @Override
            public void run(){
                System.out.println(Thread.currentThread().getName() + ":working");
                try{
                    TimeUnit.SECONDS.sleep(random.next(2));
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
                
                phaser.arriveAndAwaitAdvance();
            }
        }
  • 案例二:所有线程完成一个阶段,等待所有线程完成后再开始下一个阶段

    • 其中phaser中计数是递增的,而CountDownLatch是递减的
    public static void main(String[] args){
        final Phaser phaser = new Phaser(5);
        
        for(int i=1;i < 6;i++){
            new Athletes(i,phaser).start();
        }
    }
    ----------------------------------------------------------
        
        public class Athletes extends Thread{
            private final Phaser phaser;
            private final int number;
            
            public Athletes(Phaser phaser,int number){
                this.phaser = phaser;
                this.number = number;
            }
            
            @Override
            public void run(){
                try{
                    System.out.println(number + ": start 1");
                    TimeUnit.SECONDS.sleep(3);
                    System.out.println(number + ":end 1");
                    
                    phaser.arriveAndAwaitAdvance();
                    
                    System.out.println(number + ": start 2");
                    TimeUnit.SECONDS.sleep(3);
                    System.out.println(number + ":end 2");
                    
                    phaser.arriveAndAwaitAdvance();
                    
                    System.out.println(number + ": start 3");
                    TimeUnit.SECONDS.sleep(3);
                    System.out.println(number + ":end 3");
                    
                    phaser.arriveAndAwaitAdvance();
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
            }
        }
  • 案例三:当运动员完成到第二项后,第三项有人受伤不能触发arriveAndAwaitAdvance(),那么所有的线程都会等待这个线程完成,这样的话就会造成阻塞,所以要将这个有问题的线程异常注销掉#arriveAndDeregister()

    • 动态增加或减少参与者
    public static void main(String[] args){
        final Phaser phaser = new Phaser(5);
        
        for(int i=1;i < 5;i++){
            new Athletes(i,phaser).start();
        }
        
        new injuredAthletes(6,phaser).start();
    }
    ----------------------------------------------------------
        
    public class injuredAthletes extends Thread{
        private final Phaser phaser;
        
        injuredAthletes(int number,Phaser phaser){
            this.number = number;
            this.phaser = phaser;
        }
        
        @Override
        public void run(){
            try{
                System.out.println(number + ": start 1");
                    TimeUnit.SECONDS.sleep(3);
                    System.out.println(number + ":end 1");
                    
                    phaser.arriveAndAwaitAdvance();
                    
                    System.out.println(number + ": start 2");
                    TimeUnit.SECONDS.sleep(3);
                    System.out.println(number + ":end 2");
                    
                    phaser.arriveAndAwaitAdvance();
                    
                    System.out.println(number + ": Injured 3");
                    phaser.arriveAndDeregister();
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
    }
  • Phaser的API

    • 构造方法:Phaser phaser = new Phaser(int n):创建一个注册数为n的Phaser
    • register():注册party
    • arriveAndAwaitAdvance():arrive并等待其他parties完成
    • arriveAndDeregister():arrive并注销当前party
    • phase():获取当前Phaser处于的阶段
    • getRegisteredParties():获取总的注册数
    • getArriveParties():获取已arrive的parties数
    • getUnarrivedParties():获取还未arrive的parties数
    • bulkRegister(int n):批量注册
    • onadvice():

      • 此方法每个阶段执行完都会调用一次,一般都会进行重写
      • 此方法的返回值是true时Phaser会被终止,可以通过设置此值来终止线程
    • arrive():此方法只会通知已经到达,但是不会由于其他线程还未到达而被阻塞住
    • 案例四:子线程使用arrive()方法来进行任务,主线程使用arriveAndAwaitOnAdvance来记录所有线程完成第一阶段的时机

      public static void main(String[] args){
          final Phaser phaser = new Phaser(5);
          
          for(int i=0;i < 4;i++){
              new ArriveTask(phaser).start();
          }
          
          phaser.arriveAndAwaitAdvance();
          System.out.println("the first phase of all works were finished...")
      }
      -------------------------------------------------
          
          public class ArriveTask extends Thread{
              private final Phaser phaser;
              
              public ArriveTask(Phaser phaser,int number){
                  super(String.valueOf(number));
                  this.phaser = phaser;
              }
              
              @Override
              public void run(){
                  try{
                      System.out.println(getName() + "begin...");
                      TimeUnit.SECONDS.sleep(2);
                      System.out.println(getName() + "running...");
                      phaser.arrive();
                  
                      TimeUnit.SECONDS.sleep(3);
                      System.out.println(getName() + "other work");
                  }catch(InterruptedException e){
                      e.printStackTrace();
                  }
              }
          }
    • AwaitAdvance(int phase):类似CountDownLatch,调用AwaitAdvance(int phase)方法的线程同样进入阻塞(其中phase为当前的阶段数),但是不会计入parties,只有当parties达到指定数量时,阻塞才会解除

      public static void main(String[] args){
          final Phaser phaser = new Phaser(6);
          
          IntStream.rangeClosed(0,5).boxed().map(i -> phaser)
              .forEach(AwaitAdvanceTask::new);
          
          phaser.AwaitAdvance(phaser.getPhase());
          System.out.println("all work were finished...");
      }
      -----------------------------------------------
          
          public class AwaitAdvanceTask extends Thread{
              private final Phaser phaser;
              
              public AwaitAdvanceTask(Phaser phaser){
                  this.phaser = phaser;
                  start();
              }
              
              @Override
              public void run(){
                  try{
                      TimeUnit.SECONDS.sleep(2);
                  }catch(InterruptedException e){
                      e.printStackTrace();
                  }
                  
                  phaser.arriveAndAwaitAdvance();
                  System.out.println(getName() + "finished work...");
              }
          }
    • forceTermination():强制退出阻塞

      public static void main(String[] args){
          final Phaser phaser = new Phaser(3);
          
          new Thread(phaser::arriveAndAwaitAdvance).start();
          
          TimeUnit.SECONDS.sleep(3);
          
          System.out.println(phaser.isTerminated());
          
          phaser.forceTermination();
          System.out.println(phaser.isTerminated());
      }

4.3 Executor框架

4.3.1 Executor

  • Executor:顶层接口

    • Executor ---> ExecutorService ---> AbstractExecutorService ---> ThreadPoolExecutor/ForkJoinPool
  • ThreadPoolExecutor构造方法的参数

    1. corePoolSize:线程池中核心线程数的最大值
    2. maximunPoolSize:线程池中能拥有的最多线程
    3. keepAliveTime:表示空闲线程的存活时间

      1. 如果没有空闲的线程执行该任务,且当前运行的线程数小于corePoolSize,则添加新的线程执行该任务
      2. 如果没有空闲的线程执行该任务,且当前的线程数等于corePoolSize,同时阻塞队列未满,则将任务加入队列,而不添加新的线程
      3. 如果没有空闲的线程执行该任务,且当前阻塞队列已满,同时线程数小于maximunPoolSize,则创建新的线程执行任务
      4. 如果没有空闲的线程执行该任务,且当前阻塞队列已满,同时线程池中的线程数等于maximunPoolSize,则根据构造函数中的handler指定的策略来拒绝新的任务
    4. timeUnit:表示KeepAliveTime的单位
    5. blockingQueue:用于缓存任务的阻塞队列
    6. ThreadFactory:指定创建线程的工厂
    7. handler:表示当workQueue已满,且线程池中的线程数达到maximunPoolSize时,线程池拒绝新任务时采取的策略
    public static void main(String[] args){
        ThreadPoolExecutor pool = (ThreadPoolExecutor)buildThreadPooExecutor();
        
        int activeCount = -1;
        int queueSize = -1;
        
        while(true){
            if(activeCount != pool.getActiveCount() || queueSize != pool.getQueue().size()){
                System.out.println(pool.getActiveCount());
                System.out.println(pool.getQueue().size());
                System.out.println(pool.getCorePoolSize());
                System.out.println(pool.getMaximunPoolSize);
            }
        }
    }
    
    private static ExecutorService buildThreadPoolExecutor(){
        ExecutorService pool = new ThreadPoolExecutor(1,2,30,
            TimeUnit.SECONDS,new ArrayBlockingQueue<>(1),r -> {
            Thread t = new Thread(r);
            return t;
            },ThreadPoolExecutor.AbortPolicy());
        
        System.out.println("The ThreadPoolExecutor create Done");
        pool.execute(() -> sleepSeconds(100));
        pool.execute(() -> sleepSeconds(10));
        pool.execute(() -> sleepSeconds(10));
        
        return pool;
    }
    
    private static void sleepSeconds(long seconds){
        try{
            System.out.println(Thread.currentThread().getName() +"working...");
            TimeUnit.SECONDS.sleep(seconds);
        }catch(InterruptedException e){
            e.printStackTrace();
        }
    }
  • ThreadPoolExecutor的关闭

    • 线程池开启后执行完任务是不会自动结束的

      1. shutdown():将线程池状态置为SHUTDOWN状态,但并不会立即停止

        1. 停止接收外部submit的任务
        2. 线程池内部正在跑的任务和队列里等待的任务,会继续执行完
        3. 待第二步完成后,才真正停止
      2. shutdownNow():将线程池状态设置为STOP,企图立即停止,但事实上不一定

        1. 跟shutdown()方法一样,先停止接收外部提交的任务
        2. 忽略队列中等待的任务
        3. 调用interrupt()方法尝试中断正在执行的任务
        4. 待以上任务全部结束后,才真正停止,并返回执行的任务列表
        • 它试图终止线程的方法是通过调用Thread,interrupt()方法来实现的,但是大家都知道,这种方法的作用有限,如果线程中没有sleep,wait,Condition,定时锁等应用,interrupt()方法是无法终止当前的线程的.
      3. awaitTermination(long timeout,TimeUnit unit):当前线程阻塞,直到

        1. 等待所有已提交的任务(包括正在跑和队列中等待的)执行完
        2. 或者等待超时时间到
        3. 或者线程被中断,抛出interruptedException
        • 返回ture(shutdown请求后所有任务执行完毕)或false(已超时)
      • 其中awaitTermination()方法是阻塞的,但不关闭添加新任务的通道,而shutdown是非阻塞的,但是会关闭添加新任务的通道,所以两者一般一起使用,来阻塞主线程达到所有子线程结束后再执行之后的操作
    • 但是如果线程中没有阻塞,那么interrupt()也不能中断线程的流程,所以这时候可以使用在构造方法的ThreadFactory对象中将线程设置为Daemon线程

      • 其中ThreadFactory中的r即为submit()方法中注入的callable接口实现类
      public static void main(String[] args){
          ExecutorService pool = new ThreadPoolExecutor(10,20,30,
              TimeUnit.SECONDS,new ArrayBlockingQueue<>(10),r -> {
              Thread t = new Thread(r);
              t.setDaemon(true);
              return t;
          },ThreadPoolExecutor.AbortPolicy());
          
          IntStream.range(0,10).boxed().forEach(i -> pool.submit(() -> {
              while(true){
                  //empty...
              }
          }));
          
          pool.shutdown();
          pool.awaitTermination(5,TimeUnit.SECONDS);
          
          System.out.println("finished...")
      } 

4.3.2 Executors

  • 线程池的工厂类
  • 此类相当于线程池的工厂类,通过此类根据提供不同的构造方法参数来构造的ThreadPoolExecutor不同的实例对象
  1. newCacheThreadPool

    • 官方注释:此线程池对于执行一些生命周期很短的异步任务效率很高,不适合执行长时间任务

      public static Executor newCacheThreadPool(){
          reuturn new ThreadPoolExecutor(0,Integer.MAX_VALUE,
                                  60L,TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
      }
      1. 此线程中核心线程数为0,最大线程数为整数最大值
      2. 此线程池中的队列workQueue是同步队列,只能存放一个元素
      3. 此线程池最大空闲存活时间为60s
      4. 此线程在任务结束后,超过空闲时间就会回收所有的线程,然后自动关闭线程池
      5. 所以此线程一般不会触发拒绝策略,但是会大量的创建和销毁线程
public static void main(String[] args){
    ExecutorService executorService = Executors.newCacheThreadPool();
    System.out.println((ThreadPoolExecutor)executorService.getActiveCount());
    
    IntStream,range(0,100).boxed().forEach(i -> executorService.execute(() -> {
        try{
            TimeUnit.SECODS.sleep(10);
        }catch(InterruptedException e){
            e.printStackTrace();
        }
    }));
    System.out.println((ThreadPoolExecutor)executorService.getActiveCount());
}
  • 此线程池执行100个线程的任务时,若任务时间比较长,那么就会创建因为核心线程数为0,而workQueue只为1,所以会一直创建新的线程来执行任务,直到超过最大空闲时间会将所有超过核心线程数的线程全部回收,而核心线程数为0,所以所有创建的线程都会被回收,并且会自动的关闭线程池----所以会大量的创建和销毁线程
  1. newFixedThreadPool

    public static ExecutorService newFixedThreadPool(int nThreads){
        return new ThreadPoolExecutor(nThreads,nThreads,0L,TimeUnit.MILLISSECONDS,
                                     new LinkedBlockingQueue<Runnable>());
        
    }
    1. 此线程池的核心线程数和最大线程数都为构造方法的入参,所以当workQueue满了后再添加任务,就会直接执行拒绝策略
    2. 此线程池中的队列workQueue的最大值为最大整数
    3. 此线程的最大等待时间为0,但由于核心线程数等于最大线程数,所以不会销毁线程,并且也不会创建新的线程
    4. 所以此线程池一般不会触发拒绝策略,并且不会创建和销毁线程
  1. newSingleThreadExecutor

    • 此线程池等价于newFixedThreadExecutor(1),且此线程池是ThreadPoolExecutor的封装代理对象,不能用ThreadPoolExecutor来接收,而仅是ExecutorService的实现
    • 此线程池内部封装使用的是ThreadPoolExecutor,但是其只暴露ExecutorService接口的方法

      1. 此线程池中核心线程数为1,最大线程数为1
      2. 此线程池中的队列workQueue存放的线程数量为最大整数
      3. 此线程池的最大等待时间为0,但不会销毁线程,并且也不会创建新的线程,因为核心线程数和最大线程数相等
      4. 此线程池一般不会触发拒绝策略,并且不会创建和销毁线程
public static ExecutorService newSingleThreadExecutor(){
    return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1,1,OL,
                     new LinkedBlockingQueue<Runnable>()));
}
  • 单例线程池和一个线程的区别

    1. 单个线程会在任务完成时被销毁,而单例线程池会一直存活
    2. 单个线程不能提交runnable到缓存队列,而单例线程池可以
  1. newWorkStealingPool---1.8以后

    • 此线程池实现了ForkJoinPool,而ForkJoinPool实现了AbstractExecutorService

      1. 此线程池和newCacheThreadPool一样会自动结束,而newFixedThreadPool和newSingleThreadExecutor不会自动结束
      2. 此线程池根据电脑的核数来创建并行线程
      public static ExecutorService newWorkStealingPool(){
          return new ForkJoinPool(Runtime.getRuntime().availableProcessors(),
                ForkJoinPool.defaultForkJoinWorkerThreadFactory,null,true);
      }
    • ForkJoin#execute()/submit()

      • execute():异步执行task,无返回值
      • submit():异步执行,且带Task有返回值,可以通过task.get实现同步到主线程
      • invoke(ForkJoinTask):有join,Task会被同步到主线程,一直阻塞到任务执行完成返回
      • 当方法参数为Runnnable接口实现时,会将其封装为RunnableExecuteAction extends ForkJoinTask
      public static void main(String[] args){
          ExecutorService executorService = Executors.newWorkStralingPool();
          
          List<Callable<String>> callableList = IntStream.range(0,20)boxed().map()i -> (Callable<String> () -> {
              System.out.println(Thread.currentThread().getName() + "working...");
              sleep(100);
              return "Task-" + i;
          }).collect(Collectors.toList());
          
          executorService.invokeAll(callableList);
      }

4.3.3 ExecutorService

  • 父接口
  1. 使用模板设计模式来获取多线程运行过程中的异常

    • 通过捕获异常来获取Runnable中的返回信息,因为runnable中是没返回值的
    public static void main(String[] args){
        ExecutorService executorService = Executors.newFixedThreadPool(10,new MyThreadFactory());
        
        IntStream.rangeClosed(1,10).forEach(i -> executorService.execute(new MyTask(i){
            @Override
            public void init(){
                System.out.println(i + ":do some init...");
            }
            
            @Override
            public void done(){
                System.out.println(i + ":do some works...");
            }
            
            @Override
            public void error(Throwable e){
                System.out.println(i + ":error...");
            }
        }));
    }
    -------------------------------------------------------------
        
    public class MyThreadFactory implement ThreadFatory{
        private final static AtomicInteger i = new AtomicInteger();
        
        @Override
        public Thread newThread(Runnable r){
            Thread t = new Thread(r);
            t.setName("My-Thread" + i);
            t.setUncaughtExceptionHandler((t,cause) -> {
                System.out.println(t.getName() + "execute failed...");
                cause.printStackTrace();
            });
            return t;
        }
    }
    -------------------------------------------------------------
        
    public abstract class MyTask implement Runnable{
        private final int i;
        
        private MyTask(int i){
            this.i = i;
        }
        
        @Override
        public void run(){
            try{
                this.init();
                this.execute();
            }catch(InterruptedException e){
                this.error(e);
            }
        }
        
        protected abstract void init();
        
        protected abstract void execute();
        
        protected abstract void error(Throwable e);
    }
    
  1. ExecutorService的API

    1. ThreadPoolExecutor的拒绝策略

      1. AbortPolicy():对拒绝任务做抛弃处理,并抛出异常
      2. DiscardPolicy():对拒绝任务做直接抛弃处理,没有异常信息---不建议使用
      3. CallerRunsPolicy():直接在execute()方法的调用线程中运行被拒绝的任务,如果执行程序已关闭,则会丢弃任务
      4. DiscardOldestPolicy():当任务被拒绝添加时,会抛弃任务队列中醉旧的任务也就是最先加入到队列中的任务,再把这个新任务添加进去
  1. 线程回收:对于newFixedThreadPool和newSingleThreadPool是不会被回收的,因为core线程都大于0,如果要想线程池执行完任务后自动回收,就要设置两个属性

    1. setKeepAliveTime(10):设置其最大空闲时间不为0
    2. allowCoreThreadTimeOut(true):设置允许核心线程回收
  1. 清除队列中的任务

    • remove(Runnable r)

      public static void main(String[] args){
          ThreadPoolExecutor pool = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
          pool.setKeepAliveTime(10,TimeUnit.SECONDS);
          pool.setCoreThreadTimeOut(ture);
          
          IntStream.rangeClosed(1,2).boxed().forEach(i -> pool.execute(() -> {
              try{
                  TimeUnit.SECONDS.sleep(5);
                  System.out.println("finish");
              }catch(InterruptedException e){
                  e.printStackTrace();
              }
          }));
          
          TimeUnit.MILLISECONDS.sleep(20);
          Runnable r = () -> {
              System.out.println("new task");
          };
          pool.execute(r);
          TimeUnit.MILLISECONDS.sleep(20);
          pool.remove(r);
      }
  1. 预先创建线程

    • 在没添加任务时,core线程为0,但是没有任务也可以预先创建线程,但是只能创建一个预先线程,只有添加任务才能预先创建下一个

      1. preStartCoreThread():预先创建一个线程
      2. getActiveCount():获取正在执行的线程数
      3. preStartAllCoreThread():预先创建core数的线程数,但是实际上只能是task + 1
  1. invoke方法---阻塞方法

    • invokeAny(Collection<T> collection,TimeUnit unit):批量执行任务获取其中最快的一个结果,当设置超时时间时,若超时时间到就会停止线程,并抛出异常,返回值为T

      • 当其中某一个线程执行完成就会得到返回值,其余的线程将不会继续执行
    • invokeAll(Collection<T> collection,TimeUnit unit):批量执行线程任务获取结果集,当设置超时时间时,若超时时间到就会停止线程,并抛出异常,返回值为List<Future<T>>
  1. submit(Runnable/Callable r):参数是Runnable的submit()方法是没有返回值的,只能当做阻塞来使用,返回值future.get()需要在线程任务完成后才能向下执行

    public static void main(String[] args){
        ExecutorService pool = Executors.newFixedThreadPool(10);
        Future<?> future = pool.submit(() -> {
            try{
                TimeUnit.SECONDS.sleep(5);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        });
        Object NULL = future.get();
        System.out.println(NULL);
    }
  2. submit()和execute()的区别

    1. submit()和execute()都属于线程池的方法,execute只能提交Runnable类型的任务,而submit既能提交Runnable类型的任务也能提交Callable类型的任务
    2. execute()会直接抛出任务执行的异常,而submit()会吃掉异常,可以通过Future的get方法将任务执行时的异常重新抛出
    3. execute()所属顶层接口是Executor,submit所属顶层接口是ExecutorService,实现类ThreadPoolExecutor重写了execute方法,抽象类AbstractExecutorService重写了submit方法

      <T> Future<T> submit(Callable<T> task);
      <T> Future<T> submit(Runnable task,T result);
      Future<?> submit(Runnable task);
      void execute(Runnable command);

4.3.4 ScheduledExecutorService

  • 定时器线程池
  • Scheduler Solution:定时任务的方法

    1. Timer/TimerTask
    2. Scheduler Executor Service
    3. crontab
    4. cron4j
    5. quartz
  • Timer

    • TimeTask就是Runnable接口的一个实现类

      • 缺点:若run()方法执行时长为10s,而设定的间隔为1s,那么最后间隔为 10s
      public static void main(String[] args){
          Timer timer = new Timer();
          final TimerTask task = new TimerTask(){
              @Override
              public void run(){
                  System.out.println(System.currentTimeMillis());
              }
          };
          timer.schedule(task,1000,1000);
      }
  • crontab linux

    • 能保证间隔性
  • quartz

    public static void main(String[] args){
        JobDetail job = JobBuilder.newJob(SimpleJob.class)
            .withIdentity("job1","group1").build();
        
        Trigger trigger = TriggerBuilder.newTrigger()
            .withIdentity("trigger1","group1").withSchedule("0/5 * * * * ?").build();
        
        Schedule schedule = new StdSchedulerFactory().getScheduler();
        
        scheduler.start();
        scheduler.schedule(job,trigger);
    }
    --------------------------------------------------------------
        
    public class SimpleJob implement Job{
        @Override
        public void execute(JobExecutionContext context){
            System.out.println(System.currentTimeMillis());
        }
    }
        
  • ScheduledThreadPoolExecutor

    • ExecutorService ---> ScheduledExecutorService/ThreadPoolExecutor ---> ScheduledThreadPoolExecutor
    • 构造方法

      public ScheduledThreadPoolExecutor(int corePoolSize){
          super(corePoolSize,Integer.MAX_VALUE,0,NAOSECONDS,new DelayedWorkQueue());
      }
    • ScheduledExecutorService接口API

      1. ScheduledFuture<?> schedule(Runnable var1,long var2,TimeUnit var3):延迟执行一个runnable接口任务,返回值Future可以用来cancel任务
      2. <V> ScheduledFuture<V> schedule(Runnable var1,long var2,TimeUnit var3):延迟执行一个callable接口任务,返回值Future可以用来获取任务结果
      3. ScheduledFuture<?> scheduleAtFixedRate(Runnable var1,long initialDelay,long period,TimeUnit var4):延迟initialDelay,按周期period执行任务,如果执行任务的时间time大于period,那么周期则为time
      4. ScheduledFuture<?> scheduleWithFixedDelay(Runnable var1,long initialDelay,longdelay,TimeUnit var4):延迟initialDelay,如果执行任务的时间time,按周期(period+time)执行任务
      public static void main(String[] args){
          ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(2);
          ScheduledFuture<?> future = executor.scheduleAtFixedRate(() -> System.out.println("i am running"),1,2,TimeUnit.SECONDS);
      }

4.3.5 Future和Callable

  • 结果集接口和有返回值的任务接口---Future模式

    • Future模式就是有返回值的多线程任务
  • Future的理念:执行某个工作能快速的返回,能够去执行其他的任务,然后等工作完成后再去拿结果,其中Future是由ThreadPoolExecutor#submit(Callable c)来获取的

    • 其中Future#get()方法是一个blocked方法,会一直阻塞到任务结束得到返回值
  • 案例一:开启一个线程打断main线程中的future#get()方法的阻塞,抛出interrupt异常

    public static void main(String[] args){
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        
        Future<Integer> future = executorService.submit(() -> {
            try{
                TimeUnit.SECONDS.sleep(10);
            } catch(InterruptedException e){
                e.printStackTrace();
            }
            return 10;
        });
        
        Thread mainThread = Thread.currentThread();
        newThread(() -> {
            try{
                TimeUnit.SECONDS.sleep(3);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            
            mainThread.interrupt();
        }).start();
        
        Integer result = future.get();
        System.out.println("result:" + result);
    }
  • 案例二:设置超时的future#get()方法,main线程被打断,但是其他任务线程仍然继续执行

    • get()方法打断的是main线程,而执行任务的多线程仍然会继续执行
    public static void main(String[] args){
        ExecutorService executorService = Executors.newCacheThreadPool();
        
        Future<Integer> future = executorService.submit(() -> {
            try{
                TimeUnit.SECONDS.sleep(10);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            return 10;
        });
        
        Integer result = future.get(5,TimeUnit.SECONDS);
        System.out.println(result);
    }
  • isDone():线程任务执行结束的标志:

    1. 当任务执行完成
    2. 抛出异常
    3. 任务被cancel
    public static void main(String[] args){
        ExecutorService executorService = Executors.newCacheThreadPool();
        
        Future<Integer> future = executorService.submit(() -> {
            throw new RunTimeExection();
        });
        
        try{
            integer result = future.get();
            System.out.println(result);
        }catch(Exception e){
            System.out.println("is done:" future.isDone());
        }
    }
  • cancel():cancel不能在任务完成后或已经cancel后成功

    • cancel()后任务仍然继续执行
    • 如果有很长的任务需要结束

      • 设置interrupted标志,使用cancel来interrupted线程
public static void main(String[] args){
    ExecutorService executorService = Executors.newCacheThreadPool();
    Future<Integer> future = executorService.submit(() -> {
        while(!Thread.interrupted()){
            //empty...
        }
        System.out.println("cancel...");
        return 10;
    });
    
    TimeUnit.MILLISECODNS.sleep(10);
    System.out.println(future.cancel(true));//true
    System.out.println(future.isDone());//true
    System.out.println(future.isCancelled());//true
}
  • 若没有interrupted判别式,自定义线程工厂来设置线程为Daemon

    public static void main(String[] args){
        ExecutorService executorService = Executors.newCacheThreadPool(new ThreadFactory(){
            @Override
            public Thread newThread(Runnable r){
                Thread t = new Thread(r);
                t.setDaemon(true);
                return t;
            }
        });
        
        Future<Integer> future = executorService.submit(() -> {
            while(true){
                //empty...
            }
            return 10;
        });
    }

4.3.6 CompletionService

  • 解决Future结果集等待的缺陷----ExecutorService的包装类
  • 在Future中获取返回结果,future#get()方法是阻塞的,所以虽然future可以不用管任务线程的执行去执行其他任务,但是如果要去获取任务的返回结果,使用future#get()方法时,那么还是会阻塞调用get()方法的线程

    • Future的缺点

      1. get()方法会进行阻塞
      2. 当执行任务集合时,获取的Future集合是无序的,在获取返回结果时,可能第一个获取的任务结果Future时间是最长的,那么只能等待第一个最长时间任务结束,才能获取接下来其他时间较短的任务结果,而不能做到立即获取集合中最先返回的结果
public static void main(String[] args){
    ExecutorService executorService = Executors.newFixedThreadPool(2);
    
    final List<Callable<Integer>> tasks = Arrays.asList(
        () -> {
            try{
                TimeUnit.SECONDS.sleep(10);
                System.out.println("10 is finished...");
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            return 10;
        },
        () -> {
            try{
                TimeUnit.SECONDS.sleep(20);
                System.out.println("20 is finished...");
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            return 20;
        }
    );
    
    List<Future<Integer>> futures = executorService.invokeAll(tasks);
    
    Integer v20 = future.get(1).get();
    System.out.println(v20);//花费20s

    Integer v10 = future.get(0).get();
    System.out.println(v10);//花费20s
}
  • CompletionService是任务完成了才放入queue中,CompletionService是ExecutorService的包装类

    • 当有任务完成时,就获取已完成的任务结果
    public static void main(String[] args){
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        
        List<Callable<Integer>> tasks = Arrays.asList(
            () -> {
                try{
                    TimeUnit.SECONDS.sleep(10);
                    System.out.println("10 is finished...");
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
                return 10
            },
            () -> {
                try{
                    TimeUnit.SECONDS.sleep(20);
                    System.out.println("20 is finished...");
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
                return 20;
            }
        );
        
        ExecutorCompletionService<Integer> completionService 
            = new ExecutorCompletionService<>(executorService);
        
        tasks.stream().forEach(task -> completionService.submit(task));
        
        Future<Integer> future;
        while((future = completionService.take()) != null){
            System.out.println(future.get());
        }
    }
  • 案例一:对比案例

    • 使用ThreadPoolExecutor中invokeAll运行多个任务来获取Future的结果
    public static void main(String[] args){
        final ExecutorService executorService = Executors.newFixedThreadPool(5);
        
        List<Runnable> tasks = IntStream.rangeClosed(1,5).boxed().map(i -> (Runnable)() -> {
            try{
                TimeUnit.SECONDS.sleep(i * 5 + 10);
                System.out.println(i + ":working...");
            }catch(InterruptedException e){
                e.pritnStackTrace();
            }
        }).collect(Collectors.toList());
        
        List<Future<?>> futures = new ArrayList<>();
        tasks.forEach(r -> futures.add(executorService.submit(r)));
        
        futures.get(4).get();
        futures.get(3).get();
        futures.get(2).get();
        futures.get(1).get();
        futures.get(0).get();
    }
    • 使用CompletionService来获取Future中的结果
    public static void main(String[] args){
        final ExecutorService executorService = Executors.newFixedThreadPool(5);
        
        List<Runnable> tasks = IntStream.rangeClosed(1,5).boxed().map(i -> (Runnable)() -> {
            try{
                TimeUnit.SECONDS.sleep(i * 5 + 10);
                System.out.println(i + ":working...");
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }).collect(Collectors.toList());
        
        ExecutorCompletionService completionService 
            = new ExecutorCompletionService(executorService);
        tasks.forEach(r -> completionService.sumbit(r));
        
        Future<?> future;
        while((future = completionService.take()) != null){
            System.out.println(future.get());
        }
    }
  • 案例二:若在执行任务的过程中,执行到一半想结束,并且想获取到所有未完成的任务

    1. 通过shutDownNow()方法来中断线程,并返回队列中所有的任务

      • 但是中断时可能有很多任务都在进行中,属于未完成,这些任务也需要返回,所以通过自定义Callable任务.设置一个success字段来记录任务是否完成来筛选未完成的任务
      public static void main(String[] args){
          final ExecutorService executorService 
              = Executors.newFixedThreadPool(5);
          List<Callable<Integer>> tasks = IntStream.rangeClosed(1,5)
              .boxed().map(newTask::new).collect(Collectors.toList());
          
          final ExecutorCompletionService<Integer> completionService = 
              new ExecutorCompletionService<>(executorService);
          tasks.forEach(c -> completionService.submit(c));
          TimeUnit.SECONDS.sleep(10);
          service.shutDownNow();
          
          tasks.stream().filter(c -> !((MyTask)c).isSuccess())
              .forEach(Sysmtem.out::println);
      }
      ------------------------------------------------------
      
      public class MyTask implements Callable<Integer>{
           private final Integer value;
          
          private boolean success = false;
          
          MyTask(integer value){
              this.value = value;
          }
          
          @Override
          public Integer call(){
              TimeUnit.SECNODS.sleep(value * 5 + 10);
              System.out.println(value + ":finished...");
              success = ture;
              return value;
          }
          
          public Boolean isSuccess(){
              return success;
          }
      }

4.3.7 CompletableFuture

  • 解决Future结果集等待和get()方法阻塞的两个缺陷,能够注册一个回调,不用主动去获取结果

    • Future和ForkJoinPool的结合
  • CompletableFuture时Future和ExecutorService---ForkJoinPool的结合体,并且会设置为main线程的守护线程

    • CompletionService解决了不能根据执行顺序获取返回结果的问题,但是还没有解决get()获取结果阻塞的问题,而CompletableFuture可以解决上述问题,不用主动获取结果,而是注册一个回调,调用结束后主动通知自己,并且不同阶段的任务能够进行级联调用
    public static void main(String[] args){
        CompletableFuture.runAsync(() -> {
            try{
                TimeUnit.SECONDS.sleep(10);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }).whenComplete((v,t) -> System.out.println("Done..."));
        
        System.out.println("not blocked...");
        
        Thread.currentThread().join();//CompletableFuture为main线程的守护线程
    }
  • 案例一:当有A,B两个任务时

    • Future是将所有任务的A任务执行完后,再开始拿着A(get)任务的结果集去执行B(display)任务
    public static void main(String[] args){
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        
        List<Callable<Integer>> tasks = IntStream.rangeClosed(1,10)
            .boxed().map(i -> (Callable<Integer>)() -> 
                         get()).collect(Collectors.toList());
        
        executorService.invokeAll(tasks).stream().map(future -> {
            try{
                return future.get();
            }catch(Exception e){
                e.printStackTrace();
            }
        }).forEach(i -> display(i));
       
    }
    
    public static Integer get(){
        int value = ThreadLocalRandom.current().nextInt(10);
        try{
            TimeUnit.SECONDS.sleep(value);
        }catch(InterrutedException e){
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "getting...");
        return value;
    }
    
    public static void display(Integer value){
        int value = ThreadLocalRandom.current().nextInt(10);
        try{
            TimeUnit.SECONDS.sleep(value);
        }catch(InterruptedException e){
            e.printStackTrace():
        }
        System.out.println(data + "displaying...");
    }
    • 而使用CompletableFuture则是A任务中完成的线程就继续执行B任务,不需要等待所有的A任务全部完成
    public static void main(String[] args){
        IntStream.range(0,10).boxed()
            .forEach(i -> CompletableFuture.supplyAsync(() -> get())
                     .thenAccept(() -> display())
                     .whenComplete((v,t) -> System.out.println(i + "Done...")));
        
        Thread.currentThread().join();
    }
  • 构建方法: 推荐使用静态方法替代new

    static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs):
        Return a new CompletableFuture that is comleted when all of the given CompletableFutures complete;
    static CompletatbleFuture<Object> anyOf(CompletableFuture<?>... cfs);
        Return a new CompletableFuture that is completed when any of the given CompletableFutures , with the same result;
    
    static <U> CompletableFuture<U> completedFuture(U value);
        Return a new CompletableFuture that is already completed with the given value;
    
    static CompletableFuture<Void> runAsync(Runnable runnable);
        Return a new CompletableFuture that is asynchronously completed by a task running in the ForkJoinPool.commonPool() after it runs the given action;
    static CompletableFuture<Void> runAsync(Runnable runnable,Executor executor);
        Return a new CompletableFuture that is asynchronously completed by a task running in the given executor after it runs the given action;
    
    static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier);
        Return a new CompletableFuture that is asynchronously completed by a task running in the ForkJoinPool.commonPool() with the value obtained by calling the given Supplier;
    static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier);
        Return a new CompletableFuture that is asynchronously completed by a task running in the given executor with the value obtained by calling the given Supplier;
    • completedFuture

      public static void main(String[] args){
          Future<Void> future = CompletableFuture
              .completedFuture("Hello").thenAccept(System.out::println);
          
          System.out.println(future.isDone());
          Thread.currentThread().join();
      }
    • runAsync

      public static void main(String[] args){
          Future<?> future = CompletableFuture.runAsync(() -> {
              try{
                  TimeUnit.SECONDS.sleep(5);
                  System.out.println("runAsync...");
              }catch(InterruptedException e){
                  e.printStackTrace();
              }
          }).whenComplete((v,t) -> System.out.println("=====over======"));
          
          System.out.println(future.isDone());
          Thread.currentThread().join();
      }
    • supplyAsync

      public static void main(String[] args){
          Future<Object> future = CompletableFuture.supplyAsync(Object::new)
              .thenAcceptAsync(obj -> {
                  try{
                      TimeUnit.SECONDS.sleep(5);
                      System.out.println("supplyAsync...1");
                  }catch(InterruptedException e){
                      e.printStackTrace();
                  }
              }).runAfterBoth(CompletableFuture.supplyAsync(() -> "Hello")
                              .thenAcceptAsync(s -> {
                                  try{
                                      TimeUnit.SECONDS.sleep(5);
                                      System.out.printlb("supplyAsync...2");
                                  }catch(InterruptedException e){
                                      e.printStackTrace();
                                  }
                              }),() -> System.out.println("===over==="));
          
          System.out.println(future.isDone());
          Thread.currentThread().join();
      }
      • whenCompleted(BiConsumer<? super T, ? super Throwable> action)方法为注册回调方法

NTFA
24 声望3 粉丝

自我约束,刻意练习


引用和评论

0 条评论