juc 入门
![]()
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException { // 创建3个定长线程池 ExecutorService executorService = Executors.newFixedThreadPool(3); // 创建线程 缺点 api少 FutureTask<String> futureTask = new FutureTask<>(() -> { try { sleep(300); System.out.println(1); } catch (InterruptedException e) { throw new RuntimeException(e); } }, "花生"); FutureTask<String> futureTask1 = new FutureTask<>(() -> { try { sleep(300); System.out.println(2); } catch (InterruptedException e) { throw new RuntimeException(e); } }, "花生"); FutureTask<String> futureTask2 = new FutureTask<>(() -> { try { sleep(300); System.out.println(3); } catch (InterruptedException e) { throw new RuntimeException(e); } }, "花生"); // 将线程放入线程池中 executorService.submit(futureTask); executorService.submit(futureTask1); executorService.submit(futureTask2); // 获取线程返回值 会一直阻塞状态 get 会抛出异常处理 System.out.println(futureTask.get()); // 通过 isDone 方法判断是否处理 if (futureTask.isDone()) {
// 通过get 传入等待时间 超过等待时间 直接报错 判定方法 futureTask.get(3, TimeUnit.SECONDS); } }
实际案例 :
/** * 未使用异步时间 会在31秒 使用 流+异步线程 5.1秒 大大提升效率 * @param args * @throws ExecutionException * @throws InterruptedException * @throws TimeoutException */ public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException { // 淘宝 天猫 京东 少少 阿里 查询一个产品 并返回一个价格集合 采用异步多线程 处理问题 提高效率 long begin = System.currentTimeMillis(); List<String> list = Arrays.asList("淘宝", "淘宝", "天猫", "京东", "少少", "阿里"); // 通过流的方式 通过异步函数 调用获取价格 通过map 映射成CompletableFuture 放回 list<CompletableFuture> 集合 然后在流的方式 从CompletableFuture.join 获取结果 放回结果集 List<BigDecimal> collect = list.stream().map(s -> CompletableFuture.supplyAsync(() -> getPrice(s))).collect(Collectors.toList()).stream().map(CompletableFuture::join).collect(Collectors.toList()); System.out.println(System.currentTimeMillis()-begin); System.out.println(collect); } private static BigDecimal getPrice(String name){ try { // 时间类 睡眠5秒 TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { throw new RuntimeException(e); } // ThreadLocalRandom.current() 真正随机数字 通过 BigDecimal 转换并保留两位小数 四舍五入 return BigDecimal.valueOf(ThreadLocalRandom.current().nextDouble(100)).setScale(2, RoundingMode.UP); }
completableFuture 的 常用方法解析
/** * 常用方法 介绍 * * @param args * @throws ExecutionException * @throws InterruptedException * @throws TimeoutException */ public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException { // 创建 定长 线程池 ExecutorService executorService = Executors.newFixedThreadPool(1); CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> "张三", executorService); // 需要截获异常 阻塞 一直等待拿到结果 completableFuture.get(); // 需要截获异常 设定等待时间 超过等待时间 直接报错 判定计算失败 completableFuture.get(3, TimeUnit.SECONDS); // 无异常 一直等待结果 completableFuture.join(); // 无异常 如果计算完成 返回结果 未计算完成则返回设定的值 ->返回信息" completableFuture.getNow("返回信息"); // 计算完成 则返回 true 不打算 方法join 返回计算结果 // 计算未完成 打断计算方法 返回false join返回设定返回值 ->dd completableFuture.complete("dd"); completableFuture.join(); // 对计算结果串行话 ,thenApply里面方法依赖上个方法结果 依次传递 不修改原属性值 需要从新获取新值 // 中间步骤错误 则停止 String join = completableFuture.thenApply(v -> v + "11").thenApply(v -> v + "22").join(); System.out.println(join); // 计算结果 串行 结果处理 completableFuture.whenComplete((v, e) -> { if (e == null) System.out.println(v); //计算结果 串行 异常拦截处理 }).exceptionally(e -> { System.out.println(e.getMessage()); return e.getMessage(); }); // 当放中间步骤发生异常不会终止,会一直计算 异常问题 数值会丢失 completableFuture.handle((f, e) -> { int i = 10 / 0; // 异常 代码无法执行 失效 System.out.println(f); return e + "111"; }).handle((f, e) -> { // 上一方法 异常返回值为null System.out.println(f); // 此时 f=null 返回结果为 null2222 return f + "2222"; }).whenComplete((f, e) -> { System.out.println(f); if (e != null) System.out.println(e.getMessage()); }); // 消费类型 接参数 无返回值 Void joinAccept = completableFuture.thenAccept((v) -> { if (v.equals("11111")) System.out.println(v); }).join(); System.out.println(joinAccept); // thenApply 依赖上个返回值 并返回计算结果 thenRun 不依赖上个返回值 无返回结果 // thenAccept 消费类型 有参数无返回类型 // thenApply a执行完执行b b依赖a返回值 有参数 有返回值 // thenRun a执行完执行b b不依赖a返回值 无参数 无返回值 // thenRun 无返回值 // 无返回值 join 结果 都为null 因为无结果 Void joinRun = completableFuture.thenRun(() -> { }).join(); System.out.println(joinRun); }
线程池 和 completableFuture 选择 及 三种情况
/**
* 线程池 和 CompletableFuture 选择
* @param args
*/
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(4);
CompletableFuture.supplyAsync(()->{
try {
TimeUnit.SECONDS.sleep(10);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "1111";
},executorService).thenRun(()->{
try {
TimeUnit.SECONDS.sleep(10);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}).thenRun(()->{
try {
TimeUnit.SECONDS.sleep(10);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
executorService.shutdown();
}
/**
* 第一个没有传 线程池 则 从头开始使用 默认线程池
* 第一个注入线程池 则 使用注入线程池
* 第一个使用线程池 第二个使用 异步线程 则从二个开始使用 默认线程池
*
* 如果任务 处理时间过短 ,则会优化直接使用 main 主线程 不在使用声明线程
*/
CompletableFuture<String> playA = CompletableFuture.supplyAsync(() -> { try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { throw new RuntimeException(e); } return "play a"; }); CompletableFuture<String> playB = CompletableFuture.supplyAsync(() -> { try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { throw new RuntimeException(e); } return "play b"; }); // 两个线程比较 哪个跑的快 则返回快的 返回值 // 计算速度比较 方法 CompletableFuture<String> applyToEither = playA.applyToEither(playB, f -> f + "is win"); System.out.println(applyToEither.join());
long l = System.currentTimeMillis(); CompletableFuture<Integer> a = CompletableFuture.supplyAsync(() -> { try { TimeUnit.SECONDS.sleep(6); } catch (InterruptedException e) { throw new RuntimeException(e); } return 10; }); CompletableFuture<Integer> b = CompletableFuture.supplyAsync(() -> { try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { throw new RuntimeException(e); } return 10; }); // 整合两个异步线程结果 一个完成等另外一个,然后返回 整合结果 CompletableFuture<Integer> result = a.thenCombine(b, Integer::sum); System.out.println(System.currentTimeMillis()-l); System.out.println(result.join());
CompletableFuture<Integer> result = CompletableFuture.supplyAsync(() -> {
return 10;
}).thenCombine(CompletableFuture.supplyAsync(() -> 20), Integer::sum)
.thenCombine(CompletableFuture.supplyAsync(() -> 30), Integer::sum);
System.out.println(result.join());
package org.example.config; import java.util.concurrent.TimeUnit; public class Person { public static void main(String[] args) { Iphone iphone = new Iphone(); new Thread(Iphone::sentEmil).start(); new Thread(iphone::hello).start(); } } class Iphone{ /** * 普通方法加锁 成为对象锁 * 静态方法加锁 成为类锁 * 1.synchronized 修饰普通方法 则锁的是资源类的所有synchronized方法,第一个获取,其他需要等待 * 2.synchronized 访问 两个实例对象或者访问非synchronized方法 则不会出现争抢资源问题,因为访问的不同资源 * 3.static 静态synchronized 方法 则升级为class 模版锁,多少实例对象 都会指向同一个模版,一人获取其他等待 * 4.当调用两个种锁时(对象锁,类锁) 两者互补冲突 互不影响 * */ public static synchronized void sentEmil(){ try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println("发邮件"); } public synchronized void sentMs(){ System.out.println("发短信"); } public void hello(){ System.out.println("hello"); } }
import java.util.concurrent.locks.ReentrantLock; public class MyThreadDemo { public static void main(String[] args) { Ticket ticket = new Ticket(); new Thread(()->{ for (int i = 0; i < 100; i++) { ticket.sale(); } }).start(); new Thread(()->{ for (int i = 0; i < 100; i++) { ticket.sale(); } }).start(); new Thread(()->{ for (int i = 0; i < 100; i++) { ticket.sale(); } }).start(); } } class Ticket{ private int num =50; // 默认 非公平锁 fair true 为公平锁 ReentrantLock reentrantLock = new ReentrantLock(true); public void sale(){ // 加锁 reentrantLock.lock(); if(num>0){ System.out.println(Thread.currentThread().getName()+"卖出了第"+num--+"剩余"+num); } // 解锁 reentrantLock.unlock(); } }
/** 两都为重入锁 * synchronized 自动加锁 自动释放锁. 隐式锁 * Lock (ReentrantLock) 手动加锁 手动释放 需要加锁释放需要一一对应。显式锁 * 实现原理 每一个对象都一个锁计数器和一个持有该锁的线程指针 * 每个锁对象拥有一个锁计数器和一个指向持有该锁的线程的指针。 * 当执行monitorenter时,如果目标锁对象的计数器內零个 那么说明它没有被其他线程所持有,Java虚拟机会将该锁对象的持有线程设置为当前线程,并且将其计数器加1。 * 在目标锁对象的计数器不为零的情况下,如果锁对象的持有线程是当前线程,那么Java 虚拟机可以将其计数器加1,否则需要等待,直至持有线程释放该锁。 * 当执行monitorexit时,Java虚拟机则需将锁对象的计数器减1。计数器为零代表锁已被释放。 * @param args */ public static void main(String[] args) { // synchronized Lock lock = new ReentrantLock(); /** * 对象也作为锁 每个对象都会带有对象监视器 于monitor 关起来 */ Object o = new Object(); synchronized (o){ } }
/** * 死锁案例 a持有a 锁去获取b锁 b持有锁 去获取a锁 * (中间无等待时间因为线程过快,会导主线程main线程执行 自优化导致死锁失败) * @param args */ public static void main(String[] args) { final Object objectA = new Object(); final Object objectB = new Object(); new Thread(()->{ synchronized (objectA){ System.out.println(Thread.currentThread().getName()+"持有a锁"); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { throw new RuntimeException(e); } synchronized (objectB){ System.out.println(Thread.currentThread().getName()+"去获取b锁"); } } }).start(); new Thread(()->{ synchronized (objectB){ System.out.println(Thread.currentThread().getName()+"持有a锁"); new Thread(()->{ synchronized (objectA){ System.out.println(Thread.currentThread().getName()+"持有a锁"); synchronized (objectB){ System.out.println(Thread.currentThread().getName()+"去获取b锁"); } } }).start(); synchronized (objectA){ System.out.println(Thread.currentThread().getName()+"去获取b锁"); } } }).start(); }
/** * 死锁案例 a持有a 锁去获取b锁 b持有锁 去获取a锁 * (中间无等待时间因为线程过快,会导主线程main线程执行 自优化导致死锁失败) * @param args */ /** * * 检测程序死锁 有两种方式 * 1. 终端输入jps -l 查看进程号 (jps = java ps -ef) * 2.jstack 进程号 查看堆栈信息 * * 图形化工具 jconsole 本地链接 检查死锁 就可以显示死锁线程 */ public static void main(String[] args) { final Object objectA = new Object(); final Object objectB = new Object(); new Thread(()->{ synchronized (objectA){ System.out.println(Thread.currentThread().getName()+"持有a锁"); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { throw new RuntimeException(e); } synchronized (objectB){ System.out.println(Thread.currentThread().getName()+"去获取b锁"); } } }).start(); new Thread(()->{ synchronized (objectB){ System.out.println(Thread.currentThread().getName()+"持有a锁"); new Thread(()->{ synchronized (objectA){ System.out.println(Thread.currentThread().getName()+"持有a锁"); synchronized (objectB){ System.out.println(Thread.currentThread().getName()+"去获取b锁"); } } }).start(); synchronized (objectA){ System.out.println(Thread.currentThread().getName()+"去获取b锁"); } } }).start(); }
/** * object 等待唤醒 注意 * 1.必须 所有线程方法 都被锁资源 * 2.必须 先等待 后唤醒 否则 线程会一直 等待状态无法终止 * @param args */ public static void main(String[] args) { final Object objectA = new Object(); Thread a = new Thread(() -> { synchronized (objectA){ System.out.println(Thread.currentThread().getName()+"进来了。。。。。"); try { objectA.wait(); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println(Thread.currentThread().getName()+"结束了。。。。。"); } },"a"); a.start(); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { throw new RuntimeException(e); } Thread b = new Thread(()->{ synchronized (objectA){ System.out.println(Thread.currentThread().getName()+"开始唤醒a线程"); objectA.notify(); } },"b"); b.start(); }
/** * 解决了 线程等待唤醒 操作需要被锁的问题 * * 用于创建锁和其他同步类的基本线程阻塞原语。 * 该类与使用它的每个线程关联一个许可证(在Semaphore类的意义上》。 * 如果许可证可用,将立即返回park,并在此过程中消费;否则可能会阻止。 * 如果尚未提供许可,则致电unpark获得许可。( * 与Semaphores不同,许可证不会累积。最多只有一个。〉 * 可靠的使用需要使用volatile(或原子》变量来控制何时停放或取消停放。 * 对于易失性变量访问保持对这些方法的调用的顺序,但不一定是非易失性变量访问。 * 方法park和unpark提供了阻止和解除阻塞线程的有效方法, * 这些线程没有遇到导致不推荐使用的方法Thread.suspend和Thread.resume无法用于此类目的的问题: * 一个线程调用park和另一个线程尝试unpark将保留活跃性,由于许可证。 * 此外,如果调用者的线程被中断,则会返回park,并且支持超时版本。 * park方法也可以在任何其他时间返回,“无理由”,因此通常必须在返回时重新检查条件的循环内调用。 * 在这个意义上,park可以作为“忙碌等待“的优化,不会浪费太多时间旋转,但必须与unpark配对才能生效。 * 三种形式的park每个也支持blocker对象参数。 * 在线程被阻基时记录此对象,以允许监视和诊断工具识别线程被阻止的原因。 * 《此类工具可以使用方法getBlocker(Thread)访问阻止程序。 * 强烈建议使用这些表单而不是没有此参数的原始表单。在锁实现中作为blocker提供的正常参数是this * @param args */ public static void main(String[] args) { Thread thread = new Thread(() -> { System.out.println(Thread.currentThread().getName() + "进来了...."); LockSupport.park(); System.out.println(Thread.currentThread().getName() + "被唤醒了...."); }); thread.start(); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { throw new RuntimeException(e); } new Thread(()-> LockSupport.unpark(thread)).start(); }
/** * condition 等待唤醒 注意 * 1.必须 所有线程方法 都被锁资源 * 2.必须 先等待 后唤醒 否则 线程会一直 等待状态无法终止 * @param args */ public static void main(String[] args) { ReentrantLock lock = new ReentrantLock(); Condition condition = lock.newCondition(); new Thread(()->{ lock.lock(); System.out.println(Thread.currentThread().getName()+"进来了........"); try { condition.await(); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println(Thread.currentThread().getName()+"被唤醒了......"); lock.unlock(); }).start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { throw new RuntimeException(e); } new Thread(()->{ lock.lock(); condition.signal(); lock.unlock(); }).start(); }
public static void main(String[] args) { /** * 具体来说,当对一个线程,调用 interrupt()时: * ① 如果线程处于正常活动状态,那么会将该线程的中断标志设置为true,仅此而已。 * 被设置中断标志的线程将继续正常运行,不受影啊。 * 所以,interrupt()并不能真正的中断线程,需要被调用的线程自己进行配合才行 * ② 如果线程处于被阻塞状态(例如处于sleep, wait, join 等状态), * 在别的线程中调用当前线程对象的interrupt方法,那么线程将立即退出被阻塞状态, * 并抛出一个InterruptedException异常。 interrupt()方法是一个实例方法它通知目标线程中断,也仅是设置目标线程的中断标志位为true。 */
Thread t1 = new Thread(() -> {
for (int i = 0; i < 30; i++) {
if (Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread().getName() + ":中断");
break;
}
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
//重新中断线程
Thread.currentThread().interrupt();
e.printStackTrace();
}
System.out.println("===interrupt=== : " + i);
}
}, "t1");
t1.start();
try {
TimeUnit.MILLISECONDS.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
t1.interrupt();
Thread thread = new Thread(() -> { }); thread.interrupt(); /** islnterrupted()方法也是一个实例方法它判断当前线程是否被中断(通过检查中断标志位)并获取中断标志 */ boolean interrupted1 = thread.isInterrupted(); /** Thread类的静态方法interrupted() 返回当前线程的中断状态真实值(boolean类型)后会将当前线程的中断状态设为false,此方法调用之后会清除当前线程的中断标志位的状态( 将中断标志置为false了),返回当前值并清零置false */ Thread.interrupted(); }
static volatile boolean flag = false; public static void main(String[] args) throws InterruptedException { new Thread(() -> { while (true) { if (flag) { System.out.println(Thread.currentThread().getName() + "掐灭烟头"); break; } System.out.println(Thread.currentThread().getName() + "正在吸烟。。。"); } },"t1").start(); TimeUnit.MILLISECONDS.sleep(20); new Thread(() -> { System.out.println(Thread.currentThread().getName() + " 禁止吸烟!!"); flag = true; },"t2").start(); }
tatic AtomicBoolean flag = new AtomicBoolean(false); public static void main(String[] args) throws InterruptedException { new Thread(() -> { while (true) { if (flag.get()) { System.out.println(Thread.currentThread().getName() + "掐灭烟头"); break; } System.out.println(Thread.currentThread().getName() + "正在吸烟。。。"); } },"t1").start(); TimeUnit.MILLISECONDS.sleep(20); new Thread(() -> { System.out.println(Thread.currentThread().getName() + " 禁止吸烟!!"); flag.set(true); },"t2").start(); }
package org.example.config; public class Person { /** * 双端检锁 * 当多线程的 jvm优化 重新排序 造成提前释放锁 其他线程进来拿到null 造成空指针 * 加volatile 禁排序 避免空指针 */ private volatile static Person person; private Person(){} public static Person getPerson() { if (person == null) { synchronized (Person.class) { person = new Person(); } } return person; } }
package com.chen.MyJuc.service.impl; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class PersonServiceImpl { static volatile boolean i = true; static CountDownLatch countDownLatch = new CountDownLatch(1); public static void main(String[] args) throws InterruptedException { new Thread(() -> { System.out.println(Thread.currentThread().getName() + "come in"); while (i) { } }, "a").start(); TimeUnit.SECONDS.sleep(1); new Thread(() -> { i = false; }, "b").start(); } }
package org.example.service; import java.util.Random; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; class House{ int num; ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(()->0); public void add(){ threadLocal.set(1+threadLocal.get()); } } public class PersonService { /** * ThreadLocal 底层是 ThreadLocalMap 创建实例的时候把实例以key 值以value 粗村 * ThreadLocalMap 继承若引用 防止内存oom 内存溢出 * 在多线程的尤其线程池的情况 必须副本回收 防止线程复用 造成逻辑混乱 * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { House house = new House(); ExecutorService executorService = Executors.newFixedThreadPool(6); CountDownLatch countDownLatch = new CountDownLatch(20); for (int i = 0; i < 20; i++) { executorService.submit(()->{ try { Random random = new Random(); int j = random.nextInt(10); house.add(); System.out.println(house.threadLocal.get()); } finally { house.threadLocal.remove(); } countDownLatch.countDown(); }); } countDownLatch.await(); executorService.shutdown(); System.out.println("--------------------"); System.out.println(house.threadLocal.get()); } }
package org.example.service; import java.util.concurrent.TimeUnit; class House{ String name; Integer age; @Override protected void finalize() throws Throwable { System.out.println("回收后"); } } public class PersonService { /** * new 为强引用 在内存gvm 满的时候 不会被强行回收 * 当空指针的时候会被触发回收,回收前触发的方法 * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { House house = new House(); System.out.println(house); house=null; System.gc(); TimeUnit.SECONDS.sleep(2); System.out.println(house); } }
/** * 软引用 内存满一定回收 不满 不回收 * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { SoftReference<House> softReference = new SoftReference<>(new House()); System.gc(); TimeUnit.SECONDS.sleep(2); System.out.println(softReference.get()); for (int i = 0; i < 10000000; i++) { byte[] d = new byte[11098888*19]; } System.out.println(softReference.get()); }
/**
* 弱引用 不管内存满不满 gc 运行都会回收
* 软 弱引用 通常 在内存缓存大量数据 内存不足时 自动优化使用
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
WeakReference<House> weakReference = new WeakReference<>(new House());
System.out.println(weakReference.get());
System.gc();
TimeUnit.SECONDS.sleep(1);
System.out.println(weakReference.get());
}

浙公网安备 33010602011771号