[JUC]Java Util concurrent

Java JUC

​ Java并发编程,在Java 5.0 提供了Java.util.concurrent,在此我们简称JUC。我们希望借此来提高性能,而不是拉低我们的性能。

1、volatile与内存可见性问题

先来看段线程代码

package com.kj.test;

public class TestVolatile {
    public static void main(String[] args) {
        ThreadDemo demo = new ThreadDemo();
        new Thread(demo).start();
        while (true) {
            if (demo.isFlag()) {
                System.out.println("------");
                break;
            }
        }
    }
}

class ThreadDemo implements Runnable {
    private boolean flag = false;

    public void run() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        flag = true;
        System.out.println("flag=" + flag);
    }

    public boolean isFlag() {
        return flag;
    }

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

这段代码运行后,将只输出flag=true,一直保持运行,不会停止

但是循环中判断一直为false
这里就涉及到内存可见性的问题

可见性的问题

JVM会为每一个线程提供独立的缓存用于提高效率
在程序开始执行时,因为分线程sleep了1000单位
主线程更快开始,主线程从主存中读取到数据flag为false
由于while(true)调用的是操作系统底层代码,执行速度十分快速
在分线程改完数据,将数据返回主存后,主线程都没有机会去获取主存中的数据
一直在执行while循环

两个线程都有独立的缓存
两者的数据不可见
产生了内存可见性问题:当多个线程访问共享数据时,彼此数据不可见

解决方法

使用同步锁,每次保证刷新缓存(synchronized)

synchronized (demo){
    if(demo.isFlag()){
        System.out.println("------");
        break;
    }
}

不过用到锁,效率马上就变低了

所以如何在不使用锁的情况下,解决数据可见性问题呢
volatile关键字:保证访问共享数据时,彼此数据是可见的

private volatile boolean flag = false;

使用volatile后,访问共享内存时,线程都回去主存中读取
效率肯定是降低了,不过相比锁的方式,更快了。

volatile真的就比synchronized就好吗

相较于synchronized,volatile是一种较为轻量级的同步策略
但是
1.volatile不具备“互斥性”
2.不能保证变量的“原子性”

2、Atomic类与原子可见性问题

package com.kj.test;

/**
 * 研究原子性的问题
 *
 */
public class TestAtomicDemo {

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

class AtomicDemo implements Runnable {
    private int num = 0;

    public void run() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + ":" + getNum());
    }

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

代码运行后,有些线程将读错数据。

我们是否可以用volatile解决这个问题呢?

不行。因为这里存在多个改写数据,不能保证i++的“原则性”,即过程不能分隔。这里多个线程仍然会出现读到的数据是别的线程已经改过的情况。

解决办法

jdk1.5以后 java.util.concurrent.atomic下提供了常用的原子变量

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KwyVVQZg-1615708063502)(D:\Code\Markdown\image\juc801.png)]

这些变量都可以保证数据可见与原子性。原子变量中,封装的变量使用了volatile修饰。

并且使用CAS(compare-and-swap)算法保证数据的原子性。

CAS 算法是硬件对于并发操作共享数据的支持

CAS中包含三个操作数:

  • 内存值 V
  • 预估值 A
  • 更新值 B

当且仅当 V == A 时,才将B的值赋给V,否则不做任何操作。所以当多个线程对共享数据进行修改时,只有一个线程会成功。这时线程在不能更新数据时,将不会阻塞,再执行一遍。

package com.kj.test;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 研究原子性的问题
 * 解决办法:原子变量
 * jdk1.5以后 java.util.concurrent.atomic下提供了常用的原子变量
 */
public class TestAtomicDemo {

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

class AtomicDemo implements Runnable {
    private AtomicInteger integer = new AtomicInteger();

    public void run() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + ":" + getNum());
    }

    public int getNum() {
        return integer.getAndIncrement();
    }
}

3、ConcurrentHashMap与其他集合类

HashTable是线程安全的,但是效率特别的低。并且对于一些特定的情况,是线程不安全的。

比如:

  • 若不存在则添加
  • 若存在则删除
if(!table.contants()){
	table.put();
}

所以JDK1.5以后,提供了ConcurrentHashMap。采用了锁分段机制。默认有16个段Segment。

多个独立的锁,使得线程访问时,可以并行访问,不一定使用的是同一个段。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UHwAat9C-1615708063505)(D:\Code\Markdown\image\concurrentHahsMap.PNG)]

