JUC

写在前面:

之前学习Java基础时,对线程这块不是很清晰,当时上课几个基本的概念知识点学习了一下就匆匆过去了。后期,发现线程这块还是挺重要的。工作上,也看到有在应用。所以,趁着寒假过年在家,把线程这块给弥补弥补。老样子。尚硅谷,找视频。跟着学。做笔记。

链接地址:

http://www.gulixueyuan.com/course/96/reviews

源码百度网盘:

链接地址:https://pan.baidu.com/s/1YeyL43QOjr8gTsJso39J3A 

提取码:08c7

ps:若要收费,可去B站搜索 尚硅谷。几乎都是免费且都有的。

好了,还是老样子。列知识点。层层进入。

  • JavaJUC简介

  • Volatile关键字-内存可见性

  • 原子变量-CAS算法

  • ConcurrentHashMap锁分段机制

  • CountDownLatch闭锁

  • 实现Callable接口

  • Lock同步锁

  • 等待唤醒机制

  • Condition控制线程通信

  • 线程八锁

  • RedWriteLock读写锁

  • 线程池

  • 线程调度

  • ForkJoinPool分支/合并框架 工作窃取

 

一、JavaJUC简介

JUC就是java.util .concurrent工具包的简称。这是一个处理线程的工具包,JDK 1.5开始出现的。

二、Volatile关键字-内存可见性

含义:

当变量被volatile关键字修饰时,会保证修改的值立即写入到内存当中。其它线程需要访问时从内存中读取新值。

public class VolatileDemo{
    public static void main(String[] args) {
        ThreadDemo threadDemo = new ThreadDemo();
        new Thread(threadDemo).start();
        while (true){
                if(threadDemo.isFlag()){
                    System.out.println("-------------------");
                    break;
                }
        }
    }
}
class ThreadDemo implements Runnable{
    private boolean flag = false;
    public void run() {
        try {
            System.out.println(1);
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(2);
        flag = true;
        System.out.println(3);
        System.out.println("flag" + isFlag());
    }


    public boolean isFlag() {
        return flag;
    }
    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

一个ThreadDemo类实现Runnable创建一个线程。它有一个成员变量flag为false,然后重写run方法,在run方法里面将flag改为true,同时还有几条输出语句+线程睡眠。然后就是main方法主线程去读取flag。如果flag为true,就会break掉while循环,否则就是死循环。按道理,下面那个线程将flag改为true了,主线程读取到的应该也是true,循环应该会结束。看看运行结果:

从图中可以看到,ThreadDemo类里输出的flag已经被更改为true。但main方法并没有结束。Main主线程在while死循环。进一步分析if(threadDemo.isFlag())这里还是false。分线程里flag为true。而主线程里为false。

内存可见性问题:当多个线程操作共享数据时,彼此不可见。

解决办法1

加synchronized关键字。

while (true){
    synchronized (threadDemo){
        if(threadDemo.isFlag()){
            System.out.println("-------------------");
            break;
        }
    }
}

加了锁,就可以让while循环每次都从存中去读取数据,这样就能读取到true了。但是一加锁,每次只能有一个线程访问,当一个线程持有锁时,其他的就会阻塞,效率就非常低了。不想加锁,又要解决内存可见性问题,那么就可以使用volatile关键字。

解决办法2

private volatile boolean flag = false;

volatile关键字:当多个线程操作共享数据时,可以保证内存中的数据可见。用这个关键字修饰共享数据,就会及时的把线程缓存中的数据刷新到主存中去。

volatile和synchronized的区别:

        volatile不具备互斥性(当一个线程持有锁时,其他线程进不来,这就是互斥性)

        volatile不具备原子性


三、原子变量-CAS算法

什么是原子性?

public class AtomicDemo {
    public static void main(String[] args) {
        ThreadAtomicDemo threadAtomicDemo = new ThreadAtomicDemo();
        for(int i=0;i<10;i++){
            new Thread(threadAtomicDemo).start();
        }
    }
}


class ThreadAtomicDemo implements Runnable{
    private int num = 0;


    public int getNum(){
        return num++;
    }
    @Override
    public void run() {
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
        }
        System.out.println(getNum());
    }
}

开启10个线程,去执行getNum()方法。获得每个num值。

得到的结果。多个重复相同的值。出现了重复数据。明显产生了多线程安全问题。

所谓原子性就是操作不可再细分,而i++操作分为读改写三步

分析。num++问题。分为三步。读、改、写

读:int temp = num

改:num = num +1

写:num = temp

读改写有三步。不能分开,读改执行完,准备写时。CPU被其它线程抢走。主存中还是0,但CPU中应该要为1。

解决办法1

Synchronized关键字

public synchronized int getNum(){
    return num++;
}

每次都刷新下,让CPU去主存中读取最新的num。但一加锁,每次只能有一个线程访问,会造成线程堵塞。

解决办法2

Jdk1.5后java.util.concurrent.atomic包提供了原子类。AtomicInteger

AtomicInteger num = new AtomicInteger();
@Override
public void run() {
    try {
        Thread.sleep(200);
    } catch (InterruptedException e) {
    }
    System.out.println(num.getAndIncrement());
}

原子变量

