148、JAVA并发编程讲解2

 

1、ReentranLock的构造函数可以传入一个boolean参数,用来指定公平/非公平模式,默认是false非公平的。非公平的效率更高。

2、Lock的其他方法

      tryLock()  尝试获得锁,返回true/false

      tryLock(timeout,unit)   在给定的时间内尝试获得锁

      isFair()   是否为公平锁

      isLocked()  当前线程是否持有锁

      lock.getHoldCount()  持有锁的数量,只能在当前调用线程内部使用,不能再其他线程中使用。

3、ReentrantReadWriteLock

      读写锁,采用读写分离机制,高并发下读多写少时性能优于ReentrantLock

      读读共享,写写互斥,读写互斥。

4、四种线程池

      1、newCachedThreadPool具有缓存性质的线程池,

            线程最大空闲时间60s,线程可重复利用(缓存特性),

            没有最大线程数限制。任务耗时端,数量大。

      2、newFixedThreadPool具有固定数量的线程池,

           核心线程数等于最大线程数,

           线程最大空闲时间为0,

      3、newScheduledThreadPool  具有时间调度特性的线程池,必须初始化核心线程数,

            底层使用DelayedWorkQueue实现延迟特性。

      4、newSingleThreadExecutor  核心线程数与最大线程数均为1,用于不需要并发顺序执行。

四种线程池都是通过Executors类创建的,底层创建的都是ThreadPoolExecutor类,可以构建自己需要的线程类。

 

Future模式

JDK的Concurrent包提供了Future模式的实现,可以直接使用。

使用Future模式需要实现Callable接口,并使用FutureTask进行封装,使用线程池进行提交。

 

Master-Worker模式:

1、Master-Worker模式是一种将串行任务并行化的方案,被分解的子任务在系统中可以被并行处理,

同时,如果有需要,Master进程不需要等待所有的子任务都完成计算,就可以根据已有的部分结果集计算最终结果集。

2、客户端将所有任务提交给Master,Master分配Worker去并发处理任务,并将每一个任务的处理结果返回给Master,

所有的任务处理完毕后,由Master进行结果汇总再返回给Client。

 

 

 

 

 

高性能随机数

Random与ThreadLocalRandom在高并发场景下的性能差异和原理

1、Random存在性能缺陷,主要原因是要不断的计算新的种子,更新原种子,使用CAS方法。高并发的情况下会造成大量的线程

自旋,而只有一个线程会更新成功。

2、ThreadLocalRandom采用ThreadLocal的机制,每一个线程都是用自己的种子去进行计算下一个种子,规避CAS在并发下的问题。

 

高性能累加器LongAddr

1、AtomicLong存在性能瓶颈,由于使用CAS方法。高并发的情况下会造成大量的线程自旋,而只有一个线程会更新成功,

浪费CPU资源。

LongAdder的思想是将单一的原子变量拆分为多个变量,从而降低高并发下的资源争抢。

increment、longValue、LongAdder是LongAccumulator的特例,DoubleAdder是DoubleAccumulator的特例,

Accumulator的特点是可以设置初始值,自定义累加算法。

 

COWIterator的弱一致性

使用COW容器的iterator方法实际返回的是COWIterator实例,遍历的数据为快照数据,其他线程对于容器元素增加,

删除,修改不对快照产生影响。

对java.util.concurrent.CopyOnWriteArrayList、java.util.concurrent.CopyOnWriteArraySet均适用。

 

LockSupport

1、LockSupport的底层采用Unsafe类来实现,他是其他同步类的阻塞与唤醒的基础。

2、park与unpark需要成对使用,parkUntil与parkNanos可以单独使用。

3、先调用unpark再调用park会导致park失效。

4、线程中断interrupt会导致park失效并且不抛异常。

5、例如blocker可以对堆栈进行追踪,结合jstack进行使用。

 

AQS抽象同步队列

1、抽象队列同步器简称AQS,是同步器的基础组件,JUC种锁的底层实现均依赖于AQS,开发不需要使用。

2、采用FIFO的双向队列实现,队列元素为Node (静态内部类),Node内的thread变量用于存储进入队列的线程。

3、Node节点内部的Shared用来标记该线程是获取共享资源时被阻塞挂起后放入AQS队列的,

     EXCLUSIVE用来标记线程是获取独占资源时被挂起后放入AQS队列的。

     WaitStatus记录当前线程等待状态,可以为CANCELLED(线程被取消了)、SIGNAL(线程需要被唤醒)、

     CONDITION(线程在条件队列里面等待)、PROPAGATE(释放共享资源时需要通知其他节点);prev记录当前节点的前驱节点,next记录当前节点的后继节点。

 

下面案例说明线程在条件队列里面等待

package com.mkevin.demo8;

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

/**
 *  观察Condition的运行机制
 */
public class AQSDemo1 {

    static ReentrantLock rl = new ReentrantLock();
    static Condition condition = rl.newCondition();

    public static void main(String[] args) throws InterruptedException {

        new Runner().start();
        new Runner().start();
        new Runner().start();
    }