JDK1.8后将ConcurrentHashMap将分段锁取消了,换成了CAS。

原本线程安全的集合类,依然会保并发异常

package com.kj.test;

import java.util.*;

public class TestCopyOnWriteArrayList {
    public static void main(String[] args) {
        TestThread testThread = new TestThread();

        for (int i = 0; i < 10; i++) {
            new Thread(testThread).start();
        }
    }
    
}

class TestThread implements Runnable{

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

    static{
        list.add("AA");
        list.add("BB");
        list.add("CC");
    }

    public void run() {
        Iterator<String> it = list.iterator();
        //一遍迭代,一遍添加数据
        while(it.hasNext()){
            System.out.println(it.next());
            list.add("AA");
        }
    }
}

java.util.ConcurrentModificationException并发修改异常。这里迭代和加入都是操作的同一个数据源。

iterator多线程修改异常:https://www.cnblogs.com/zhuyeshen/p/10956822.html

可以使用CopyOnWriteArrayList

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

注意:CopyOnWriteArrayList会在每次写入时,都会进行一次复制,开销会很大。所以推荐在迭代次数多的情况下选择使用

4、CountDownLatch与闭锁

juc包中提供了多种并发容器类改进同步容器的性能。

CountDownLatch在完成某些运算时,只有其他线程的运算全部完成,当前运算才继续执行。

来一个情景,我们需要计算一个多线程程序执行的时间。

我们可能写出这样的代码

package com.kj.test;

import java.util.concurrent.CountDownLatch;

public class TestCountDownLatch {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        for (int i = 0; i < 10; i++) {
            new Thread(demo).start();
        }
        long end = System.currentTimeMillis();
        System.out.println("消耗时间为:" + (end - start));
    }
}

class LatchDemo implements Runnable {

    public void run() {
        for (int i = 0; i < 50000; i++) {
            if (i % 2 == 0) {
                System.out.println(i);
            }
        }
    }
}

但是这个时间并不是我们想要的,这个是主线程执行结束的时间,其他的线程可能依然在执行。

我们希望主线程等其他线程都执行结束,才开始继续运行。

package com.kj.test;

import java.util.concurrent.CountDownLatch;

public class TestCountDownLatch {
    public static void main(String[] args) {
        //当CountDownLatch为0时,代表全部线程执行完
        int threadNum = 10;
        final CountDownLatch latch = new CountDownLatch(threadNum);
        LatchDemo demo = new LatchDemo(latch);
        long start = System.currentTimeMillis();
        for (int i = 0; i < threadNum; i++) {
            new Thread(demo).start();
        }
        try {
            latch.await(); //latch不为0将一直等待
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        System.out.println("消耗时间为:" + (end - start));
    }
}

class LatchDemo implements Runnable {
    private CountDownLatch latch;

    public LatchDemo(CountDownLatch latch) {
        this.latch = latch;
    }

    public void run() {
        synchronized (this){
            try{
                for (int i = 0; i < 50000; i++) {
                    if (i % 2 == 0) {
                        System.out.println(i);
                    }
                }
            }finally {
                //每一个线程执行完时,我们将CountDownLatch-1。
                latch.countDown();
            }
        }
    }
}

5、Callable与创建线程

这里我们提出新的一种创建线程的方式Callable。

我们可以与Runable接口进行对比下。

我们原本的方案:

class ThreadDemo1 implements Runnable{
    public void run() {

    }
}

现在我们使用Callable编写创建线程

package com.kj.test;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * 创建执行线程的方式三:实现Callable接口
 */
public class _05_TestCreateThread {
    public static void main(String[] args) {
        TestCallable callable = new TestCallable();
        // 相较与Runable方式,可以有返回值,并且可以抛出异常
        // 执行Callable方式,需要FutureTask实现类的支持,用于接受运算结果。
        // FutureTask是Future接口的实现类
        FutureTask<Integer> res = new FutureTask(callable);
        new Thread(res).start();
        try {
            // 在线程执行过程中,get并没有执行.
            // 所以FutureTask 也可以用于闭锁
            Integer sum = res.get();
            System.out.println("-------"+sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

class TestCallable implements Callable<Integer> {

    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 0; i < 100; i++) {
            sum += i;
        }
        return sum;
    }
}

可以看到的是Callable带有一个泛型。并且call()方法带有返回值。

通过运行程序,查看结果我们可以知道在线程执行过程中,主线程中的get()方法并没有执行,而是等待整个其他线程执行结束,才执行。所以FutureTask 也可以用于闭锁

6、Lock同步锁

我们上锁的方法有三种

synchronized(隐式的锁),有jvm底层完成

  • 同步代码块
  • 同步方法

jdk1.5后:

  • 同步锁Lock

Lock可以显示的上锁与开锁。通过lock()方法上锁,必须通过unlock()方法进行解锁。

unlock()通常放在finally中,避免遇到异常使程序逻辑异常。

package com.kj.test;

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

public class _06_TestLock {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        new Thread(ticket, "一号").start();
        new Thread(ticket, "二号").start();
        new Thread(ticket, "三号").start();
    }
}

class Ticket implements Runnable {