  • 有volatile保证内存可见性。

  • 用CAS算法保证原子性。

CAS算法

CAS算法是计算机硬件对并发操作共享数据的支持,CAS包含3个操作数:

  • 内存值V

  • 预估值A

  • 更新值B

当且仅当 V==A时,V=B。否则,不做任何操作。

首先V是主存中的值0,然后预估值A也是0,因为此时还没有任何操作,这时V=A,所以进行自增,同时把主存中的值变为1。如果第二个线程读取到主存中的还是0也没关系,因为此时预估值已经变成1,V不等于A,所以不进行任何操作。

四、ConcurrentHashMap锁分段机制

JDK 1.5之后,在java.util.concurrent包中提供了多种并发容器类来改进同步容器类的性能。其中最主要的就是ConcurrentHashMap。

ConcurrentHashMap

ConcurrentHashMap就是一个线程安全的hash表。我们知道HashMap是线程不安全的,Hash Table加了锁,是线程安全的,因此它效率低。HashTable加锁就是将整个hash表锁起来,当有多个线程访问时,同一时间只能有一个线程访问,并行变成串行,因此效率低。所以JDK1.5后提供了ConcurrentHashMap,它采用了锁分段机制。

将ConcurrentHashMap分成了16段,每一段对应一个Hash表,且都有独立的锁。所以这样就可以每个线程访问一个Segment,就可以并行访问了,从而提高了效率。这就是锁分段。但是,java 8 又更新了,不再采用锁分段机制,也采用CAS算法了。

public class CopyOnWriteDemo {
    public static void main(String[] args) {
        CopyOnWriteArrayDemoThread copy = new CopyOnWriteArrayDemoThread();
        for(int i=0;i<=10;i++){
            new Thread(copy).start();
        }
    }
}
class CopyOnWriteArrayDemoThread implements Runnable{


    private static List<String> list = Collections.synchronizedList(new ArrayList<String>());


    static {
        list.add("1");
        list.add("2");
    }


    @Override
    public void run() {
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
            list.add("0");
        }
    }
}

10个线程并发访问这个集合,读取集合数据的同时再往集合中添加数据。运行这段代码会报错,并发修改异常。

将创建集合方式改成:

private static CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();

复制并写入。每次生成新的,所以如果添加操作比较多的话,开销非常大,适合修改操作比较多的时候使用。

五、CountDownLatch闭锁

java.util.concurrent包中提供了多种并发容器类来改进同步容器的性能。ContDownLatch是一个同步辅助类,在完成某些运算时,只有其他所有线程的运算全部完成,当前运算才继续执行,这就叫闭锁。看下面代码:

public class CountDownLatchDemo {
    public static void main(String[] args) {
        final CountDownLatch countDownLatch = new CountDownLatch(5);//有多少个线程这个参数就是几
        LatchThread latchThread = new LatchThread(countDownLatch);


        long start = System.currentTimeMillis();
        for (int i = 0; i < 5; i++) {
            new Thread(latchThread).start();
        }
        try {
            countDownLatch.await();//这5个线程执行完之前先等
        } catch (InterruptedException e) {
        }
        long end = System.currentTimeMillis();
        System.out.println("耗时时间:"+(end-start));


    }
}
class LatchThread implements Runnable{
    private CountDownLatch countDownLatch;


    public LatchThread(CountDownLatch countDownLatch){
        this.countDownLatch = countDownLatch;
    }


    @Override
    public void run() {
        synchronized (this){
            try {
                for(int i=0;i<=50;i++){
                    if(i%2==0){
                        System.out.println(i);
                    }
                }
            }finally {
                countDownLatch.countDown();//每执行完一个就递减一个
            }
        }
    }
}

开启了5个线程。每个线程去打印50以内的偶数。统计这5个线程执行完共花费了多少时间。若不用到CountDownLatch因为主线程与这5个线程也是同时执行的,可能那5个线程才执行到一半,主线程就已经输出“耗费时间为x秒”这句话了。所以要想计算这5个线程执行的时间,就得让主线程先等待等5个分线程都执行完了才能执行主线程。这就要用到闭锁。

如上代码,主要就是用latch.countDown()和latch.await()实现闭锁。

CountDownLatch countDownLatch = new CountDownLatch(5)
countDownLatch.await();
countDownLatch.countDown();

六、实现Callable接口

创建线程的4种方式?

①继承Thread类,重写run方法。子类对象调用start()方法。

②实现Runnable接口,重写run方法。Thread(Runnable r)传递Runnable接口实现类。

③实现Callable接口

④线程池。Executors线程池工厂。

 

线程对象调用start()和run()方法有什么区别?

线程对象调用start()代表开启线程并让JVM调用run()方法在开启的线程中执行。

线程对象调用run()方法不开启线程,仅仅是普通对象调用方法。

 

继承Thread类 VS 实现Runable接口?

用继承的话,线程对象和线程任务紧密耦合在一起,而实现接口的话,可以根据Thread构造方法自定义需要实现的线程任务。且将线程对象和线程任务分离出来。

 

线程有哪几种运行状态?

新建、运行、休眠、睡眠、等待、死亡、受阻塞

 

线程池作用?

并发线程数量很多,每个线程都执行一下就结束了。频繁创建影响效率,使用线程池可以复用,执行完并不被销毁,继续执行其它线程。

 

实现Runnable接口VS实现Callable接口的区别?

实现Runnable接口 没有返回值 不带泛型 不能往上抛异常

实现Callable接口 有返回值 带泛型 运行抛异常

 

解决多线程安全问题有哪几种方法?

①synchronized代码块

②synchronized方法

③Lock前置锁

 

先看代码