    static class Runner extends Thread{
        @Override
        public void run() {
            try {
                Thread.sleep(1000);
                rl.lock();
                System.out.println(Thread.currentThread().getName()+" before await..");
                condition.await();
                System.out.println(Thread.currentThread().getName()+" after await..");
                rl.unlock();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}



下面是执行的结果:


Thread-0 before await..
Thread-2 before await..
Thread-1 before await..

 

 

4、在AQS中维持了一个单一的状态信息state,可以通过getState、setState、compareAndSetState函数修改其值。对于ReentrantLock的实现来说,state可以用来表示

      当前线程获取锁的可重入次数;对于读写锁ReentrantReadWriteLock来说,state的高16位表示可读状态,也就是获取该读锁的次数,低16位表示获取到写锁的线程的可重入次数;

      对于semaphore来说,state用来表示当前可用信号的个数;

      对于CountDownlatch来说,state用来表示计数器当前的值。

 

Phaser移相器

在JDK7中加入的并发工具类

1、通过phaser同步的线程被称为party。所有需要同步的party必须持有同一个phaser对象。

party需要向phaser注册,执行phaser.register()方法注册,该方法仅仅是增加phaser中的线程计数。

也可以通过构造器注册,new Phaser(3) 就会在创建phaser对象时注册3个party

2、unarrived party到达一个phaser (阶段)之前处于unarrived状态。

3、arrived到达时处于arrived状态,一个arrived的party也被称为arrival

4、deregister

     一个线程可以在arrive某个phase后退出deregister,与参赛者中途退赛相同,可以使用

    arriveAndDeregister()方法来实现。(到达并注销)

package com.mkevin.demo10;

import java.util.concurrent.Phaser;
import java.util.concurrent.ThreadLocalRandom;

/**
 * Phaser的正常使用
 */
public class PhaserDemo1 {

    public static void main(String[] args) throws InterruptedException {

        //Phaser(5)代表注册的party数量, 不传入默认为0
        Phaser phaser = new Phaser(5);
        new Runner(phaser).start();
        Thread.sleep(ThreadLocalRandom.current().nextInt(1000));
        new Runner(phaser).start();
        Thread.sleep(ThreadLocalRandom.current().nextInt(1000));
        new Runner(phaser).start();
        Thread.sleep(ThreadLocalRandom.current().nextInt(1000));
        new Runner(phaser).start();
        Thread.sleep(ThreadLocalRandom.current().nextInt(1000));
        new Runner(phaser).start();
    }

    static  class Runner extends  Thread{

        private Phaser phaser;

        // 多个线程必须持有同一个phaser
        public Runner(Phaser phaser){
            this.phaser=phaser;
        }

        @Override
        public void run() {
            try {
                System.out.println(this.getName()+" is ready1");
                phaser.arriveAndAwaitAdvance();
                System.out.println(this.getName()+" running...");
                Thread.sleep(ThreadLocalRandom.current().nextInt(3000));
                System.out.println(this.getName()+" is ready2");
                phaser.arriveAndAwaitAdvance();
                System.out.println(this.getName()+" running...");
                Thread.sleep(ThreadLocalRandom.current().nextInt(3000));
                System.out.println(this.getName()+" is ready3");
                phaser.arriveAndAwaitAdvance();
                Thread.sleep(ThreadLocalRandom.current().nextInt(3000));
                System.out.println(this.getName()+" over");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

下面是执行的结果:

"C:\Program Files\Java\jdk1.8.0_221\bin\java.exe" "-javaagent:D:\IntellJ\IntelliJ IDEA 2019.3.4\lib\idea_rt.jar=51731:D:\IntellJ\IntelliJ IDEA 2019.3.4\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_221\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\rt.jar;C:\Users\weizhen\Downloads\e2d60ba87a7a8405405d05627e31e279\JAVA并发编程核心技术精讲【346812】JOIN方法详细解析\mima-concurrent\target\classes" com.mkevin.demo10.PhaserDemo1
Thread-0 is ready1
Thread-1 is ready1
Thread-2 is ready1
Thread-3 is ready1
Thread-4 is ready1
Thread-3 running...
Thread-2 running...
Thread-1 running...
Thread-0 running...
Thread-4 running...
Thread-4 is ready2
Thread-0 is ready2
Thread-2 is ready2
Thread-1 is ready2
Thread-3 is ready2
Thread-1 running...
Thread-3 running...
Thread-4 running...
Thread-2 running...
Thread-0 running...
Thread-0 is ready3
Thread-4 is ready3
Thread-3 is ready3
Thread-1 is ready3
Thread-2 is ready3
Thread-4 over
Thread-0 over
Thread-2 over
Thread-3 over
Thread-1 over

Process finished with exit code 0

 

 

 

自定义phaser类继承phaser接口,实现onAdvance方法。

package com.mkevin.demo10;

import com.mkevin.entity.P;

import java.util.concurrent.Phaser;
import java.util.concurrent.ThreadLocalRandom;

public class PhaserDemo2 {

    public static void main(String[] args) throws InterruptedException {

        P.o("Phaser's onAdvance 使用: 到达每个阶段执行");
        Phaser phaser = new MKevinPhaser(5, 3);
        System.out.println("需要参与者数量:" + phaser.getRegisteredParties());
        new Runner(phaser).start();
        Thread.sleep(ThreadLocalRandom.current().nextInt(1000));
        new Runner(phaser).start();
        Thread.sleep(ThreadLocalRandom.current().nextInt(1000));
        new Runner(phaser).start();
    }

    static class MKevinPhaser extends Phaser {

        //总计阶段数量
        private int totalPhaseNum = 3;

        public MKevinPhaser(int totalPhaseNum, int parties) {
            super(parties);
            this.totalPhaseNum = totalPhaseNum;
        }

        @Override
        protected boolean onAdvance(int phase, int registeredParties) {
            System.out.println("phase " + phase + " is over, registeredParties is " + registeredParties);
            //如果已经到达了最后一个阶段,或者参与者为0,则结束
            return (totalPhaseNum - 1) == phase || registeredParties == 0;
        }
    }


    static class Runner extends Thread {

        private Phaser phaser;

        public Runner(Phaser phaser) {
            this.phaser = phaser;
        }

        @Override
        public void run() {
            try {
                System.out.println(this.getName() + " is ready1");
                phaser.arriveAndAwaitAdvance();
                System.out.println(this.getName() + " running...");
                Thread.sleep(ThreadLocalRandom.current().nextInt(3000));
                System.out.println(this.getName() + " is ready2");
                phaser.arriveAndAwaitAdvance();
                System.out.println(this.getName() + " running...");
                Thread.sleep(ThreadLocalRandom.current().nextInt(3000));
                System.out.println(this.getName() + " is ready3");
                phaser.arriveAndAwaitAdvance();
                System.out.println(this.getName() + " running...");
                Thread.sleep(ThreadLocalRandom.current().nextInt(3000));
                System.out.println(this.getName() + " is ready4");
                phaser.arriveAndAwaitAdvance();
                System.out.println(this.getName() + " running...");
                Thread.sleep(ThreadLocalRandom.current().nextInt(3000));
                System.out.println(this.getName() + " is ready5");
                phaser.arriveAndAwaitAdvance();
                Thread.sleep(ThreadLocalRandom.current().nextInt(3000));
                System.out.println(this.getName() + " over");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

下面是运行的结果:


---------------------------------------------
Phaser's onAdvance 使用: 到达每个阶段执行
---------------------------------------------
需要参与者数量:3
Thread-0 is ready1
Thread-1 is ready1
Thread-2 is ready1
phase 0 is over, registeredParties is 3
Thread-1 running...
Thread-0 running...
Thread-2 running...
Thread-0 is ready2
Thread-1 is ready2
Thread-2 is ready2
phase 1 is over, registeredParties is 3
Thread-1 running...
Thread-0 running...
Thread-2 running...
Thread-2 is ready3
Thread-1 is ready3
Thread-0 is ready3
phase 2 is over, registeredParties is 3
Thread-2 running...
Thread-0 running...
Thread-1 running...
Thread-0 is ready4
Thread-2 is ready4
Thread-1 is ready4
phase 3 is over, registeredParties is 3
Thread-1 running...
Thread-0 running...
Thread-2 running...
Thread-2 is ready5
Thread-0 is ready5
Thread-1 is ready5
phase 4 is over, registeredParties is 3
Thread-2 over
Thread-1 over
Thread-0 over

Process finished with exit code 0

 

 

Phaser到达某个阶段后,一部分Phaser中途退出,另一部分继续执行:

package com.mkevin.demo10;

import com.mkevin.entity.P;

import java.util.concurrent.Phaser;

public class PhaserDemo3 {

    public static void main(String[] args) throws InterruptedException {

        P.o("Phaser's register 使用: 到达每个阶段执行");
        Phaser phaser = new Phaser();
        for (int i = 0; i < 4; i++) {
            phaser.register();
            new Thread(new Runner(phaser, i)).start();
        }
    }

    static class Runner implements Runnable {

        private Phaser phaser;

        private int no;

        public Runner(Phaser phaser, int no) {
            this.phaser = phaser;
            this.no = no;
        }

        @Override
        public void run() {
            try {
                System.out.println(Thread.currentThread().getName() + " start");
                phaser.arriveAndAwaitAdvance();
                System.out.println(Thread.currentThread().getName() + " running 1");
                if (no == 1) {
//                    int a = 10 / 0;
                    phaser.arriveAndDeregister();
                    return;
                }
                System.out.println(Thread.currentThread().getName() + " running 2");
                if (no == 3) {
                    phaser.arriveAndDeregister();
                    return;
                }
                phaser.arriveAndAwaitAdvance();
                System.out.println(Thread.currentThread().getName() + " end");
            } catch (Exception ex) {
                ex.printStackTrace();
                phaser.arriveAndDeregister();
            }

        }
    }

}

下面是运行的结果:


---------------------------------------------
Phaser's register 使用: 到达每个阶段执行
---------------------------------------------
Thread-0 start
Thread-1 start
Thread-2 start
Thread-3 start
Thread-3 running 1
Thread-0 running 1
Thread-0 running 2
Thread-1 running 1
Thread-2 running 1
Thread-2 running 2
Thread-3 running 2
Thread-2 end
Thread-0 end

Process finished with exit code 0

     

StampedLock简介

StampedLock类,在JDK8中加入全路径为java.util.concurrent.locks.StampedLock。

功能与RRW (ReentrantReadWriteLock) 功能类似提供三种读写锁。

StampedLock中引入了一个stamp (邮戳)的概念。它代表线程获取到锁的版本,每一把锁都有一个唯一的stamp。

写锁writeLock,是排它锁、也叫独占锁,相同时间只能有一个线程获取锁,其他线程请求读锁和写锁都会被阻塞。

功能类似于ReetrantReadWriteLock.writeLock

区别是StampedLock的写锁是不可重入锁。当前没有线程持有读锁或写锁的时候才可以获得获取到该锁。

 

stampedLock获取锁:

        P.o("StampedLock的WriteLock正常使用");
        //创建StampedLock对象
        StampedLock sl = new StampedLock();
        //获取读锁,并且返回stamp
        long stamp = sl.writeLock();
        System.out.println("get write lock,stamp="+stamp);
        //使用完毕,释放锁,但是要传入对应的stamp
        sl.unlockWrite(stamp);
        //再次获取写锁
        stamp = sl.writeLock();
        System.out.println("get write lock,stamp="+stamp);
        //释放写锁
        sl.unlockWrite(stamp);

下面是获取到锁的时间戳


---------------------------------------------
StampedLock的WriteLock正常使用
---------------------------------------------
get write lock,stamp=384
get write lock,stamp=640

Process finished with exit code 0

 

下面这段stampLock一个线程重新获得锁,不会成功。

writeLock与unlockWrite必须成对使用,解锁时必须需要传入相对应的stamp才可以释放锁。每次获得锁之后都会得到一个新的stamp值。

        P.o("StampedLock的WriteLock非正常使用:非重入锁");
        StampedLock sl = new StampedLock();
        long stamp = sl.writeLock();
        System.out.println("get write lock,stamp="+stamp);
        stamp = sl.writeLock();
        System.out.println("get write lock,stamp="+stamp);
        sl.unlockWrite(stamp);

下面是执行结果:

---------------------------------------------
StampedLock的WriteLock非正常使用:非重入锁
---------------------------------------------
get write lock,stamp=384

 

 

ReentrantReadWriteLock的可重入锁

同一个线程获取锁后,再次尝试获取锁而无法获取,则证明其为非重入锁。

对于ReentrantLock,同一个线程获取锁后,再次尝试获取锁可以获取,则证明其为重入锁。

对于ReentrantReadWriteLock.WriteLock,同一个线程获取写锁后,再次尝试获取锁依然可获取锁,则证明其为重入锁。

        P.o("ReentrantReadWriteLock:重入锁对比");
        ReentrantReadWriteLock rrw = new ReentrantReadWriteLock();
        ReentrantReadWriteLock.WriteLock writeLock = rrw.writeLock();
        writeLock.lock();
        System.out.println("get ReentrantReadWriteLock.WriteLock lock1");
        writeLock.lock();
        System.out.println("get ReentrantReadWriteLock.WriteLock lock2");
        writeLock.unlock();

下面是可重入锁的执行结果,同一个线程是可以重复获得到锁的:


---------------------------------------------
ReentrantReadWriteLock:重入锁对比
---------------------------------------------
get ReentrantReadWriteLock.WriteLock lock1
get ReentrantReadWriteLock.WriteLock lock2

 

 

StampedLock获取不到锁就不阻塞

StampedLock的写锁是不可重入锁

        P.o("StampedLock.tryWriteLock:尝试获取,如果能获取则获取锁,获取不到不阻塞");
        StampedLock sl1 = new StampedLock();
        long stamp1 = sl1.tryWriteLock();
        System.out.println("get StampedLock.tryWriteLock lock1,stamp="+stamp1);
        long stamp2 = sl1.tryWriteLock();
        System.out.println("can not get StampedLock.tryWriteLock lock1,stamp="+stamp2);
        long stamp3 = sl1.writeLock();
        System.out.println("can not get StampedLock.writeLock lock2,stamp="+stamp3);
        sl1.unlockWrite(stamp1);

下面是执行的结果,获取不到锁,这里就没有继续阻塞


---------------------------------------------
StampedLock.tryWriteLock:尝试获取,如果能获取则获取锁,获取不到不阻塞
---------------------------------------------
get StampedLock.tryWriteLock lock1,stamp=384
can not get StampedLock.tryWriteLock lock1,stamp=0

 

StampedLock的读锁

悲观读锁是一个共享锁,没有线程占用写锁的情况下,多个线程可以同时获取读锁。如果其他线程已经获得了写锁,则阻塞当前线程。

        P.o("StampedLock.readLock正常使用:注意每次获得的写锁stamp不同,必须所有的读锁都释放才可以获得写锁");
        StampedLock sl = new StampedLock();
        long stamp1 = sl.readLock();
        System.out.println("get read lock1,stamp="+stamp1);
        long stamp2 = sl.readLock();
        System.out.println("get read lock2,stamp="+stamp2);
        sl.unlockRead(stamp1);
        sl.unlockRead(stamp2);
        long stamp3 = sl.writeLock();
        System.out.println("get write lock,stamp="+stamp3);

下面是执行的结果:

StampedLock.readLock正常使用:注意每次获得的写锁stamp不同,必须所有的读锁都释放才可以获得写锁
---------------------------------------------
get read lock1,stamp=257
get read lock2,stamp=258
get write lock,stamp=384
--------------------------------

 

 

StampedLock存在读锁没有释放,所以不能获取到写锁,就是会一直阻塞住。

        P.o("StampedLock.readLock非正常使用:存在读锁未释放,无法获得写锁");
        StampedLock sl1 = new StampedLock();
        long stamp11 = sl1.readLock();
        System.out.println("get read lock1,stamp="+stamp11);
        long stamp21 = sl1.readLock();
        System.out.println("get read lock2,stamp="+stamp21);
        sl1.unlockRead(stamp11);
        long stamp31 = sl1.writeLock();
        System.out.println("get write lock,stamp="+stamp31);

下面是运行的结果,就是会一直阻塞住:

---------------------------------------------
StampedLock.readLock非正常使用:存在读锁未释放,无法获得写锁
---------------------------------------------
get read lock1,stamp=257
get read lock2,stamp=258

 

 

下面是StampedLock的正常使用,获取读锁,释放,获取写锁

        P.o("StampedLock.readLock正常使用:获取读锁->释放->获取写锁");
        StampedLock sl2 = new StampedLock();
        long stamp12 = sl2.readLock();
        System.out.println(Thread.currentThread().getName()+" get read lock1,stamp="+stamp12);
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+" run");
                long stamp121 = sl2.writeLock();
                System.out.println(Thread.currentThread().getName()+" get write lock1,stamp="+stamp121);
            }
        }).start();

        Thread.sleep(3000);
        System.out.println(Thread.currentThread().getName()+" get unlock read lock1,stamp="+stamp12);
        sl2.unlockRead(stamp12);

下面是运行的结果:

---------------------------------------------
StampedLock.readLock正常使用:获取读锁->释放->获取写锁
---------------------------------------------
main get read lock1,stamp=257
Thread-0 run
main get unlock read lock1,stamp=257
Thread-0 get write lock1,stamp=384

 

StampedLock获取写锁,释放,获取读锁

P.o("StampedLock.readLock正常使用:获取写锁->释放->获取读锁");
        StampedLock sl3 = new StampedLock();
        long stamp13 = sl3.writeLock();
        System.out.println(Thread.currentThread().getName()+" get write lock1,stamp="+stamp13);
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+" run");
                long stamp131 = sl3.readLock();
                System.out.println(Thread.currentThread().getName()+" get read lock1,stamp="+stamp131);
            }
        }).start();
        Thread.sleep(3000);
        System.out.println(Thread.currentThread().getName()+" get unlock write lock1,stamp="+stamp13);
        sl3.unlockWrite(stamp13);

下面是运行的结果:


---------------------------------------------
StampedLock.readLock正常使用:获取写锁->释放->获取读锁
---------------------------------------------
main get write lock1,stamp=384
Thread-0 run
main get unlock write lock1,stamp=384
Thread-0 get read lock1,stamp=513

Process finished with exit code 0

 

StampLock使用快照的方式,需要复制一份要操作的变量到方法栈,

操作的数据只是一个快照,从而保证了数据的最终一致性。

但是读线程可能使用的数据不是最新的。特别适用于读多写少的高并发场景

 

乐观读锁:

原理很简单,就是我先尝试获取,这时候没有写锁我就拿到了一个锁,在我真正要使用的时候我再验证一下是否发生了改变,如果没有发生改变就可以安心使用。
如果某个线程已经获取了写锁,这时候再尝试获取乐观锁也是可以获取的,只是得到的stamp为0,无法通过validate验证。

乐观读锁本质上并未加锁,而是提供了获取和检测的方法,由程序人员来控制该做些什么。
虽然性能大大提升,但是却增加了开发人员的复杂度,如果不是特别高的并发场景,对性能不要求极致,可以不考虑使用。

tryOptimisticRead与validate一定要紧紧挨着使用,否则在获取和验证之间很可能数据被修改。
如果这期间锁发生变化则validate返回false,否则返回true。

StampedLock sl = new StampedLock();
        //乐观读
        long stamp = sl.tryOptimisticRead();
        System.out.println(Thread.currentThread().getName()+" tryOptimisticRead stamp="+stamp);
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+" run");
                long stamp131 = sl.writeLock();
                System.out.println(Thread.currentThread().getName()+" get write lock1,stamp="+stamp131);
                sl.unlockWrite(stamp131);
                System.out.println(Thread.currentThread().getName()+" unlock write lock1,stamp="+stamp131);
            }
        }).start();
        Thread.sleep(3000);
        //验证是否发生过变化
        boolean validate = sl.validate(stamp);
        System.out.println(Thread.currentThread().getName()+" tryOptimisticRead validate="+validate);
        stamp = sl.tryOptimisticRead();
        System.out.println(Thread.currentThread().getName()+" tryOptimisticRead stamp="+stamp);
        validate = sl.validate(stamp);