    private int tick = 100;

    private Lock lock = new ReentrantLock();

    public void run() {
        while (tick > 0) {
            lock.lock(); // 上锁
            try {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "完成售票,余票为:" + --tick);
            }finally {
                lock.unlock(); // 释放锁
            }
        }
    }

}

7、线程有序

需求:这里有三个线程,ID分别为ABC,每个线程在控制台打印自己ID,要求输出结果有序。
如:ABCABC…

package com.kj.test;

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

public class _09_TestABCThread {
    public static void main(String[] args) {
        final AlternateDemo demo = new AlternateDemo();

        new Thread(new Runnable() {
            public void run() {
                for (int i = 0; i < 20; i++) {
                    demo.loopA(i);
                }
            }
        }, "A").start();

        new Thread(new Runnable() {
            public void run() {
                for (int i = 0; i < 20; i++) {
                    demo.loopB(i);
                }
            }
        }, "B").start();

        new Thread(new Runnable() {
            public void run() {
                for (int i = 0; i < 20; i++) {
                    demo.loopC(i);
                }
            }
        }, "C").start();
    }
}

class AlternateDemo {
    // 当前正在执行线程标记
    private int number = 1;

    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();

    public void loopA(int totalLoop) {
        lock.lock();
        try {
            // 判断
            if (number != 1) {
                try {
                    condition1.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + ":" + i + "." + totalLoop);
            }
            number = 2;
            condition2.signal();
        } finally {
            lock.unlock();
        }
    }

    public void loopB(int totalLoop) {
        lock.lock();
        try {
            // 判断
            if (number != 2) {
                try {
                    condition2.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + ":" + i + "." + totalLoop);
            }
            number = 3;
            condition3.signal();
        } finally {
            lock.unlock();
        }
    }

    public void loopC(int totalLoop) {
        lock.lock();
        try {
            // 判断
            if (number != 3) {
                try {
                    condition3.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + ":" + i + "." + totalLoop);
            }
            number = 1;
            condition1.signal();
        } finally {
            lock.unlock();
        }
    }
}

我们需要三个进程间进行通信,在A打印完后通知B,并开锁

8、读写锁

对于写写、读写操作需要互斥

对于读读操作不需要互斥

ReadWirteLock维护了一对锁,一个读锁,一个写锁。写锁是独占的,而读锁是可以多个线程占有。

package com.kj.test;

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class _10_TestReadWriteLock {
    public static void main(String[] args) {
        final ReadWriteDemo rw = new ReadWriteDemo();
        new Thread(new Runnable() {
            public void run() {
                try {
                    //控制线程在中间写入
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                rw.wirte((int) (Math.random() * 10));
            }
        }, "write").start();

        for (int i = 0; i < 100; i++) {
            new Thread(new Runnable() {
                public void run() {
                    rw.read();
                }
            }).start();
        }
    }
}

class ReadWriteDemo {

    private int number = 0;

    private ReadWriteLock lock = new ReentrantReadWriteLock();

    // 读
    public void read() {
        lock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + ":" + number);
        } finally {
            lock.readLock().unlock();
        }
    }

    // 写
    public void wirte(int number) {
        lock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName());
            this.number = number;
        } finally {
            lock.writeLock().unlock();
        }
    }
}

9、线程八种锁

  1. 两个普通同步方法,两个线程,标准打印
package com.kj.test;

public class _11_TestThread8Monitor {
    public static void main(String[] args) {
        final Number num = new Number();

        new Thread(new Runnable() {
            public void run() {
                num.getOne();
            }
        }).start();

        new Thread(new Runnable() {
            public void run() {
                num.getTwo();
            }
        }).start();
    }
}

class Number {

    public synchronized void getOne() {
        System.out.println("one");
    }