public class CallableDemo {
    public static void main(String[] args) {
        ThreadCallableDemo threadCallableDemo = new ThreadCallableDemo();
//执行callable方式,需要FutureTask实现类的支持,用来接收运算结果
        FutureTask<Integer> futureTask = new FutureTask<>(threadCallableDemo);
        new Thread(futureTask).start();


        try {
//当上面的线程执行完后,才会打印结果。跟闭锁一样。所有futureTask也可以用于闭锁
            Integer sum = futureTask.get();
            System.out.println(sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}
class ThreadCallableDemo implements Callable<Integer>{
    private int sum = 0;


    @Override
    public Integer call() throws Exception {
        for(int i=0;i<=100;i+=2){
            sum+=i;
            System.out.println(i);
        }
        return sum;
    }
}

现在Callable接口和实现Runable接口的区别就是,Callable带泛型,其call方法有返回值。使用的时候,需要用FutureTask来接收返回值。而且它也要等到线程执行完调用get方法才会执行,也可以用于闭锁操作。

七、Lock同步锁

引入:

在JDK1.5之前,解决多线程安全问题(通过线程休眠出现的安全问题)有两种方式(sychronized隐式锁):

同步代码块

同步方法

在JDK1.5之后,出现了更加灵活的方式(Lock显式锁):

同步锁

 

Lock需要通过lock()方法上锁,通过unlock()方法释放锁。为了保证锁能释放,所有unlock方法一般放在finally中去执行。

 

之前一直采用synchronized加锁的方式来解决多线程安全问题。

典型例子:售票。

public class LockDemo {
    public static void main(String[] args) {
        LockThread lockThread = new LockThread();


        new Thread(lockThread,"1号窗口").start();
        new Thread(lockThread,"2号窗口").start();
        new Thread(lockThread,"3号窗口").start();
    }
}
class LockThread implements Runnable{
    private int ticket = 50;


    @Override
    public void run() {
        while (true){
                if(ticket>0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                    }
                    System.out.println(Thread.currentThread().getName()+"完成售票,还剩余"+ --ticket);
                }else{
                    break;
                }
        }
    }
}


可以看到。不加锁,多个线程来抢,票数出现了问题。

解决办法1

加synchronized关键字。

@Override
public void run() {
    while (true){
        synchronized (this){
            if(ticket>0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                }
                System.out.println(Thread.currentThread().getName()+"完成售票,还剩余"+ --ticket);
            }else {
                break;
            }
        }
    }
}

解决办法2

Lock锁。Lock lock =new ReentrantLock();

直接创建lock对象,然后用lock()方法上锁,最后用unlock()方法释放锁即可。

Lock lock = new ReentrantLock();

@Override
public void run() {
    while (true){
        lock.lock();
        try {
            if(ticket>0){
                System.out.println(Thread.currentThread().getName()+"完成售票,还剩余"+ --ticket);
            }else{
                break;
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

八、等待唤醒机制

生产消费模式是等待唤醒机制的一个经典案例。

采用同步锁解决

public class ConditionDemo {
    public static void main(String[] args) {
        Shop shop = new Shop();
        Producter producter = new Producter(shop);
        Consumer consumer = new Consumer(shop);
        new Thread(producter,"生产者A").start();
        new Thread(consumer,"消费者B").start();
    }
}
//商店
class Shop{
    private int product = 0;


    //卖货
    public synchronized void sale(){
        if(product<=0){
            System.out.println("产品缺货!");
        }else{
            System.out.println(Thread.currentThread().getName()+"卖货成功,还剩余"+ --product);
        }
    }
    //进货
    public synchronized void get(){
        if(product>=10){
            System.out.println("产品已满!");
        }else{
            System.out.println(Thread.currentThread().getName()+"进货成功,库存有"+ ++product);
        }
    }
}
//生产者
class Producter implements Runnable{
    private Shop shop;
    public Producter(Shop shop) {
        this.shop = shop;
    }


    @Override
    public void run() {
        //进行生产
        for (int i = 0; i < 20; i++) {
            shop.get();
        }
    }
}
//消费者
class Consumer implements Runnable{
    private Shop shop;
    public Consumer(Shop shop) {
        this.shop = shop;
    }


    @Override
    public void run() {
        //进行消费
        for (int i = 0; i < 20; i++) {
            shop.sale();
        }
    }
}

可以看到。上面运行的代码,产品已满了,没有通知消费者可以进行消费。产品缺货了,没有通知生产者可以进行生产。不是我们想要产品满了通知消费者消费,产品缺货了通知生产者生产的结果。

用等待唤醒机制改进:

*Obeject类下常用的方法?

toEquals() toSring() wait() notify() getClass() hashCode()

    //卖货
    public synchronized void sale(){
        if(product<=0){
            System.out.println("产品缺货!");
            try {
                this.wait();//缺货就等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }else{
            System.out.println(Thread.currentThread().getName()+"卖货成功,还剩余"+ --product);
            this.notifyAll();//不缺货就可以卖
        }
    }
    //进货
    public synchronized void get(){
        if(product>=10){
            System.out.println("产品已满!");
            try {
                this.wait();//满货就等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }else{
            System.out.println(Thread.currentThread().getName()+"进货成功,库存有"+ ++product);
            this.notifyAll();//没满就可以进货
        }
    }
}

这样就不会出现上述问题了。没有的时候就生产,生产满了就通知消费,消费完了再通知生产。但是这样还是有点问题,将上述代码做如下改动:

if(product>=1){
    System.out.println("产品已满!");
@Override
public void run() {
    //进行生产
    for (int i = 0; i < 20; i++) {
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        shop.get();
    }
}

就做这两处修改,再次运行,发现虽然结果没问题,但是程序却一直没停下来。出现这种情况是因为有一个线程在等待,而另一个线程没有执行机会了,唤醒不了这个等待的线程了,所以程序就无法结束。

解决办法就是把get和sell方法里面的else去掉,不要用else包起来。

但如果再增加一对消费者和生产者。

new Thread(producter,"生产者AA").start();
new Thread(consumer,"消费者BB").start();

发现出现了负数。

分析:一个消费者线程抢到执行权,发现product是0,就等待,这个时候,另一个消费者又抢到了执行权,product是0,还是等待,此时两个消费者线程在同一处等待。然后当生产者生产了一个product后,就会唤醒两个消费者,发现product是1,同时消费,结果就出现了0和-1。这就是虚假唤醒

解决办法就是if判断改成while

while(product<=0){
while(product>=1){

这样就可以了。

采用Lock锁解决

class Shop2 {
    private int product = 0;//共享数据
    private Lock lock = new ReentrantLock();//创建锁对象
    private Condition condition = lock.newCondition();//获取condition实例


    //卖货
    public void sale(){
        lock.lock();
        try {
            while(product<=0){
                System.out.println("产品缺货!");
                try {
                    /*this.wait();*/
                    condition.await();//缺货就等待
                } catch (InterruptedException e) {
                }
            }
            System.out.println(Thread.currentThread().getName()+"卖货成功,还剩余"+ --product);
            /*this.notifyAll();*/
            condition.signalAll();//不缺货就可以卖
        }finally {
            lock.unlock();
        }
    }
    //进货
    public void get(){
        lock.lock();
        try {
            while(product>=1){
                System.out.println("产品已满!");
                try {
                    /*this.wait();*/
                    condition.await();//满货就等待
                } catch (InterruptedException e) {
                }
            }
            System.out.println(Thread.currentThread().getName()+"进货成功,库存有"+ ++product);
            /*this.notifyAll();*/
            condition.signalAll();//没满就可以进货
        }finally {
            lock.unlock();
        }
    }
}

使用lock同步锁,就不需要sychronized关键字了,需要创建lock对象和condition实例。condition的await()方法、signal()方法和signalAll()方法分别与wait()方法、notify()方法和notifyAll()方法对应。

九、Condition控制线程通信

编写一个程序,开启 3 个线程,这三个线程的 ID 分别为 A、B、C,

每个线程将自己的 ID 在屏幕上打印10 遍,要求输出的结果必须按顺序显示。

如:ABCABCABC…… 依次递归

 

线程本来是抢占式进行的,要按序交替,所以必须实现线程通信,

那就要用到等待唤醒。可以使用同步方法,也可以用同步锁。

public class ABCLoopDemo {
    public static void main(String[] args) {
        Print print = new Print();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 5; i++) {
                    print.printA(i);
                }
            }
        },"A").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 5; i++) {
                    print.printB(i);
                }
            }
        },"B").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 5; i++) {
                    print.printC(i);
                    System.out.println("========================");
                }
            }
        },"C").start();
    }
}
class Print{
    private int num = 1;//当前正在执行的线程的标记
    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();