下面是执行的结果(由执行结果可以看出乐观读锁,不是真正的锁):


main tryOptimisticRead stamp=256
Thread-0 run
Thread-0 get write lock1,stamp=384
Thread-0 unlock write lock1,stamp=384
main tryOptimisticRead validate=false
main tryOptimisticRead stamp=512

Process finished with exit code 0

 

锁的分类:

1、乐观锁和悲观锁的概念来自于数据库
2.悲观锁对数据被修改持悲观态度,认为数据很容易就会被其他线程修改,所以在处理数据之前先加锁,处理完毕释放锁。
3.乐观锁对数据被修改持乐观态度,认为数据一般情况下不会被其他线程修改,所以在处理数据之前不会加锁,而是在数据进行更新时进行冲突检测。
4.对于数据库的悲观锁就是排它锁,在处理数据之前,先尝试给记录加排它锁,如果成功则继续处理,如果失败则挂起或抛出异常,直到数据处理完毕释放锁。
5.对于数据库的乐观锁所典型的就是CAS方式更新, 例如:update name=‘kevin’ where id=1 and name=‘kevin0’,在更新数据的时候校验这个值是否发生了变化,类似于CAS的操作。

 

公平锁与非公平锁:

1.据线程获取锁的抢占机制,锁可以分为公平锁和非公平锁,最早请求锁的线程将最早获取到锁。而非公平锁则先请求不一定先得。JUC中的ReentrantLock提供了公平和非公平锁特性。·
2.公平锁:ReentrantLockpairLock= new ReentrantLock(true)
3.非公平锁:ReentrantLockpairLock= new ReentrantLock(false),如果构造函数不传递参数,则默认是非公平锁。
4.非必要情况下使用非公平锁,公平锁存在性能开销

 