    public synchronized void getTwo() {
        System.out.println("two");
    }
}

打印:one two

  1. 新增Thread.sleep()给getOne()
package com.kj.test;

public class _11_TestThread8Monitor {
    public static void main(String[] args) {
        final Number num = new Number();

        new Thread(new Runnable() {
            public void run() {
                num.getOne();
            }
        }).start();

        new Thread(new Runnable() {
            public void run() {
                num.getTwo();
            }
        }).start();
    }
}

class Number {

    public synchronized void getOne() {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("one");
    }

    public synchronized void getTwo() {
        System.out.println("two");
    }
}

打印:one two

  1. 新增普通方法getThree()
package com.kj.test;

public class _11_TestThread8Monitor {
    public static void main(String[] args) {
        final Number num = new Number();

        new Thread(new Runnable() {
            public void run() {
                num.getOne();
            }
        }).start();

        new Thread(new Runnable() {
            public void run() {
                num.getTwo();
            }
        }).start();


        new Thread(new Runnable() {
            public void run() {
                num.getThree();
            }
        }).start();
    }
}

class Number {

    public synchronized void getOne() {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("one");
    }

    public synchronized void getTwo() {
        System.out.println("two");
    }

    public void getThree() {
        System.out.println("three");
    }
}

打印:three one two

  1. 两个普通同步方法,两个Number对象
package com.kj.test;

public class _11_TestThread8Monitor {
    public static void main(String[] args) {
        final Number num1 = new Number();
        final Number num2 = new Number();

        new Thread(new Runnable() {
            public void run() {
                num1.getOne();
            }
        }).start();

        new Thread(new Runnable() {
            public void run() {
                num2.getTwo();
            }
        }).start();

    }
}

class Number {

    public synchronized void getOne() {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("one");
    }

    public synchronized void getTwo() {
        System.out.println("two");
    }

}

打印:two one

  1. static修饰getOne(),一个Number对象
package com.kj.test;

public class _11_TestThread8Monitor {
    public static void main(String[] args) {
        final Number num1 = new Number();
        //final Number num2 = new Number();

        new Thread(new Runnable() {
            public void run() {
                num1.getOne();
            }
        }).start();

        new Thread(new Runnable() {
            public void run() {
                num1.getTwo();
                //num2.getTwo();
            }
        }).start();

    }
}

class Number {

    public static synchronized void getOne() {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("one");
    }

    public synchronized void getTwo() {
        System.out.println("two");
    }

    public void getThree() {
        System.out.println("three");
    }
}

打印:two one

  1. static修饰getOne()、getTwo(),一个Number对象
package com.kj.test;

public class _11_TestThread8Monitor {
    public static void main(String[] args) {
        final Number num1 = new Number();
        //final Number num2 = new Number();

        new Thread(new Runnable() {
            public void run() {
                num1.getOne();
            }
        }).start();

        new Thread(new Runnable() {
            public void run() {
                num1.getTwo();
                //num2.getTwo();
            }
        }).start();

    }
}

class Number {

    public static synchronized void getOne() {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("one");
    }

    public static synchronized void getTwo() {
        System.out.println("two");
    }

    public void getThree() {
        System.out.println("three");
    }
}

打印:one two

  1. 一个静态同步方法,一个非静态同步方法;两个Number对象,一个GetOne、一个GetTwo
package com.kj.test;

public class _11_TestThread8Monitor {
    public static void main(String[] args) {
        final Number num1 = new Number();
        final Number num2 = new Number();

        new Thread(new Runnable() {
            public void run() {
                num1.getOne();
            }
        }).start();

        new Thread(new Runnable() {
            public void run() {
                //num1.getTwo();
                num2.getTwo();
            }
        }).start();

    }
}

class Number {

    public static synchronized void getOne() {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("one");
    }

    public synchronized void getTwo() {
        System.out.println("two");
    }

    public void getThree() {
        System.out.println("three");
    }
}

打印:two one

  1. 两个静态同步方法;两个Number对象,一个GetOne、一个GetTwo
package com.kj.test;

public class _11_TestThread8Monitor {
    public static void main(String[] args) {
        final Number num1 = new Number();
        final Number num2 = new Number();

        new Thread(new Runnable() {
            public void run() {
                num1.getOne();
            }
        }).start();

        new Thread(new Runnable() {
            public void run() {
                //num1.getTwo();
                num2.getTwo();
            }
        }).start();

    }
}

class Number {

    public static synchronized void getOne() {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("one");
    }