    public void printA(int totalLoop){
        lock.lock();//采用Lock锁替代synchronized解决多线程安全问题
        try {
            if(num != 1){//判断
                condition1.await();
            }
            for (int i = 1; i <= 1; i++) {
                System.out.println(Thread.currentThread().getName()+"\t"+num+"\t"+totalLoop+"次");
            }
            num = 2;
            condition2.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();//执行完成 必须在finally里解锁
        }
    }
    public void printB(int totalLoop){
        lock.lock();//采用Lock锁替代synchronized解决多线程安全问题
        try {
            if(num != 2){//判断
                condition2.await();
            }
            for (int i = 1; i <= 1; i++) {
                System.out.println(Thread.currentThread().getName()+"\t"+num+"\t"+totalLoop+"次");
            }
            num = 3;
            condition3.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();//执行完成 必须在finally里解锁
        }
    }
    public void printC(int totalLoop){
        lock.lock();//采用Lock锁替代synchronized解决多线程安全问题
        try {
            if(num != 3){//判断
                condition3.await();
            }
            for (int i = 1; i <= 1; i++) {
                System.out.println(Thread.currentThread().getName()+"\t"+num+"\t"+totalLoop+"次");
            }
            num = 1;
            condition1.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();//执行完成 必须在finally里解锁
        }
    }
}

