JUC多线程编程总结
JUC:java.util.concurrent多线程工具包的使用
测试demo:https://gitee.com/aqq/mytest.git
1、守护线程
java中有两类线程:用户线程、守护线程
只有当前JVM中存在用户线程没结束,守护线程就会全部工作,只有当最后一个用户线程结束之后,守护线程随着JVM结束工作,比如JVM中的垃圾回收(GC)就是守护线程。
Thread thread = new Thread();
// 设定 thread 为 守护线程,default false(非守护线程)
thread.setDaemon(true)
//判断某个线程是否是守护线程用isDaemon
thread.isDaemon();
注意:
1、thread.setDaemon(true)必须在thread.start()之前,否则会抛出IllegalThreadStateException异常,不能把正在运行的线程设置为守护线程
2、在Daemon线程中产生的新线程也是守护线程
3、不要任务所有的线程都可以分配给Daemon来进行服务,比如读写操作或者计算逻辑
2、Synchronized的使用
模拟三个售票员卖30张票的场景
/**
* 模拟 三个售票员卖30张票的情景
* 1、首先定义资源,以及资源属性
*
*/
public class SaleTickets {
public static void main(String[] args) {
Tickets tickets = new Tickets();
Thread thread1 = new Thread(new SaleThread(tickets), "AA");
Thread thread2 = new Thread(new SaleThread(tickets), "BB");
Thread thread3 = new Thread(new SaleThread(tickets), "CC");
thread1.start();
thread2.start();
thread3.start();
}
}
class SaleThread implements Runnable{
private Tickets tickets;
public SaleThread(Tickets tickets) {
this.tickets = tickets;
}
@Override
public void run() {
for(int i = 0;i<120;i++){
tickets.sail();
}
}
}
class Tickets{
private int numbers = 100;
public synchronized void sail(){
if(numbers>0){
System.out.println(Thread.currentThread().getName() + "卖出:1张,剩余:" + (numbers--) + "张");
}
}
}
3、Lock的使用
ReentrantLock(可重入锁)的使用:同样是模拟三个售票员卖30张票的场景
调用lock.lock()方法,必须要有释放锁的调用(lock.unlock()),最好写在finally中
public class LSaleTickets {
public static void main(String[] args) {
Tickets tickets = new Tickets();
Thread thread1 = new Thread(new SaleThread(tickets), "AA");
Thread thread2 = new Thread(new SaleThread(tickets), "BB");
Thread thread3 = new Thread(new SaleThread(tickets), "CC");
thread1.start();
thread2.start();
thread3.start();
}
}
/**
* 创建资源对象
**/
class Tickets{
//票数量
private int numbers = 30;
//创建可重入锁
private final ReentrantLock lock = new ReentrantLock();
//买票
public void sale(){
lock.lock();
try{
//判断是否有票
if (numbers > 0) {
System.out.println(Thread.currentThread().getName() + "卖出:1张,剩余:" + (numbers--) + "张");
}
}finally {
lock.unlock();
}
}
}
//定义卖票的线程
class SaleThread implements Runnable{
private Tickets tickets;
public SaleThread(Tickets tickets) {
this.tickets = tickets;
}
@Override
public void run() {
for(int i = 0;i<40;i++){
tickets.sale();
}
}
}
4、线程间通信,虚假唤醒
线程间通信,主要是通过wait()和notifyAll()方法,或者Condition类的await()和signalAll()来控制线程的等待和唤醒,从而实现线程间的通信
案例1:通过synchronized关键字和wait和notify方法实现一个+1,一个-1的操作
package com.vike.juctest.sync;
/**
* 模拟线程间通信
* if(num!=0){
* this.wait();//在哪里睡,就在哪里唤醒
* }
* 采用if的话会存在虚假唤醒的问题,因为if条件只判断一次,应该为while
* while(num!=0){
* this.wait();
* }
*/
public class ThreadTransf {
public static void main(String[] args) {
Share share = new Share();
new Thread(()->{
for (int i =0;i<10;i++){
try {
share.incr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"AA").start();
new Thread(()->{
for (int i=0;i<10;i++){
try {
share.decr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"BB").start();
new Thread(()->{
for (int i =0;i<10;i++){
try {
share.incr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"CC").start();
new Thread(()->{
for (int i=0;i<10;i++){
try {
share.decr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"DD").start();
}
}
/**
* 创建资源类
*/
class Share{
private int number = 0;
/**
* +1 方法
*/
public synchronized void incr() throws InterruptedException {
while (number != 0) {
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"::数据+1后::"+number);
this.notifyAll();
}
/**
* -1 方法
*/
public synchronized void decr() throws InterruptedException {
while (number != 1) {
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"::数据-1后::"+number);
this.notifyAll();
}
}
案例二:通过Lock的方式实现,需要用到Condition类的await()和signalAll()方法等待和唤醒
package com.vike.juctest.lock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* 线程间通信2 采用lock的形式
*/
public class ThreadTransf2 {
public static void main(String[] args) {
Share share = new Share();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
share.incy();
}
}, "AA").start();
new Thread(()->{
for (int i=0;i<10;i++){
share.decy();
}
},"BB").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
share.incy();
}
}, "CC").start();
new Thread(()->{
for (int i=0;i<10;i++){
share.decy();
}
},"DD").start();
}
}
/**
* 定义资源
*/
class Share{
private int number = 0;
final ReentrantLock lock = new ReentrantLock();
final Condition condition = lock.newCondition();
/**
* 定义加1的方法
*/
public void incy(){
lock.lock();
try {
while (number!=0){
//当前线程等待
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName()+"::数据+1后::"+number);
//唤醒其他线程
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
/**
* 定义减1的方法
*/
public void decy(){
//上锁
lock.lock();
try {
while (number != 1) {
//线程等待
condition.await();
}
number--;
//唤醒其他线程
condition.signalAll();
System.out.println(Thread.currentThread().getName()+"::数据-1后::"+number);
} catch (Exception e) {
e.printStackTrace();
} finally {
//解锁
lock.unlock();
}
}
}
案例3:线程之间定制化通信
-
线程之间定制化通信
-
AA、BB、CC三个线程按顺序执行
-
状态为1, AA执行5次,状态改为2,通知BB执行,
-
状态为2, BB执行10次,状态改为3,通知CC执行
-
状态为3, CC执行15次,状态改为1,通知AA执行
public class ThreadTransf3 { public static void main(String[] args) { ShareResource shareResource = new ShareResource(); new Thread(()->{ for (int i =1;i<3;i++){ try { shareResource.printAA(i); } catch (InterruptedException e) { e.printStackTrace(); } } },"AA").start(); new Thread(()->{ for (int i =0;i<2;i++){ try { shareResource.printBB(i); } catch (InterruptedException e) { e.printStackTrace(); } } },"BB").start(); new Thread(()->{ for (int i =0;i<2;i++){ try { shareResource.printCC(i); } catch (InterruptedException e) { e.printStackTrace(); } } },"CC").start(); } } /** * 定义资源类 */ class ShareResource{ //定义线程状态 1:AA线程执行 2:BB线程执行 3:CC线程执行 private int flag = 1; final ReentrantLock lock = new ReentrantLock(); final Condition conditionA = lock.newCondition(); final Condition conditionB = lock.newCondition(); final Condition conditionC = lock.newCondition(); public void printAA(int loop) throws InterruptedException { lock.lock(); try { //判断条件 while (flag != 1) { conditionA.await(); } //干活 for (int i=1;i<6;i++){ System.out.println(Thread.currentThread().getName() + "::轮数" + loop + ",打印:" + i); } flag=2; //通知其他线程 conditionB.signal(); }finally { lock.unlock(); } } public void printBB(int loop) throws InterruptedException { lock.lock(); try { //判断条件 while (flag != 2) { conditionB.await(); } //干活 for (int i=1;i<11;i++){ System.out.println(Thread.currentThread().getName() + "::轮数" + loop + ",打印:" + i); } flag=3; //通知其他线程 conditionC.signal(); }finally { lock.unlock(); } } public void printCC(int loop) throws InterruptedException { lock.lock(); try { //判断条件 while (flag != 3) { conditionC.await(); } //干活 for (int i=1;i<16;i++){ System.out.println(Thread.currentThread().getName() + "::轮数" + loop + ",打印:" + i); } flag=1; //通知其他线程 conditionA.signal(); }finally { lock.unlock(); } } }
5、集合安全
List:
ArrayList线程不安全,替代方案有三个
1)Vector vector = new Vector();
2)Collections工具类:List list = Collections.synchronizedList(new ArrayList<>());
3)CopyOnWriteArrayList: List list = new CopyOnWriteArrayList();
Set:
HashSet线程不安全,替代方案用CopyOnWriteSet
Map:
HashMap线程不安全,替代方案用:ConcurrentHashMap
package com.vike.juctest.collection;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* 集合的线程安全问题
*
*/
public class ThreadCollertion {
public static void main(String[] args) {
ThreadCollertion threadCollertion = new ThreadCollertion();
// threadCollertion.mainList();
// threadCollertion.mainSet();
threadCollertion.mainmap();
}
public void mainList(){
// List list = new ArrayList();
// List list = new Vector();
// List list = Collections.synchronizedList(new ArrayList<>());
List list = new CopyOnWriteArrayList();
for (int i = 0; i < 30; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0, 8));
System.out.println(list);
}, String.valueOf(i)).start();
}
}
public void mainSet(){
// Set<String> set = new HashSet<>();
Set<String> set = new CopyOnWriteArraySet<>();
for (int i = 0; i < 30; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0, 8));
System.out.println(set);
},String.valueOf(i)).start();
}
}
public void mainmap(){
// Map<String, String> map = new HashMap<>();
Map<String, String> map = new ConcurrentHashMap<>();
for (int i = 0; i < 30;i++) {
String key = String.valueOf(i);
new Thread(() -> {
map.put(key, UUID.randomUUID().toString().substring(0, 8));
System.out.println(map);
}, String.valueOf(i)).start();
}
}
}
6、多线程锁
公平锁和非公平锁,案例:买票案例,线程AA可能把所有的票卖完,BB和CC则没卖出去票,
公平锁:效率低,需要判定是否公平
非公平锁:效率高,线程饿死
private final ReentrantLock lock = new ReentrantLock(false);//非公平锁
private final ReentrantLock lock = new ReentrantLock(true);//公平锁
死锁:两个或者两个以上的进程在执行过程中,因为争夺资源而造成一种互相等待的现象,如果没有外力干涉,他们无法再执行下去
死锁可以通过jps -l 查看java线程id,然后通过jstack 线程id 查看线程死锁
public class DeadLock {
public static void main(String[] args) {
Object a = new Object();
Object b = new Object();
new Thread(()->{
synchronized (a){
System.out.println(Thread.currentThread().getName()+":持有锁a,试图获取锁b");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (b){
System.out.println(Thread.currentThread().getName()+":获取锁b");
}
}
},"a").start();
new Thread(()->{
synchronized (b){
System.out.println(Thread.currentThread().getName()+":持有锁b,试图获取锁a");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (a){
System.out.println(Thread.currentThread().getName()+":获取锁a");
}
}
},"b").start();
}
}
7、方式实现多线程
实现多线程的四种方式
Runnable和Callable两种方式比较
1)有无返回参数:Callable有返回参数
2)对外是否返回异常:Callable调用call方法返回异常
3)实现方式不同,一个是run方法,一个是call方法
######1)集成Thread类
class MyThread extends Thread{
@Override
public void run() {
System.out.println("线程执行");
}
}
######2)实现Runnable接口
class TheradRunnable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " run方法执行");
}
}
######3)实现Callable接口
class ThreadCallable implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName() + " call方法执行");
return 200;
}
}
######4)线程池的方式
下边详细说明
8、辅助类(计数器、循环栅栏、信号灯)的使用
1)CountDownLatch(计数器)
模拟场景:现场有6个同学,最后一个同学离开之后,班长锁门
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatchDemo demo = new CountDownLatchDemo();
// demo.test1();
demo.test2();
}
/**
* 不采用CountDownLatch的时候
*/
public void test1(){
for (int i = 1; i < 7; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "号同学离开教室");
}, String.valueOf(i)).start();
}
System.out.println("班长锁门");
}
/**
* 不采用CountDownLatch的时候,班长不是最后锁门的
* 采用CountDownLatch的时候
*/
public void test2() throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 1; i < 7; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "号同学离开教室");
countDownLatch.countDown();
System.out.println("剩下同学人数:"+countDownLatch.getCount());
}, String.valueOf(i)).start();
}
System.out.println("班长等待最后同学离开");
countDownLatch.await();
System.out.println("班长锁门");
}
}
2)CyclicBarrier(循环栅栏)
/**
* 循环栅栏CyclicBarrier
* CyclicBarrier看英文单词,大概可以看出是循环阻塞的意思,在使用中CyclicBarrier的构造方法第一个参数是目标障碍数,如果阻塞达到目标
* 障碍数,才会执行cyclicBarrier.await()之后的语句,可以将CyclicBarrier理解为加1操作
*
* 模拟场景:集齐7颗龙珠可以召唤神龙
*/
public class CyclicBarrierDemo {
private static final Integer NUMBER = 7;
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER,()->{
System.out.println("集齐7颗龙珠,可以召唤神龙了");
});
for (int i = 1; i < 8; i++) {
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + "星珠已经收集到了");
cyclicBarrier.await();
} catch (Exception e) {
e.printStackTrace();
}
}, String.valueOf(i)).start();
}
}
}
3)Semaphore(信号灯)
/**
* Semaphore 信号灯
* 一个计数信号量,从概念上讲,信号量维护了一个许可集,如果有必要,在许可可用前会阻塞一个acquire(),然后再获取该许可
* 每个release()添加一个许可,从而可能释放一个正在阻塞的获取着,但是,不使用实际的许可对象,Semaphore只对可用许可号码进行计数,
* 并采取相应的行动
*
* 例子:六辆汽车抢三个停车位
*
*/
public class SemaphoreDemo {
public static void main(String[] args) {
//创建Semaphore,设置许可数量
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 6; i++) {
new Thread(() -> {
try {
//抢占许可
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + " 抢到了车位");
int timeForTC = new Random().nextInt(5);
System.out.println(Thread.currentThread().getName() + "停车" + timeForTC + "秒钟");
//随机设置停车时间
TimeUnit.SECONDS.sleep(timeForTC);
System.out.println(Thread.currentThread().getName()+" -----------离开了车位");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release();
}
}, String.valueOf(i + 1)).start();
}
}
}
9、读写锁
1、读锁是共享锁,写锁是独占锁
2、读写互斥,读读共享
/**
* 读写锁
* 读锁是共享锁,可以多个线程共享
* 写锁时独占锁,只能单个线程占用
* 读写是互斥的
* 读读是共享的
* synchronized和ReentrantLock都是独占锁
*/
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCache cache = new MyCache();
//创建存放线程
for (int i = 0; i < 3; i++) {
final int num =i;
new Thread(() -> {
cache.put(String.valueOf(num), String.valueOf(num));
}, String.valueOf(i)).start();
}
//创建读取线程
for (int i = 0; i < 3; i++) {
final int num =i;
new Thread(() -> {
Object o = cache.get(String.valueOf(num));
}, String.valueOf(i)).start();
}
}
}
/**
* 定义资源类
*/
class MyCache {
private volatile Map<String, Object> map = new HashMap<>();
ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
/**
* map中放值
*/
public void put(String key,Object value){
ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
try {
writeLock.lock();
System.out.println(Thread.currentThread().getName() + "正在写操作" + key);
TimeUnit.MICROSECONDS.sleep(300);
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "写完了" + key);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
writeLock.unlock();
}
}
/**
* map中取值
*/
public Object get(String key){
Object result = null;
ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
try {
readLock.lock();
System.out.println(Thread.currentThread().getName() + "正在读操作" + key);
TimeUnit.MICROSECONDS.sleep(300);
result = map.get(key);
System.out.println(Thread.currentThread().getName() + "读完了" + key + ",值是" + result);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
readLock.unlock();
}
return result;
}
}
10、阻塞队列
阻塞队列BlockingQueue
当队列中元素已满的时候再往里放值会阻塞
当队列中元素为空的时候再取值会阻塞
队列:先进先出 栈:后进先出
队列中的API分为四组
-
抛异常:add(e),remove(),element()
-
返回特殊值:offer(e),poll(),peek()
-
阻塞:put(e),take()
-
超时退出:offer(e,time,unit),poll(time,unit)
public class BlockingQueueDemo { public static void main(String[] args) throws InterruptedException { ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(3); /** * 第一组:add():放值,当队列已满的时候会抛异常 * remove():取值,当队列为空的时候会抛异常 * elemeng():判断队列是否有值 */ // System.out.println(queue.add("a")); // System.out.println(queue.add("b")); // System.out.println(queue.add("c")); // System.out.println(queue.add("w")); // System.out.println(queue.remove()); // System.out.println(queue.remove()); // System.out.println(queue.remove()); // System.out.println(queue.remove()); // System.out.println(queue.add("a")); // System.out.println(queue.element()); // System.out.println(queue.element()); /** * 第二组: * offer():放值,如果未满返回true,否则返回false * poll():取值,如果为空返回null,否则返回true * peek():判断是否有值,如果有值返回true,否则返回null */ // System.out.println(queue.offer("a")); // System.out.println(queue.offer("b")); // System.out.println(queue.offer("c")); // System.out.println(queue.offer("w")); // System.out.println(queue.poll()); // System.out.println(queue.poll()); // System.out.println(queue.offer("a")); // System.out.println(queue.peek()); /** * 第三组 * put(e):放值 * take():取值 */ queue.put("a"); queue.put("a"); queue.put("a"); // queue.put("a"); /** * 第四组: * offer(e,time,unit):放值,如果阻塞超过unit时间,则不再等待 * poll(time,unit):取值,如果阻塞超过unit时间,则不再等待 */ queue.offer("a", 1l, TimeUnit.SECONDS); queue.offer("a", 1l, TimeUnit.SECONDS); queue.offer("a", 1l, TimeUnit.SECONDS); queue.offer("a", 1l, TimeUnit.SECONDS); } }
11、线程池
-
优点:
-
1.降低资源消耗,通过复用已创建的线程降低线程创建和销毁造成的消耗
-
2.提高响应速度,当任务达到时,任务可以不需要等待线程创建就可以立刻执行
-
3.提高线程的可管理性,线程属于稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性
创建线城池采用工具列Executers,线程池分类:
-
1)newFixedThreadPool(常用):创建一个可重用的固定线程数的线程池,以共享无界队列的方式运行这些线程,线程池内部线程都处于运行状态
-
当再次新增附加线程,则线程在队列中等待,直到有可用线程为止
-
特征:1、线程池中的线程数量是固定的,可以很好的控制线程的并发量
-
2、线程可以重复使用,在显式关闭之前,线程一直存在
-
3、超出一定量的线程被提交的时候需在队列中等待
-
2)newSingleThreadExecutor(),单一线程的线程池
* -
- newCachedThreadPool(),可变线程数量的线程池
三个线程池的构造方法都是通过ThreadPoolExecutor类实现的
ThreadPoolExecutor类的构造方法参数详解
-
int corePoolSize:核心线程数量,常驻线程数量
-
int maximumPoolSize:线程池最大线程数量
-
long keepAliveTime:线程存活时间,当线程数超过核心线程数之后,并且处于非活跃状态超过keepAliveTime之后被回收
-
TimeUnit unit:线程存活时间单位
-
BlockingQueue workQueue:阻塞队列
-
ThreadFactory threadFactory:线程工厂,用于创建线程
-
RejectedExecutionHandler handler:拒绝策略,当线程达到最大线程数时的拒绝策略
-
核心线程 > 阻塞队列 > 最大线程数 > 拒绝策略
-
1、当有线程请求时,首先通过核心线程运行
-
2、当核心线程达到最大时,新线程添加到阻塞队列中
-
3、当阻塞队列达到最大时,并且线程池没达到最大线程数,则新建线程,优先新线程执行
-
4、当线程池达到最大线程数时,再有新线程到达时,采用拒绝策略
```java public class Demo1 { public static void main(String[] args) { //可重用固定容量线程池 ExecutorService executorService = Executors.newFixedThreadPool(5); //单一线程的线程池 // ExecutorService executorService = Executors.newSingleThreadExecutor(); //长度可变的线程池 // ExecutorService executorService = Executors.newCachedThreadPool(); try{ for (int i = 0; i < 10; i++) { executorService.execute(()->{ System.out.println(Thread.currentThread().getName() + " 正在执行!"); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } }); } }catch (Exception e){ e.printStackTrace(); }finally { System.out.println("====================程序执行完毕"); executorService.isShutdown(); } } } ```
-
线程池在使用的时候一般不采用工具类的形式,大都采用自定义的方式,如下
public class Demo1 {
public static void main(String[] args) {
//可重用固定容量线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
//单一线程的线程池
// ExecutorService executorService = Executors.newSingleThreadExecutor();
//长度可变的线程池
// ExecutorService executorService = Executors.newCachedThreadPool();
try{
for (int i = 0; i < 10; i++) {
executorService.execute(()->{
System.out.println(Thread.currentThread().getName() + " 正在执行!");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}catch (Exception e){
e.printStackTrace();
}finally {
System.out.println("====================程序执行完毕");
executorService.isShutdown();
}
}
}
12、分支与合并
-
线程的分支及合并
-
场景模拟:从1到100,实现相加,如果两数相差大于10,则分支,采用二分查找法,对数值进行分支
-
1.。。10 11.。。。20 。。。。 91.。。。。100
-
用到类
-
ForkJoinPool 分支合并池
-
RecursiveTask extends ForkJoinTask
public class Demo1 { public static void main(String[] args) throws ExecutionException, InterruptedException { long startTimes1 = System.currentTimeMillis(); MyTask2 task = new MyTask2(0, 100); Integer compute = task.compute(); System.out.println(compute); long endTimes1 = System.currentTimeMillis(); System.out.println("单线程用时:" + (endTimes1 - startTimes1)); long startTimes = System.currentTimeMillis(); //创建task对象 MyTask myTask = new MyTask(0, 100); //创建分支合并池对象 ForkJoinPool forkJoinPool = new ForkJoinPool(); ForkJoinTask<Integer> forkJoinTask = forkJoinPool.submit(myTask); //获取最终合并结果 Integer integer = forkJoinTask.get(); System.out.println(integer); //关闭分支合并池 forkJoinPool.shutdown(); long endTimes = System.currentTimeMillis(); System.out.println("多线程用时:" + (endTimes - startTimes)); } } /** * 任务执行类 */ class MyTask extends RecursiveTask<Integer> { private static final Integer LIMIT = 10; private int start; private int end; private int result; public MyTask(int start,int end){ this.start=start; this.end=end; } @Override protected Integer compute() { if((end-start)>LIMIT){ int middle = (end+start)/2; MyTask myTask1 = new MyTask(start, middle); MyTask myTask2 = new MyTask(middle + 1, end); myTask1.fork(); myTask2.fork(); result = myTask1.join() + myTask2.join(); }else { for (int i = start; i <=end; i++) { result = result + i; } } return result; } } /** * 任务执行类 */ class MyTask2 { private static final Integer LIMIT = 10; private Integer start; private Integer end; private Integer result; public MyTask2(Integer start,Integer end){ this.start=start; this.end=end; } protected Integer compute() { if((end-start)>LIMIT){ Integer middle = (end+start)/2; MyTask myTask1 = new MyTask(start, middle); MyTask myTask2 = new MyTask(middle + 1, end); result = myTask1.compute() + myTask2.compute(); }else { for (int i = start; i <=end; i++) { result = result + i; } } return result; } }
13、异步回调
线程的同步与异步,采用CompletableFuture类
异步是调用completableFuture.whenComplete().get()方法,其中whenComplete传参为消费性函数式编程对象
其中T:异步调用成功返回值
U:异常时的异常信息
public class CompletableFutureDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFutureDemo completableFutureDemo = new CompletableFutureDemo();
// completableFutureDemo.synchronize();
// System.out.println("------------------------------------------------------");
completableFutureDemo.asynchronous();
}
/**
* Synchronize
* 线程同步调用,调用completableFuture.get()方法时线程会阻塞,等到子线程执行完之后再继续执行
*/
private void synchronize() throws ExecutionException, InterruptedException {
//同步调用
//runAsync 同步调用
CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()->{
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "线程调用!");
});
System.out.println("线程执行2");
//同步调用返回结构
completableFuture.get();
System.out.println("线程调用3");
}
/**
* asynchronous
* whenComplete 实现异步回调
* 参数解析:
* t:成功返回值
* u:失败的异常信息
*/
private void asynchronous() throws ExecutionException, InterruptedException {
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(()->{
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "子线程调用");
return 200;
});
System.out.println("线程执行2");
completableFuture.whenComplete((Object t,Object u)->{
System.out.println(t);
System.out.println(u);
}).get();
System.out.println("线程执行结束");
}
}