    public static synchronized void getTwo() {
        System.out.println("two");
    }

    public void getThree() {
        System.out.println("three");
    }
}

打印:one two

线程八锁关键:

  • 非静态方法的锁为 this,静态方法锁为对应的Class实例
  • 在某个时刻内,只有某一个线程持有锁,无论是有几个方法

10、线程池

线程池提供了一个线程队列,线程池中保存了所有等待状态的线程

java.util.concurrent.Executor:负责线程的使用与调度的根接口

​ |–ExecutorService 子接口:线程池的主要接口

​ |–ThreadPoolExecutor 线程池的实现类

​ |–ScheduledExecutorService 子接口:负责线程调度

​ |–ScheduledThreadPoolExecutor:继承了ThreadPoolExecutor ,实现了ScheduledExecutorService

工具类:Executors

ExecutorService newFixedThreadPool(int size) 创建固定大小的连接池

ExecutorService newCachedThreadPool() 缓存线程池,数量不固定,可以根据需求自动的更改数据

ExecutorService newSingleThreadExecutor() 创建单个线程池。线程池中只有一个线程

ScheduledExecutorService newScheduledThreadPool() 创建固定大小的线程池,延迟或者定时的执行任务

package com.kj.test;

import java.util.concurrent.*;

public class _12_TestThreadPool {

    public static void main(String[] args) {
        // 1. 创建线程池
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        // 2.为线程池中的线程分配任务
        ThreadPoolDemo demo = new ThreadPoolDemo();
        for (int i = 0; i < 10; i++) {
            //executorService.submit(demo);
            Future<Integer> res = executorService.submit(new Callable<Integer>() {
                public Integer call() throws Exception {
                    int i = 0;
                    while (i <= 100) {
                        System.out.println(Thread.currentThread().getName() + ":" + i++);
                    }
                    return i;
                }
            });
            try {
                System.out.println(res.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
        // 3. 关闭线程池。shutdown等待所有线程池中的任务都关闭后,再关闭
        executorService.shutdown();
    }
}

class ThreadPoolDemo implements Runnable {
    private int i = 0;

    public void run() {
        while (i <= 100) {
            System.out.println(Thread.currentThread().getName() + ":" + i++);
        }
    }
}

使用线程调度ScheduledExecutorService

package com.kj.test;

import java.util.Random;
import java.util.concurrent.*;

/**
 * 调度
 */
public class _13_TestScheduledThreadPool {
    public static void main(String[] args) {
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);
        // 延迟三秒执行Callable
        for (int i = 0; i < 10; i++) {
            Future<Integer> future =  pool.schedule(new Callable<Integer>() {

                public Integer call() throws Exception {
                    int num = new Random().nextInt(100);
                    System.out.println(Thread.currentThread().getName() + ":" + num);
                    return num;
                }
            }, 1, TimeUnit.SECONDS);
            try {
                System.out.println(future.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
        pool.shutdown();
    }
}

11、分支合并ForkJoinPool

可以将一个大任务拆分成一个个小任务,然后将小任务的结果一个个合并。

采用“工作窃取”模式(work-stealing) :当执行新的任务时它可以将其拆分分成更小的任务执行,并将小任务加到线程队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队列中。

拆分和合并是需要时间的,如果工程量不大,可以直接运算。

package com.kj.test;

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;

public class _14_TestForkJoinPool {
    public static void main(String[] args) {
        ForkJoinPool pool = new ForkJoinPool();
        ForkJoinTask<Long> demo = new ForkJoinDemo(0L, 100000000L);
        Long sum = pool.invoke(demo);
        System.out.println(sum);
    }
}

class ForkJoinDemo extends RecursiveTask<Long> {

    private long start;
    private long end;

    private static final long THRSHOLD = 10000L; //临界值

    public ForkJoinDemo(long start, long end) {
        this.start = start;
        this.end = end;
    }

    protected Long compute() {
        long length = end - start;
        if (length <= THRSHOLD) {
            long sum = 0L;
            for (long i = start; i <= end; i++) {
                sum += i;
            }
            return sum;
        } else {
            long middle = (start + end) / 2;
            ForkJoinDemo culculate1 = new ForkJoinDemo(start, middle);
            culculate1.fork(); //进行拆分,同时加入线程队列

            ForkJoinDemo culculate2 = new ForkJoinDemo(middle + 1, end);
            culculate2.fork();
            //合并
            return culculate1.join() + culculate2.join();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值