独占锁与共享锁

1.只能被单个线程所持有的锁是独占锁,可以被多个线程持有的锁是共享锁。
2.ReentrantLock就是以独占方式实现的,属于悲观锁
3.ReadWriteLock读写锁是以共享锁方式实现的,属于乐观锁
4.StampedLock的写锁,属于悲观锁。
5.StampedLock的乐观读锁,悲观读锁、属于乐观锁。

 

可重入锁

1.当一个线程想要获取本线程已经持有的锁时,不会被阻塞,而是能够再次获得这个锁,这就是重入锁。
2.Synchornized是一种可重入锁,内部维护一个线程标志(谁持有锁),以及一个计数器。
3.ReentrantLock也是一种可重入锁
4.ReadWriteLock、StampedLock的读锁也是可重入锁

 

自旋锁

1.当获取锁的时候如果发现锁已经被其他线程占有,则不阻塞自己,也不释放CPU使用权,而是尝试多次获取,如果尝试了指定次数之后仍然没有获得锁,再阻塞线程。
2.自旋锁认为锁不会被长时间持有,使用CPU时间来换取线程上下文切换的开销,从而提高性能。但是可能会浪费CPU资源。
3.-XX:PreBlockSpin=n可以设置自旋次数(已经成为了历史),在Jdk7u40时被删除了,其实在jkd6的时候就已经无效了,现在HotSpotVM采用的是adaptive spinning(自适应自旋),虚拟机会根据情况来对每个线程使用不同的自旋次数。

 