十、线程八锁

同步代码块执行原理?

①线程遇到同步代码块后,线程判断同步锁还有没有。

②有,获取锁,进入同步中去执行。执行完毕,出同步块,把锁对象还回去

②没有,没有锁的线程不能进入同步代码块去执行,被阻挡在同步块外面

*加同步后,线程进同步代码块判断锁,出同步后,释放锁。速度下降。容易阻塞。

*某一时刻内,无论有多少个方法,只能有一个线程持有锁

 

同步方法有锁吗?

有,同步方法中的锁对象是本类对象的引用。This

如果方法是静态的呢?

有,静态方法中,同步锁是本类对象.class

十一、RedWriteLock读写锁

我们在读数据的时候,可以多个线程同时读,不会出现问题,但是写数据的时候,如果多个线程同时写数据,那么到底是写入哪个线程的数据呢?所以,如果有两个线程,写写/读写需要互斥,读读不需要互斥。这个时候可以用读写锁。看例子:

ReadWriteLock rw = new ReentrantReadWriteLock();
rw.readLock().lock();
rw.readLock().unlock();
rw.writeLock().lock();
rw.writeLock().unlock();


public class ReadWriteLockDemo {
    public static void main(String[] args) {
        ReadWriteLockThread readWriteLockThread = new ReadWriteLockThread();
        new Thread(new Runnable() {
            @Override
            public void run() {
                readWriteLockThread.set((int)(Math.random()*100));
            }
        },"write1").start();
        for (int i = 1; i <= 10 ; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    readWriteLockThread.get();
                }
            }).start();
        }
        new Thread(new Runnable() {
            @Override
            public void run() {
                readWriteLockThread.set2((int)(Math.random()*100));
            }
        },"write2").start();
    }
}
class ReadWriteLockThread{
    private int num = 0;
    private ReadWriteLock rw = new ReentrantReadWriteLock();


    //读
    public void get(){
        rw.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()+":"+num);
        }finally {
            rw.readLock().unlock();
        }
    }
    //写1
    public void set(int num){
        rw.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()+":"+num);
            this.num = num;
        }finally {
            rw.writeLock().unlock();
        }
    }
    //写2
    public void set2(int num){
        rw.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()+":"+num);
            this.num = num;
        }finally {
            rw.writeLock().unlock();
        }
    }
}

