文章目录
JUC基本知识点
所谓juc就是 java.util.concurrent 在并发编成中使用的工具包。
线程的状态:
- 新建 (new) new一个线程对象
- 就绪 (Runnable)调用线程的start方法,处于可运行状态,等待获取cpu使用权
- 运行 (Running)执行程序代码
- 阻塞 (Blocked) 放弃cpu的执行权
- 销毁 (Dead)线程执行完或异常退出run方法,结束生命周期
阻塞分三种:
- 等待阻塞:运行的线程执行wait方法后,会释放占用资源,JVM把该线程放入“等待池”中。进入这个状态后,是不能自我唤醒的,需要其他线程唤醒(notify、notifyAll)。
- 同步阻塞:运行的线程在获取锁的对象时,发现锁已经被占用了,JVM会把该线程放入锁池中。
- 其他阻塞:运行线程的sleep、join或者IO请求阻塞。JVM会把该线程置为阻塞状态,当sleep超时、join等待终止或者超时、IO处理完毕,JVM会把该线程置为就绪状态
wait、sleep的区别
- sleep是Thread的静态方法,wait是Object的方法,任何对象实例都能调用。
- sleep不会释放琐,他也不需要占用锁。wait会释放锁,但调用它前提是当前线程占用琐(即代码要在synchronized中)。
- 它们都能被interrupted方法打断。
管程
就是monitor监视器,琐。是一种同步机制,保证同一时间只能有一个线程对保护数据或代码进行访问。
jvm同步基于进入和退出,使用管制对象实现。
用户线程、守护线程
用户线程:平时使用的线程或自定义的线程,主线程结束,用户线程运行,jvm存活。
守护线程:gc垃圾回收线程,运行在在后台,没有用户线程,都是守护线程,jvm结束。
lock接口
synchronized关键字
可以用来:修饰一个代码块、修饰一个方法(但是synchronized关键字不能被继承,子类重写父类的方法并不是同步的)、修饰一个静态方法、修饰一个类。
class Ticket {
private int number = 30;
public synchronized void sale() {
if (number > 0) {
System.out.println(Thread.currentThread().getName()+" 卖出第" + (number--) + "张票" + ",剩余票的数量为" + number);
}
}
public void method(){
synchronized(Ticket.class){
// todo
}
}
public void methodA(){
synchronized (this){
// todo
}
}
}
synchronized主动释放锁的两种情况:
- 获取琐的线程执行完该代码,然后释放对琐的占有
- 线程执行发生异常,此时jvm会让线程自动释放琐
Lock
lock与synchronized的区别:
- synchronized是关键字,Lock是一个接口
- synchronized自动释放锁,Lock需要用户手动释放锁,不释放可能会导致死锁问题。
- lock可以让等待琐的线程响应中断,synchronized不行。
- lock可以知道是否成功获取锁
- 在大量请求竞争下,lock性能远大于synchronized
class LTicket {
private int number = 30;
private final ReentrantLock lock = new ReentrantLock();
public void Sale() {
lock.lock();
try {
if (number > 0) {
System.out.println(Thread.currentThread().getName() + " 卖出第" + (number--) + "张票,还剩" + number);
}
} finally {
lock.unlock();
}
}
}
wait虚假唤醒
虚假唤醒:下面代码中wait在执行等待时,当被其它线程唤醒,那么它就不会再执行判断,二是接着向下执行。
出现线程阻塞等问题。解决方案if改用while,唤醒后执行判断。
wait在哪里睡,就在那里醒
public synchronized void incr() throws InterruptedException {
//判断 执行 通知
if (number != 0) {
this.wait(); //在哪里“睡”,就在那里“醒”
}
number++;
System.out.println(Thread.currentThread().getName() + " :: " + number);
//通知唤醒其它线程
this.notifyAll();
}
public synchronized void decr() throws InterruptedException {
if (number != 1) {
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName() + " :: " + number);
this.notifyAll();
}
定制化通信
通过flag判断
class ShareResource{
//定制化标志
private int flag = 1;
private ReentrantLock reentrantLock = new ReentrantLock();
private Condition c1 = reentrantLock.newCondition();
private Condition c2 = reentrantLock.newCondition();
private Condition c3 = reentrantLock.newCondition();
public void print5(int loop) throws InterruptedException {
reentrantLock.lock();
try {
while(flag != 1){
c1.await();
}
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName()+" : "+ i + "轮数" + loop);
}
//通知
flag = 2;
c2.signal();
} finally {
reentrantLock.unlock();
}
}
public void print10(int loop) throws InterruptedException {
reentrantLock.lock();
try {
while(flag != 2){
c2.await();
}
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName()+" : "+ i + "轮数" + loop);
}
//通知
flag = 3;
c3.signal();
} finally {
reentrantLock.unlock();
}
}
public void print15(int loop) throws InterruptedException {
reentrantLock.lock();
try {
while(flag != 3){
c3.await();
}
for (int i = 1; i < 16; i++) {
System.out.println(Thread.currentThread().getName()+" : "+ i + " 轮数" + loop);
}
//通知
flag = 1;
c1.signal();
} finally {
reentrantLock.unlock();
}
}
}
public class CustomNotice {
public static void main(String[] args) {
ShareResource shareResource = new ShareResource();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
shareResource.print5(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"AA").start();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
shareResource.print10(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"BB").start();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
shareResource.print15(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"CC").start();
}
}
集合线程安全
ArrayList的add方法:并没有加锁,线程不安全
并发增加代码:注意这里是取数据的时候发生异常
for (int i = 0; i < 10; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(list);
},String.valueOf(i)).start();
}
并发修改异常
Vector解决
读写都加琐
List<String> list = new Vector<>();
增加关键字synchronized,解决线程安全问题,但是效率低下,使用少
Collections解决
List<String> list = Collections.synchronizedList(new ArrayList<>());
CopyOnWriteArrayList解决
List<String> list = new CopyOnWriteArrayList<>();
写式复制技术:只在写加锁
源码
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
获取原有数组和长度,
copy原数组,不过长度+1
将新数据填入复制数据,
最后将复制数组 赋值给原数组
hashSet
使用CopyOnWriteArraySet
解决
线程不安全:
Set<String> set = new CopyOnWriteArraySet<>();
// Set<String> set = new HashSet<>();
for (int i = 0; i < 100; i++) {
new Thread(() -> {
set.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(set);
},String.valueOf(i)).start();
}
Hashset底层就是Hashmap,他是往map中放数据,所以key不能重复。(无序)
hashMap
使用 ConcurrentHashMap
解决
// Map<String, String> map = new HashMap<>();
ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
for (int i = 0; i < 100; i++) {
int key = i;
new Thread(() -> {
map.put(String.valueOf(key),UUID.randomUUID().toString().substring(0,8));
System.out.println(map);
},String.valueOf(i)).start();
}
多线程锁
悲观锁: 在获取资源数据后认为其它线程会修改
该数据,因此在获取数据时会先加锁,确保数据不被修改。适合写操作多的场景。
乐观锁: 在获取资源数据后认为其它线程不会修改
该数据,在java中是通过无锁编程实现,只是在更新数据时去判断数据是否被更改。如果被更改则会重试枪锁、放弃修改等操作。判断规则:1、版本号,2、CAS算法,Java原子类中递增操作就通过CAS实现。适合读操作多的场景。
synchronized同步代码块:实现使用的是monitorenter和monitorexit指令,一个enter两个exit(保证异常时也能释放锁)
synchronized普通同步方法:字节码中含有ACC_SYNCHRONIZED访问标识,执行线程会先持有monitor锁,再执行方法,最后无论正常或非正常完成都会释放锁。
synchronized静态同步方法:字节码含有ACC_STATIC,ACC_SYNCHRONIZED访问标识。
锁的8种情况
对象锁: synchronized 加在普通方法上锁的是调用此方法的对象(this),
类锁(Class): synchronized 加在静态方法上锁的是当前类的字节码中大Class
静态锁与类锁不会竞争锁,它俩锁的东西不一样。
class Phone{
public static synchronized void sendMSM() throws InterruptedException {
TimeUnit.SECONDS.sleep(4);
System.out.println("------sendSMS");
}
public synchronized void sendEmail() throws InterruptedException {
System.out.println("------sendEmail");
}
public void getHello() {
System.out.println("------getHello");
}
}
/**
* 1、标准访问 2、sendMSM停留4秒 3、通过同一对象调用getHello 4、创建两个phone对象 为了证明使用普通同步方法锁的是调用此方法的对象实例。
*
* 5、两个静态同步方法,两个线程使用一个对象调用两种方法 6、两个静态同步方法,两个线程使用两个对象调用两种方法 静态同步方法锁的是当前类(字节码中的大Class)
*
* 7、一个静态同步,一个普通同步,一个对象
* 8、一个静态同步,一个普通同步,两个对象
*/
public class Lock_8 {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
Phone phone1 = new Phone();
new Thread(() -> {
try {
phone.sendMSM();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"AA").start();
Thread.sleep(100);
new Thread(() -> {
try {
phone1.sendEmail();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"BB").start();
}
}
公平锁与非公平锁
在new ReentrantLock()创建Lock对象时,使用无参构造就是非公平锁,有参构造(布尔类型传入true)就是公平锁。
公平锁:private final ReentrantLock lock = new ReentrantLock(true);
为什么会有公平锁/非公平锁?
- 非公平锁能更充分利用CPU时间片,减少CPU空闲状态时间, 释放锁的线程更容易再次获取锁,减少了线程切换的开销效率高。
源码:
可重入锁(lock synchronized)
可重入锁我理解为,如家外面门的锁与家内房间的锁一致,拿到一把就可以畅通无阻。
下面代码如果synchronized不是可重复锁的话,那么会等到最外层同步代码块执行结束才能释放锁,产生死锁问题。
Object o = new Object();
new Thread(() -> {
synchronized(o){
System.out.println(Thread.currentThread().getName()+" 外锁");
synchronized(o) {
System.out.println(Thread.currentThread().getName() + " 中锁");
synchronized(o) {
System.out.println(Thread.currentThread().getName() + " 内锁");
}
}
}
},"t1").start();
每一个锁对象拥有一个锁计数器和一个指向持有该锁线程的指针。
重复获取锁有一个锁计数器,同一个对象获取(monitorenter)同一个锁时+1,离开(monitorexit)时-1,到0就完全释放锁。
lock重复获取锁时,一定要释放锁(上锁解锁)。否则其他线程无法获取这把锁(造成死锁现象)。
为什么任何一个对象都可以成为一个锁。
所有的类都继承Object类,java底层由c++支持,由objectMonitor.hpp文件控制。
_owner:锁定当前进程ID
_count:表示当前锁对象被加锁的次数(加锁+1,解锁-1),为0时未加锁。
_recursions:初始值为0,表示同步代码块重入的次数
_EntryList:阻塞队列,存放阻塞的线程
_WaitSet:等待队列,存放等待的线程
死锁
形成死锁四大必要条件:
- 互斥条件
- 占有且等待
- 不可抢占(剥夺)
- 循环等待
检测死锁:jps + jstack PID jconsole => 线程 => 检测死锁
两个或两个以上的线程在执行过程中,因为争夺资源而造成一种互相等待的现象,如果没有外力干涉,他们无法再执行下去。
产生死锁的原因:系统资源不足、进程运行推进顺序不合适、资源分配不当
Object o1 = new Object();
Object o2 = new Object();
new Thread(() -> {
synchronized (o1) {
try {
TimeUnit.MILLISECONDS.sleep(100);
synchronized (o2) {
System.out.println();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t1").start();
new Thread(() -> {
synchronized (o2) {
try {
TimeUnit.MILLISECONDS.sleep(100);
synchronized (o1) {
System.out.println();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t2").start();
Callable接口
Callable与Runnable相比较:它有返回值、异常抛出,它的执行方法是call()
class MyThread2 implements Callable {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName()+" come in Callable");
return 200;
}
}
public class Demo1 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<Integer> futureTask1 = new FutureTask<>(new MyThread2());
//lambda表达式
FutureTask<Integer> futureTask2 = new FutureTask<>(() -> {
System.out.println(Thread.currentThread().getName()+" come in Callable");
return 1024;
});
new Thread(futureTask1,"AA").start();
System.out.println(futureTask1.get()+" come over");
}
}
juc辅助类
减少计数CountDownLatch
有参构造赋初始值,设定执行那一次任务后减一,直到初始值为0时,await方法停止等待。
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(100);
for (int i = 1; i <= 100; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+" 离开了");
countDownLatch.countDown();
},String.valueOf(i)).start();
}
countDownLatch.await();
System.out.println(Thread.currentThread().getName()+" 离开了");
}
}
循环栅栏CyclicBarrier
允许一组线程相互等待,直到达到某个公共屏障点。到达某个条件就会执行CyclicBarrier的第二个参数的实现Runnable接口中的方法。
public class CyclicBarrierDemo {
private static final int NUMBER = 7;
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER, () -> {
System.out.println("集齐7颗龙珠召唤神龙");
});
for (int i = 1; i <= 7; i++) {
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName()+" 星被收集到了");
//执行等待
cyclicBarrier.await();
} catch (Exception e) {
e.printStackTrace();
}
},String.valueOf(i)).start();
}
}
}
信号灯Semaphore
一个计数信号量,PV操作。只有拿到许可的线程才能执行代码,未拿到的只能执行等待其他线程释放许可,如3个车位6辆车来停车。
public class SemaphoreDemo {
public static void main(String[] args) {
//创建Semaphore,设置许可数量
Semaphore semaphore = new Semaphore(3);
//模拟6辆汽车
for (int i = 1; i <= 6; i++) {
new Thread(()->{
try {
//获取许可证(抢占车位)
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+" 抢到了车位");
TimeUnit.SECONDS.sleep(new Random().nextInt(5));
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//释放许可证
semaphore.release();
System.out.println(Thread.currentThread().getName()+ " ---------离开了车位");
}
},String.valueOf(i)).start();
}
}
}
读写锁
速度快于普通ReentrantLock
读写锁:一个资源可以被多个读线程
访问,只能被一个写线程
访问,不能同时存在读写操作,(不同线程)读写互斥,读读共享。
写锁饥饿:读操作没有完成时其他线程无法获取写锁。
锁降级:同一线程,在获取写锁完成写操作后再获取读锁进行去操作,释放写锁,然后现在就降级为读锁。
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
lock.writeLock().lock();
System.out.println("lock");
//TODO
lock.readLock().lock();
System.out.println("read");
lock.writeLock().unlock();
//TODO
lock.readLock().unlock();
写锁:独占锁,只允许一个线程执行操作
读锁:共享锁,允许多个线程同时执行
读写锁缺点: 1、造成(写)锁饥饿,一直读没有写操作。就是读操作占用大量资源,写操作无法进行(如高峰期坐地铁,却只有你一个人下车)。2、读的时候不能写,只有读完才能写.
- 在线程持有读锁的情况下,该线程不能取得写锁(因为获取写锁的时候,如果发
现当前的读锁被占用,就马上获取失败,不管读锁是不是被当前线程持有)。 - 在线程持有写锁的情况下,该线程可以继续获取读锁(获取读锁时如果发现写
锁被占用,只有写锁没有被当前线程占用的情况才会获取失败)。 - 原因: 当线程获取读锁的时候,可能有其他线程同时也在持有读锁,因此不能把
获取读锁的线程“升级”为写锁;而对于获得写锁的线程,它一定独占了写
锁,因此可以继续让它获取读锁,当它同时获取了写锁和读锁后,还可以先释
放写锁继续持有读锁,这样一个写锁就“降级”为了读锁。
通过 new ReentrantReadWriteLock()实现读写锁
class MyCache {
private volatile Map<String, Object> map = new HashMap<>();
//读写锁
private ReadWriteLock rwLock = new ReentrantReadWriteLock();
//写数据
public void put(String key, Object value) {
rwLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " 正在执行写操作" + value);
TimeUnit.MILLISECONDS.sleep(300);
map.put(key, value);
System.out.println(Thread.currentThread().getName() + " 写完了" + value);
} catch (InterruptedException e) {
} finally {
rwLock.writeLock().unlock();
}
}
//读数据
public Object get(String key) {
Object result = null;
rwLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " 正在执行读取操作" + key);
TimeUnit.MILLISECONDS.sleep(300);
result = map.get(key);
System.out.println(Thread.currentThread().getName() + " 读取完了" + key);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
rwLock.readLock().unlock();
return result;
}
}
}
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCache myCache = new MyCache();
for (int i = 1; i <= 5; i++) {
final String key = i + "", value = i + "";
new Thread(() -> {
myCache.put(key, value);
}, String.valueOf(i)).start();
}
for (int i = 1; i <= 5; i++) {
final String key = i + "", value = i + "";
new Thread(() -> {
myCache.get(key);
}, String.valueOf(i)).start();
}
}
}
StampedLock(版本锁,邮戳锁)
速度块于ReentrantLock,主要升级是读的过程中也允许锁的介入。
对于短的只读代码段,使用乐观模式通常可以减少征用提高吞吐量。
三种模式:
- Reading(悲观读模式),与ReentrantReadWriteLock的读锁类似。
- Writing(写模式),功能与ReentrantReadWriteLock的写锁类似。
- Optimistic reading(乐观读模式),无锁机制,类似于数据库中的乐观锁。支持读写并发,很乐观的认为读的时候没人修改,假如被修改升级到悲观读模式。
缺点:
4. 但不可重入
5. 悲观读锁和写锁不支持条件变量(condition),
6. 使用StampedLock不要调用interrupt()中断操作。
validate(long stamp)方法:返回true表示无修改邮戳。
tryOptimisticRead()方法:乐观读
很乐观的认为读的时候没人修改,假如被修改升级到悲观读模式。
public void read() throws InterruptedException {
long stamp = stampedLock.tryOptimisticRead();
//TODO 模拟读操作
System.out.println(Thread.currentThread().getName() + "\t" + "尝试读取");
Thread.sleep(4000);
long result = number.sum();
if (!stampedLock.validate(stamp)) {//判断这4秒中是否有人修改这个值
System.out.println(Thread.currentThread().getName()+"\t被修改");
try {
stamp = stampedLock.readLock();
//TODO 模拟重新读取
System.out.println(Thread.currentThread().getName() + "\t" + "尝试读取");
result = map.get(version.sum() - 1);
System.out.println("被修改\t"+Thread.currentThread().getName() + "\t" + "读取完成" + "\t" +result);
}finally {
stampedLock.unlockRead(stamp);
}
} else
System.out.println("未被修改\t" + result);
}
阻塞队列
当队列是空的,从队列中获取元素将会被阻塞。
当队列是满的,向队列中添加元素将会被阻塞。
在多线程领域:所谓阻塞,在某些情况下会挂起线程(即阻塞),一旦条件满足,被挂起
的线程又会自动被唤起
第一组在使用时出现:队列满添加,队列空移除,队列空检查会抛异常。
第二组:成功返回true,失败返回false,检查空返回null
第三组:队列满添加、队列空移除会进入阻塞
第四组:和第三组一样,不过能设置阻塞时间
线程池ThreadPoll
基础知识点
线程池做的工作只要是控制运行的线程数量,处理过程中将任
务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量,
超出数量的线程排队等候,等其他线程执行完毕,再从队列中取出任务来执行。
- 优势:降低资源消耗、提高响应速度、提高线程可管理性
Executors.newFixedThreadPool(x) 创建x个线程
Executors.newSingleThreadExecutor() 创建单个线程
Executors.newCachedThreadPool() 创建可扩容的线程
使用:
try{
for (int i = 1; i <= 10; i++) {
executorService.execute(() -> {
System.out.println(Thread.currentThread().getName() + "办理业务");
});
}
}finally {
//关闭线程池,不关闭线程则不结束执行
executorService.shutdown();
}
三种创建方式的底层都是new ThreadPoolExecutor 对象
defaultHandler:默认拒绝策略
在execute()时才会创建线程
拒绝策略
超过最大线程数量、队列已经满了。
- AbortPolicy(默认) :直接抛出RejectedExecutionException异常阻止系统正常运行
- CallerRunsPolicy :”调用者运行“一种调节机制.该策略既不会抛弃任务,也不
会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量。 - DiscardOldestPolicy :抛弃队列中等待最久的任务,然后把当前任务加人队列中
尝试再次提交当前任务。 - DiscardPolicy :该策略默默地丢弃无法处理的任务。不予任何处理也不抛出异常。
如果允许任务丢失。这是最好的一种策略。
自定义线程池
实际开发中不用上面三种方式创建线程池,而是自定义。
原因:
- FixedThreadPool和SingleThreadPool:允许请求队列的长度为 Integer.MAX_VALUE,面临大量请求会导致OOM。
- CachedThreadPool 和 SingleThreadPool:允许创建线程数量为Integer.MAX_VALUE,可能会创建大量线程,从而导致OOM。
//自定义线程池
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
2, //常驻线程数量
5, //最大线程数量
2L, //存活时间数量
TimeUnit.SECONDS, //存活时间单位
new ArrayBlockingQueue<>(3), //阻塞队列
Executors.defaultThreadFactory(), //创建线程的工厂
new ThreadPoolExecutor.AbortPolicy() //拒绝策略(这里是默认的抛异常)
);
//6个任务
try{
for (int i = 1; i <= 6; i++) {
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + "办理业务");
});
}
}finally {
//关闭线程池
threadPool.shutdown();
}
Fork/Jion 框架
分支合并框架
Fork:把一个复杂任务进行分拆,大事化小
Join:把分拆任务的结果进行合并
有点像归并排序
class MyTask extends RecursiveTask<Integer> {
//拆分差值不能超过10,计算10以内的运算
private static final int NUMBER = 10;
private int begin; //拆分开始值
private int end; //拆分结束值
private int result;//返回计算结果
public MyTask(int begin,int end){
this.begin =begin;
this.end = end;
}
//拆分和合并的过程
@Override
protected Integer compute() {
//判断两个数值差是否大于10
if ((end - begin) <= NUMBER){
for (int i = begin; i <= end; i++) {
result += i;
}
}else { //进一步拆分
//获取中间值
int mid = begin + end >> 1;
//拆分左边
MyTask myTaskLeft = new MyTask(begin, mid);
myTaskLeft.fork();
//拆分右边
MyTask myTaskRight = new MyTask(mid + 1, end);
myTaskRight.fork();
//合并结果
result = myTaskLeft.join() + myTaskRight.join();
}
return result;
}
}
public class ForkJoinDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyTask myTask = new MyTask(1, 100);
//创建分支合并池对象
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask<Integer> forkJoinTask = forkJoinPool.submit(myTask);
//得到结果
int result = forkJoinTask.get();
System.out.println(result);
//关闭池对象
forkJoinPool.shutdown();
}
}