通常使用线程池的submit方法将任务提交到线程池内执行。

如果此时线程池内有空闲的线程,则会立即执行该任务,如果没有则需要根据线程池的类型选择等待,或者新建线程。

线程池内的线程并不是线程池对象初始化的时候就创建好的,而是当有任务被提交进来之后才创建的,

创建线程的过程是无法干预的。

使用ThreadFactory

第一步:编写ThreadFactory接口的实现类

第二步:创建线程池时传入ThreadFactory对象

 

下面代码讲的是ThreadFactory初始化的时候动态输出日志的程序

package com.mkevin.demo13;

import com.mkevin.entity.P;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadLocalRandom;
/**
 * 线程工场
 */
class DemoThreadFactory implements ThreadFactory {

    // 是否记录日志
    private boolean saveLog;
    // 工场名称
    private String factoryName;

    public DemoThreadFactory(String factoryName, boolean saveLog) {
        this.factoryName = factoryName;
        this.saveLog = saveLog;
    }

    @Override
    public Thread newThread(Runnable r) {
        if (saveLog) {
            //动态输出日志
            P.l(System.currentTimeMillis() + this.factoryName + " create start");
        }
        Thread thread = new Thread(r);
        //自定义线程名字
        thread.setName("Kevin-Thread-" + thread.getName() + ":" + thread.getId());
        try {
            //模拟初始化时间
            Thread.sleep(ThreadLocalRandom.current().nextInt(1000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (saveLog) {
            P.l(System.currentTimeMillis() + this.factoryName + " " + thread.getName() + " create end");
        }
        return thread;
    }
}

下面是自定义线程工厂的使用:

package com.mkevin.demo13;

import com.mkevin.entity.P;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 使用线程工场创建线程
 */
public class ThreadFactoryDemo0 {
    public static void main(String[] args) {

        //创建任务对象
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                P.l(Thread.currentThread().getName() + " run");
            }
        };

        P.o("newCachedThreadPool使用ThreadFactory");
        //创建自定义线程工场
        DemoThreadFactory threadFactory = new DemoThreadFactory("newCachedThreadPoolFactory",true);
        //使用线程工场初始化线程池
        ExecutorService executorService = Executors.newCachedThreadPool(threadFactory);
        for (int i = 0; i < 3; i++) {
            executorService.execute(runnable);
        }
        executorService.shutdown();


        P.o("newFixedThreadPool使用ThreadFactory");
        //创建自定义线程工场
        threadFactory = new DemoThreadFactory("newFixedThreadPoolFactory",true);
        //使用线程工场初始化线程池
        executorService = Executors.newFixedThreadPool(2, threadFactory);
        for(int i=0;i<3;i++){
            executorService.execute(runnable);
        }
        executorService.shutdown();

        //创建其他线程池也可以使用ThreadFactory
        //Executors.newScheduledThreadPool(2,threadFactory);
        //Executors.newSingleThreadExecutor(threadFactory);
        //Executors.newSingleThreadScheduledExecutor(threadFactory);
    }
}

下面是线程工厂的运行结果:


---------------------------------------------
newCachedThreadPool使用ThreadFactory
---------------------------------------------
1608532534788newCachedThreadPoolFactory create start
1608532534951newCachedThreadPoolFactory Kevin-Thread-Thread-0:11 create end
1608532534951newCachedThreadPoolFactory create start
Kevin-Thread-Thread-0:11 run
1608532535264newCachedThreadPoolFactory Kevin-Thread-Thread-1:12 create end
Kevin-Thread-Thread-0:11 run
---------------------------------------------
newFixedThreadPool使用ThreadFactory
---------------------------------------------
Kevin-Thread-Thread-1:12 run
1608532535264newFixedThreadPoolFactory create start
1608532535846newFixedThreadPoolFactory Kevin-Thread-Thread-2:13 create end
1608532535846newFixedThreadPoolFactory create start
Kevin-Thread-Thread-2:13 run
1608532536590newFixedThreadPoolFactory Kevin-Thread-Thread-3:14 create end
Kevin-Thread-Thread-2:13 run
Kevin-Thread-Thread-3:14 run

Process finished with exit code 0

 

 

线程池发生异常的状态捕获

1、线程池内运行的线程如果发生异常,一定要捕获

2、可以采用更优雅的方式处理所有线程的异常,例如记录日志,发送预警消息等。

3、结合ThreadFactory以及线程的setUncaughtExceptionHandler方法来处理最好

4、对execute提交的任务有效,对submit提交的任务无效!