这个就是读写锁的用法。上面的代码实现了2个线程写,10个线程同时读的操作。


十二、线程池

线程池作用?

并发线程数量很多,每个线程都执行一下就结束了。频繁创建影响效率,使用线程池可以复用,执行完并不被销毁,继续执行其它线程。

public class ThreadPoolExecutorDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //1.创建线程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        List<Future<Integer>> list = new ArrayList<>();


        for (int i = 0; i <= 10; i++) {
            //2.为线程池中的线程分配任务
            Future<Integer> futureTask = executorService.submit(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    int sum = 0;
                    for (int i = 0; i <= 100; i++) {
                        sum+=i;
                    }
                    return sum;
                }
            });
            list.add(futureTask);
        }
        for (Future<Integer> integerFuture : list) {
            System.out.println(integerFuture.get());
        }
        //3.关闭线程池
        executorService.shutdown();
    }
}

通过Executors线程池工具类创建了一个可缓存的线程池。向线程池中调用了一个Callable线程对象,开启10个线程执行累加100操作。最后shutdown关闭线程。

十三、线程调度

ScheduledExecutorService. Schedule();


public <V> ScheduledFuture<V> schedule(Callable<V> callable,
                                       long delay, TimeUnit unit);
public class ScheduledExecutorServiceDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //1.创建线程池
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);


        for (int i = 1; i <= 5; i++) {
            //2.为线程池中的线程分配任务.schedule
            //延迟3秒 执行
            Future<Integer> future = scheduledExecutorService.schedule(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    int num = new Random().nextInt(100);
                    System.out.println(Thread.currentThread().getName()+":"+num);
                    return num;
                }
            },3, TimeUnit.SECONDS);
            System.out.println(future.get());
        }
        //3.关闭线程池
        scheduledExecutorService.shutdown();
    }
}

十四、ForkJoinPool分支/合并框架 工作窃取

ForkJoinPool forkJoinPool = new ForkJoinPool();
public <T> ForkJoinTask<T> submit(ForkJoinTask<T> task) {


ForkJoinTask<Long> submit = forkJoinPool.submit(new CopyForkJoinPoolDemo(0L,100000000L));
public class ForkJoinPoolDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Instant start = Instant.now();


        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Long> submit = forkJoinPool.submit(new CopyForkJoinPoolDemo(0L,100000000L));
        System.out.println(submit.invoke());
        //System.out.println(submit.get());
        Instant end = Instant.now();


        System.out.println("耗费时间为:"+ Duration.between(start,end).toMillis());
        forkJoinPool.shutdown();
    }
    @Test
    public void test1(){
        Instant start = Instant.now();
        long sum = 0L;
        for (long i = 0L; i <= 100000000L; i++) {
            sum+=i;
        }
        System.out.println(sum);
        Instant end = Instant.now();
        System.out.println("耗费时间为:"+ Duration.between(start,end).toMillis());
    }


}
class CopyForkJoinPoolDemo extends RecursiveTask<Long>{// extends递归任务 有返回值


    private static final long serialVersionUID = -9173191152935453018L;


    private Long start;
    private Long end;


    private static final long criticalValue = 1000000L;   //临界值


    public CopyForkJoinPoolDemo(Long start, Long end) {
        this.start = start;
        this.end = end;
    }


    //计算
    @Override
    protected Long compute() {
        long length = end-start;
        if(length<=criticalValue){
            long num = 0;
            for (long i=start; i<=end; i++) {
                num+=i;
            }
            return num;
        }else{
            long middle = (start+end) / 2;
            CopyForkJoinPoolDemo left = new CopyForkJoinPoolDemo(start,middle);
            left.fork();//进行拆分
            CopyForkJoinPoolDemo right = new CopyForkJoinPoolDemo(middle+1,end);
            right.fork();//进行拆分


            return left.join()+right.join();//进行合并
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值