        // 创建线程池,并向线程池提交任务,则创建线程,任务执行完毕而线程不销毁,JVM继续运行
        // 等待60秒后,自动销毁空闲线程,JVM退出
        Runner runner = new Runner();
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.submit(runner);
        P.l("main is over");

下面显示60秒后线程自动退出


main is over
pool-1-thread-1 begin
pool-1-thread-1 end

 

 

 

创建线程池,向线程池提交任务,任务执行完毕而线程不销毁,JVM继续运行

始终保持1个线程存活,所以JVM不会退出。

        // 创建线程池,并向线程池提交任务,则创建线程,任务执行完毕而线程不销毁,JVM继续运行
        // 始终保持有1个线程存活,所以JVM不会退出
        Runner runner = new Runner();
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        executorService.submit(runner);
        P.l("main is over");

下面的执行结果显示那个线程一直没有退出


main is over
pool-1-thread-1 begin
pool-1-thread-1 end

 

 

 

 

关闭线程池

1、shutdown让线程池内的任务继续执行完毕,但是不允许新的任务提交。

2、shutdown方法不阻塞, 等所有线程执行完毕后,销毁线程。

3、shutdown之后提交的任务会抛出RejectedExecutionException异常,代表拒绝接收。

4、shutdownNow之后提交的任务会抛出RejectedExecutionException异常,代表拒绝接收。

5、shutdownNow之后会引发sleep、join、wait方法的InterruptedException异常。

6、如果任务中没有触发InterruptedException的条件,则任务会继续运行直到结束。

 

线程池的各种状态

7、isShutdown用来判断线程池是否已经关闭。

8、isTerminated任务全部执行完毕,并且线程池已经关闭,才会返回true。

9、awaitTermination 阻塞,直到所有任务在关闭请求后完成执行,或发生超时,或当前线程中断。

 

shutdown让线程池内的任务继续执行完毕,但是不允许新的任务提交,shutdow方法不阻塞,

在fixThreadPool执行完任务后,停掉线程,释放资源等所有线程执行完毕后, 销毁线程,JVM退出。

        //shutdown让线程池内的任务继续执行完毕,但是不允许新的任务提交,shutdow方法不阻塞
        //等所有线程执行完毕后, 销毁线程,JVM退出
        Runner runner = new Runner();
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        executorService.submit(runner);
        executorService.shutdown();
        P.l("main is over");

下面是执行的结果:


pool-1-thread-1 begin
main is over
pool-1-thread-1 end

Process finished with exit code 0

 

 

  shutdown之后提交的任务会报RejectedExecutionException异常,代表拒绝接收

        Runner runner = new Runner();
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        executorService.submit(runner);
        executorService.shutdown();
        P.l("main is over");
        executorService.submit(runner);

下面是执行的结果:


main is over
pool-1-thread-1 begin
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@677327b6 rejected from java.util.concurrent.ThreadPoolExecutor@14ae5a5[Shutting down, pool size = 1, active threads = 1, queued tasks = 0, completed tasks = 0]
	at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
	at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
	at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
	at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)
	at com.mkevin.demo14.ThreadPoolDemo1.main(ThreadPoolDemo1.java:44)
pool-1-thread-1 end

Process finished with exit code 1

 

 

如果任务中没有触发InterruptedException的条件,则会继续执行直到停止。

        ExecutorService executorService = Executors.newFixedThreadPool(5);
        executorService.submit(new Runner1());
        executorService.shutdownNow();
        P.l("main is over");

下面是一直在运行

535061931
pool-1-thread-1 1608535061931
pool-1-thread-1 1608535061931
pool-1-thread-1 1608535061931
pool-1-thread-1 1608535061931
pool-1-thread-1 1608535061931
pool-1-thread-1 1608535061931
pool-1-thread-1 1608535061931
pool-1-thread-1 1608535061931


Process finished with exit code -1

 

 

可以在任务中判断Thread.currentThread().isInterrupted()来规避shutdownNow的问题
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        executorService.submit(new Runner2());
        executorService.shutdownNow();
        P.l("main is over");

下面是运行的结果


pool-1-thread-1 1608535331388
main is over
pool-1-thread-1 interrupted

Process finished with exit code 0

 

 

核心线程预启动策略

1、默认情况下,核心线程只有在任务提交的时候才会创建。

2、而预启动策略,可以让核心线程提前启动,从而增强最初提交的线程运行性能。

3、prestartCoreThread启动1个核心线程,覆盖仅在执行新任务时启动核心线程的默认策略。

     如果所有核心线程都已启动,则此方法将返回false。

4、prestartAllCoreThreads启动所有核心线程。覆盖仅在执行新任务时启动核心线程的默认策略。

     如果核心线程全部启动后再次调用,则会返回0。

 

  核心线程预启动策略

        ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 2, 5,
                TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>());
        P.l("线程池中的线程数:" + executor.getPoolSize());
        P.l(executor.prestartCoreThread());
        P.l("线程池中的线程数:" + executor.getPoolSize());
        P.l(executor.prestartCoreThread());
        P.l("线程池中的线程数:" + executor.getPoolSize());
        P.l(executor.prestartCoreThread());
        P.l(executor.prestartCoreThread());
        P.l("线程池中的线程数:" + executor.getPoolSize());

下面是执行的结果:


线程池中的线程数:0
true
线程池中的线程数:1
true
线程池中的线程数:2
false
false
线程池中的线程数:2

 

 

预启动所有核心线程

        Runner runner = new Runner();
        ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 2, 5,
                TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>());
        P.l("线程池中的线程数:" + executor.getPoolSize());
        P.l("启动核心线程数量为:" +executor.prestartAllCoreThreads());
        P.l("线程池中的线程数:" + executor.getPoolSize());
        P.l("启动核心线程数量为:" +executor.prestartAllCoreThreads());

下面是执行的结果


线程池中的线程数:0
启动核心线程数量为:2
线程池中的线程数:2
启动核心线程数量为:0

 

 

Java ThreadPool线程例子:

package com.mkevin.demo18;


import com.mkevin.entity.P;

import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolGetDemo {
    public static void main(String[] args) throws InterruptedException {
        Runner runner = new Runner();
        ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 2, 5,
                TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>());
        executor.execute(runner);
        executor.execute(runner);
        executor.execute(runner);
        executor.execute(runner);
        Thread.sleep(1000);
        P.l("返回核心线程数getCorePoolSize:"+executor.getCorePoolSize());
        P.l("返回当前线程池中的线程数getPoolSize:"+executor.getPoolSize());
        P.l("返回最大允许的线程数getMaximumPoolSize:"+executor.getMaximumPoolSize());
        P.l("返回池中同时存在的最大线程数getLargestPoolSize:"+executor.getLargestPoolSize());
        P.l("预定执行的任务总和getTaskCount:"+executor.getTaskCount());
        P.l("当前线程池已经完成的任务数getCompletedTaskCount:"+executor.getCompletedTaskCount());
        P.l("正在执行任务的线程的大致数目getActiveCount:"+executor.getActiveCount());
        P.l("空闲时间getKeepAliveTime:"+executor.getKeepAliveTime(TimeUnit.SECONDS));
        P.l("------------------------------------");
        Thread.sleep(1100);
        P.l("当前线程池已经完成的任务数getCompletedTaskCount:"+executor.getCompletedTaskCount());
        Thread.sleep(1100);
        P.l("当前线程池已经完成的任务数getCompletedTaskCount:"+executor.getCompletedTaskCount());
        executor.shutdown();
        P.l("当前线程池已经完成的任务数getCompletedTaskCount:"+executor.getCompletedTaskCount());
    }

    static class Runner implements Runnable {
        @Override
        public void run() {
            P.l(Thread.currentThread().getName() + " run " + System.currentTimeMillis()+ "start");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            P.l(Thread.currentThread().getName() + " run " + System.currentTimeMillis()+ "end");
        }
    }
}

下面是运行的结果:

pool-1-thread-2 run 1608605075322start
pool-1-thread-1 run 1608605075322start
返回核心线程数getCorePoolSize:2
返回当前线程池中的线程数getPoolSize:2
返回最大允许的线程数getMaximumPoolSize:2
返回池中同时存在的最大线程数getLargestPoolSize:2
预定执行的任务总和getTaskCount:4
当前线程池已经完成的任务数getCompletedTaskCount:0
正在执行任务的线程的大致数目getActiveCount:2
空闲时间getKeepAliveTime:5
------------------------------------
pool-1-thread-2 run 1608605076338end
pool-1-thread-1 run 1608605076338end
pool-1-thread-1 run 1608605076338start
pool-1-thread-2 run 1608605076338start
pool-1-thread-2 run 1608605077338end
pool-1-thread-1 run 1608605077338end
当前线程池已经完成的任务数getCompletedTaskCount:4
当前线程池已经完成的任务数getCompletedTaskCount:4
当前线程池已经完成的任务数getCompletedTaskCount:4

Process finished with exit code 0

 

CompletionService

 

CompletionService的使用模式,不断地提交任务给executor处理。

处理后的结果都会自动放入BlockedQueue。

另外一个线程不断地从队列里面取得处理的结果。

哪个任务先处理完,就能先得到哪个结果。

最后做汇总处理。

 

线程池Executor加上阻塞队列BlockedQueue

线程池Executor用来处理任务。

BlockedQueue用来获取每个线程的运行结果。

 

CompletionService 用来获取线程执行的结果

1、阻塞take 获取已经处理结束的结果,最先处理结束的,最先得到

Future f = cs.take();

2、非阻塞-poll,获取不到返回null

Future f = cs.poll();

3、阻塞固定时间-到时间获取不到返回null

Future f = cs.poll(2,TimeUnit.SECONDS);

 

 

package com.mkevin.demo11;

import java.util.concurrent.*;

public class CompletionServiceDemo0 {
    public static void main(String[] args) {
        try {
            //创建线程池
            ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 10, 5, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>());
            //创建CompletionService, 需要借助Executor来实现
            CompletionService cs = new ExecutorCompletionService(executor);
            //创建回调线程
            SalaryCallable0 sc1 = new SalaryCallable0(300, 3000);
            SalaryCallable0 sc2 = new SalaryCallable0(200, 2000);
            SalaryCallable0 sc3 = new SalaryCallable0(100, 1000);
            //提交到线程池执行
            cs.submit(sc1);
            cs.submit(sc2);
            cs.submit(sc3);

            for (int i = 0; i < 3; i++) {
                //阻塞-take获取已经处理结束的结果, 最先处理结束的,最先得到
                Future f = cs.take();
                //非阻塞-poll, 获取不到则返回null
//                Future f = cs.poll();
                //阻塞固定时间-到时间获取不到则返回null
//                Future f = cs.poll(2,TimeUnit.SECONDS);
                System.out.println("result(" + i + ")=" + f.get());
            }

        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

 

 

使用CompletionService提交任务,让Runnable也具有获得结果的特性

package com.mkevin.demo11;

import java.util.concurrent.*;

/**
 * 让Runnable也具有获得结果的特性
 */
public class CompletionServiceDemo2 {
    public static void main(String[] args) {
        try {
            //创建结果对象,用于获取结果
            Result result = new Result();
            ExecutorService executorService = Executors.newCachedThreadPool();
            CompletionService cs = new ExecutorCompletionService(executorService);
            //创建Runnable对象
            SalaryRunner2 runner1 = new SalaryRunner2(result, 10, 1000);
            SalaryRunner2 runner2 = new SalaryRunner2(result, 20, 2000);
            //提交对象, 并使用result对象接收结果
            Future<Result> f1 = cs.submit(runner1,result);
            Future<Result> f2 = cs.submit(runner2,result);
            //获取结果
            System.out.println("f1:" + f1.get().getValue());
            System.out.println("f2:" + f2.get().getValue());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

下面是运行的结果:


f1:10000
f2:20000

Process finished with exit code -1

 

 

 

下面展示一个线程不断产生任务,另外一个线程不断消费任务

package com.mkevin.demo11;

import java.util.concurrent.*;

public class CompletionServiceDemo3 {
    public static void main(String[] args) throws InterruptedException {
        //创建线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 10, 5, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>());
        //创建CompletionService, 需要借助Executor来实现
        CompletionService cs = new ExecutorCompletionService(executor);

        //一个线程不断产生任务
        Thread t0 = new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i=0;i<100;i++){
                    SalaryCallable0 sc1 = new SalaryCallable0(300, ThreadLocalRandom.current().nextInt(2000));
                    cs.submit(sc1);
                }
            }
        });

        t0.start();

        //一个线程不断处理结果
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                //阻塞-take获取已经处理结束的结果, 最先处理结束的,最先得到
                Future f = null;
                try {
                    while (true) {
                        f = cs.take();
                        System.out.println(Thread.currentThread().getName() + " getResult = " + f.get());
                    }
                } catch (InterruptedException | ExecutionException e) {
                    e.printStackTrace();
                }
            }
        });

        t1.start();

        t0.join();
        t1.join();

        executor.shutdown();
    }
}

下面是运行的结果:


pool-1-thread-3 sleep=1734 salary*costTime=520200.0
pool-1-thread-1 sleep=683 salary*costTime=204900.0
pool-1-thread-2 sleep=158 salary*costTime=47400.0
pool-1-thread-2 sleep=977 salary*costTime=293100.0
Thread-1 getResult = 47400.0
Thread-1 getResult = 204900.0
pool-1-thread-1 sleep=1996 salary*costTime=598800.0
pool-1-thread-2 sleep=520 salary*costTime=156000.0
Thread-1 getResult = 293100.0
pool-1-thread-2 sleep=1204 salary*costTime=361200.0
Thread-1 getResult = 156000.0
pool-1-thread-3 sleep=450 salary*costTime=135000.0
Thread-1 getResult = 520200.0
Thread-1 getResult = 135000.0
pool-1-thread-3 sleep=990 salary*costTime=297000.0
pool-1-thread-1 sleep=1085 salary*costTime=325500.0
Thread-1 getResult = 598800.0
pool-1-thread-2 sleep=669 salary*costTime=200700.0
Thread-1 getResult = 361200.0
pool-1-thread-3 sleep=112 salary*costTime=33600.0
Thread-1 getResult = 297000.0
Thread-1 getResult = 33600.0
pool-1-thread-3 sleep=1365 salary*costTime=409500.0
pool-1-thread-2 sleep=1221 salary*costTime=366300.0
Thread-1 getResult = 200700.0
pool-1-thread-1 sleep=51 salary*costTime=15300.0
Thread-1 getResult = 325500.0
Thread-1 getResult = 15300.0

 

ForkJoin使用

java.util.concurrent.RecursiveTask 递归任务

java.util.concurrent.RecursiveAction 递归活动

ForkJoin递归地去执行任务

package com.mkevin.demo12;

import java.util.concurrent.ForkJoinPool;

public class ForkJoinDemo0 {
    public static void main(String[] args) throws InterruptedException {
        //创建ForkJoinPool
        ForkJoinPool pool = new ForkJoinPool();
        //提交任务
        pool.submit(new RecursiveAction0(0, 9));
        Thread.sleep(Integer.MAX_VALUE);
    }
}
RecursiveAction0这个类
package com.mkevin.demo12;

import java.util.concurrent.RecursiveAction;
import java.util.concurrent.ThreadLocalRandom;

public class RecursiveAction0 extends RecursiveAction {
    private int beginValue;
    private int endValue;

    public RecursiveAction0(int beginValue, int endValue) {
        super();
        this.beginValue = beginValue;
        this.endValue = endValue;
    }

    @Override
    protected void compute() {
        System.out.println(Thread.currentThread().getName() + " start compute");
        try {
            Thread.sleep(ThreadLocalRandom.current().nextInt(5000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (endValue - beginValue > 2) {
            //计算中间值
            int middelNum = (beginValue + endValue) / 2;
            //左计算
            RecursiveAction0 leftAction = new RecursiveAction0(beginValue, middelNum);
            //右计算
            RecursiveAction0 rightAction = new RecursiveAction0(middelNum + 1, endValue);
            //拆解
            this.invokeAll(leftAction, rightAction);
        } else {
            System.out.println(Thread.currentThread().getName() +  " : " + beginValue + "-" + endValue);
        }
    }
}

下面是执行的结果:

ForkJoinPool-1-worker-1 start compute
ForkJoinPool-1-worker-1 start compute
ForkJoinPool-1-worker-2 start compute
ForkJoinPool-1-worker-2 start compute
ForkJoinPool-1-worker-3 start compute
ForkJoinPool-1-worker-1 start compute
ForkJoinPool-1-worker-0 start compute
ForkJoinPool-1-worker-2 : 5-7
ForkJoinPool-1-worker-1 : 0-2
ForkJoinPool-1-worker-3 : 8-9
ForkJoinPool-1-worker-0 : 3-4

Process finished with exit code -1

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值