juc是java.util.current的简写,意思是并发编程。
package com.atlinxi.gulimall.springdemo.juc;
/**
* 线程之间的通信问题,生产者和消费者问题!
* 线程交替执行
*
* a b 两个线程操作同一个变量 num
* a num++
* b num--
*
* 这两条线程是互相隔离的,它们彼此不知道对方是++还是--
* 需要一种机制使它们之间产生通信,
* 比如a++后告诉b该--了,b--后再告诉a该++了
*
* 解决:
* 等待唤醒,通知唤醒
*
*/
public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 5; i++) {
data.increment();
}
},"a").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
data.decrement();
}
},"b").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
data.increment();
}
},"c").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
data.decrement();
}
},"d").start();
// a-->1
// b-->0
// a-->1
// b-->0
// c-->1
// b-->0
// a-->1
// b-->0
// c-->1
// b-->0
// a-->1
// c-->2
// a-->3
// d-->2
// d-->1
// d-->0
// c-->1
// d-->0
// c-->1
// d-->0
}
}
class Data{ // 数字,资源类
private int number = 0;
// ++
public synchronized void increment(){
if (number!=0){
// 等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number++;
// 通知其他线程,++完毕,开始--
System.out.println(Thread.currentThread().getName()+"-->"+number);
this.notifyAll();
}
// --
public synchronized void decrement(){
if (number==0){
// 等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number--;
System.out.println(Thread.currentThread().getName()+"-->"+number);
this.notifyAll();
}
}
虚假唤醒
多线程环境下,有多个线程执行了wait()方法,需要其他线程执行notify()或者notifyAll()方法去唤醒它们,假如多个线程都被唤醒了,但是只有其中一部分是有用的唤醒操作,其余的唤醒都是无用功;对于不应该被唤醒的线程而言,便是虚假唤醒。
以上代码是两个线程,但是如果四个线程的话,线程就不安全了。
解决虚假唤醒的问题,只需将上面的if判断改为while即可。
为什么 if会出现虚假唤醒?
如何避免虚假唤醒
使用while循环去循环判断一个条件,而不是使用if只判断一次条件;即wait()要在while循环中。
package com.atlinxi.gulimall.springdemo.juc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class B {
public static void main(String[] args) {
Data2 data2 = new Data2();
new Thread(()->{
for (int i = 0; i < 5; i++) {
data2.increment();
}
},"a").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
data2.decrement();
}
},"b").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
data2.increment();
}
},"c").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
data2.decrement();
}
},"d").start();
}
}
class Data2 { // 数字,资源类
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
// condition.await(); // 等待
// condition.signalAll(); // 唤醒全部
// ++
public void increment() {
lock.lock();
try {
// 业务代码
while (number != 0) {
// 等待
condition.await();
}
number++;
// 通知其他线程,++完毕,开始--
System.out.println(Thread.currentThread().getName() + "-->" + number);
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
// --
public void decrement() {
lock.lock();
try {
while (number == 0) {
// 等待
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName() + "-->" + number);
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
// 输出和上面的synchronized是一样的,线程是随机的,
// 而我们想做到a执行完了通知b执行,b执行完了通知c执行这样有序执行
// 专业术语叫作精准通知和唤醒线程
可以设置多个同步监视器,每个监视器监视一个资源。
package com.atlinxi.gulimall.springdemo.juc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* a执行完调用b,b执行完调用c,c执行完调用a
*/
public class C {
public static void main(String[] args) {
Data3 data3 = new Data3();
new Thread(()->{
for (int i = 0; i < 5; i++) {
data3.printA();
}
},"a").start();
new Thread(()->{for (int i = 0; i < 5; i++) {
data3.printB();
}},"b").start();
new Thread(()->{for (int i = 0; i < 5; i++) {
data3.printC();
}},"c").start();
//a->aaaaaaaa
//b->bbbbbbb
//c->aaaaaaaa
//a->aaaaaaaa
//b->bbbbbbb
//c->aaaaaaaa
//a->aaaaaaaa
//b->bbbbbbb
//c->aaaaaaaa
//a->aaaaaaaa
//b->bbbbbbb
//c->aaaaaaaa
//a->aaaaaaaa
//b->bbbbbbb
//c->aaaaaaaa
//
//Process finished with exit code 0
}
}
class Data3{
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
private int number = 1; // 1a 2b 3c
public void printA(){
lock.lock();
try {
// 业务代码
while (number!=1){
// 等待
condition.await();
}
System.out.println(Thread.currentThread().getName() + "->aaaaaaaa");
// 唤醒b
number = 2;
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
// 业务代码
while (number!=2){
// 等待
condition2.await();
}
System.out.println(Thread.currentThread().getName() + "->bbbbbbb");
// 唤醒b
number = 3;
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
// 业务代码
while (number!=3){
// 等待
condition3.await();
}
System.out.println(Thread.currentThread().getName() + "->aaaaaaaa");
// 唤醒b
number = 1;
condition.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
如何判断锁的是谁?永远知道什么锁,锁到底锁的是谁!
锁只有两个,对象和Class
。
8锁现象实际上就是说,在8种情况下,锁究竟是对象还是Class。
package com.atlinxi.gulimall.springdemo.juc.lock8;
import java.util.concurrent.TimeUnit;
/**
* 8锁,就是关于锁的8个问题
*
* 1. TimeUnit.SECONDS.sleep(1); 标准情况下,先输出发短信,后输出打电话
* 2. sendSms延迟4s,先输出发短信,后输出打电话
* 我们使用的同步方式是synchronized,锁是被调用方法的对象,也就是phone,
* 两个线程用的是同一把锁,谁先拿到,谁先执行
*/
public class Test1 {
public static void main(String[] args) {
Phone phone = new Phone();
// 不是先调用的先执行,其实是锁的问题
new Thread(()->{
phone.sendSms();
},"a").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.call();
},"b").start();
}
}
class Phone{
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("sendSms");
}
public synchronized void call(){
System.out.println("call");
}
}
package com.atlinxi.gulimall.springdemo.juc.lock8;
import java.util.concurrent.TimeUnit;
/**
* 3. sendSms()是同步方法,hello()不是同步方法,不存在抢锁,所以是hello先输出
* 4. 两个对象,一个sendSms(),一个call(),先执行call(),
* 两个对象,自然就是两把锁,所以按正常的执行就好。
*
*/
public class Test2 {
public static void main(String[] args) {
// 两个对象,
Phone2 phone = new Phone2();
Phone2 phone2 = new Phone2();
// 不是先调用的先执行,其实是锁的问题
new Thread(() -> {
phone.sendSms();
}, "a").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone2.call();
}, "b").start();
}
}
class Phone2 {
public synchronized void sendSms() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("sendSms");
}
public synchronized void call() {
System.out.println("call");
}
public void hello(){
System.out.println("hello");
}
}
package com.atlinxi.gulimall.springdemo.juc.lock8;
import java.util.concurrent.TimeUnit;
/**
* 5. 增加两个静态同步方法,只有一个对象,sendSms()先输出
* 静态方法,类一加载就有了!锁的是Class,一个类只能有一个class对象,静态方法锁的是Class,所以用的是同一个锁
* 6. 增加两个静态同步方法,两个对象,还是sendSms()先输出,因为一个类只能有一个Class对象,即使是两个对象依然是同一把锁
*/
public class Test3 {
public static void main(String[] args) {
Phone3 phone = new Phone3();
Phone3 phone2 = new Phone3();
// 不是先调用的先执行,其实是锁的问题
new Thread(() -> {
phone.sendSms();
}, "a").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone2.call();
}, "b").start();
}
}
class Phone3 {
public static synchronized void sendSms() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("sendSms");
}
public static synchronized void call() {
System.out.println("call");
}
}
package com.atlinxi.gulimall.springdemo.juc.lock8;
import java.util.concurrent.TimeUnit;
/**
* 7. sendSms()是静态同步方法,call()是普通同步方法,同一个对象,先输出call(),
* 因为不是同一把锁,静态的锁是Class,普通的锁是对象
* 8. sendSms()是静态同步方法,call()是普通同步方法,同一个对象,先输出call(),
* 原因依然是两把锁
*/
public class Test4 {
public static void main(String[] args) {
Phone4 phone = new Phone4();
Phone4 phone2 = new Phone4();
// 不是先调用的先执行,其实是锁的问题
new Thread(() -> {
phone.sendSms();
}, "a").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone2.call();
}, "b").start();
}
}
class Phone4 {
public static synchronized void sendSms() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("sendSms");
}
public synchronized void call() {
System.out.println("call");
}
}
package com.atlinxi.gulimall.springdemo.juc.unsafe;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;
// java.util.ConcurrentModificationException 并发修改异常!
public class ListTest {
public static void main(String[] args) {
// 并发下 ArrayList 不安全的
/**
* 解决方案:
* 1. Vector类线程安全
* 2. Collections.synchronizedList(new ArrayList<>())
* 3. juc解决方案 new CopyOnWriteArrayList<>()
*
*
* Vector的add()是通过synchronized修饰的,只要synchronized修饰的方法效率相对较低
*/
// CopyOnWrite 写入时复制
// 简称 COW 计算机设计领域的一种优化策略;是提高效率的一种方式
// 多个线程调用的时候,list(资源)读取的时候是固定的,
// 写入的时候可能存在一种覆盖操作(后来的线程把之前的线程覆盖掉),
// 写入的时候避免覆盖,造成数据问题!(写入的时候复制一份)
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 1; i <= 10; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(list);
}).start();
}
}
}
package com.atlinxi.gulimall.springdemo.juc.unsafe;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
// new HashSet<>() java.util.ConcurrentModificationException
public class SetTest {
public static void main(String[] args) {
/**
* 解决方案:
* 1. Collections.synchronizedSet(new HashSet<>());
* 2. juc解决方案,new CopyOnWriteArraySet<>()
*/
Set<String> set = new CopyOnWriteArraySet<>();
for (int i = 0; i < 10; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(set);
}).start();
}
}
}
package com.atlinxi.gulimall.springdemo.juc.unsafe;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class MapTest {
public static void main(String[] args) {
// map是这样用的么? 不是,工作中不用HashMap
// 默认等价于什么? new HashMap<>(16,0.75) 初始化容量,加载因子
Map<String, String> hashMap = new ConcurrentHashMap<>();
for (int i = 0; i < 30; i++) {
new Thread(()->{
hashMap.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 5));
System.out.println(hashMap);
},String.valueOf(i)).start();
}
}
}
与Runnable相比
package com.atlinxi.gulimall.springdemo.juc.callable;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// new Thread(new Runnable()).start();
// new Thread(new FutureTask()).start();
// new Thread(new FutureTask(Callable)).start();
MyThread myThread = new MyThread();
FutureTask<String> futureTask = new FutureTask<>(myThread);
new Thread(futureTask,"a").start();
new Thread(futureTask,"b").start(); // 结果会被缓存,两个线程执行call()的话,只会打印一个call()
String s = futureTask.get(); // 获取Callable的返回结果
// get()可能会产生阻塞,或者使用异步通信来处理
System.out.println(s);
}
}
class MyThread implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("call()");
return "123";
}
}
package com.atlinxi.gulimall.springdemo.juc.add;
import java.util.concurrent.CountDownLatch;
// 减法计数器 等待计数器归零再向下执行
// 场景是 必须要执行任务的时候,再使用
// 原理,每次有线程调用countDown()数量-1,计数器变为0,await()就会被唤醒,继续执行
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
// 总数是6,
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 0; i < 6; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"go out");
countDownLatch.countDown(); // -1
},String.valueOf(i)).start();
}
countDownLatch.await(); // 等待计数器归零,然后再向下执行
System.out.println("close door");
// 不加 countDownLatch.await(); 的结果
// close door的位置是发生变化的
// 0go out
// 3go out
// 2go out
// 1go out
// close door
// 5go out
// 4go out
// 加 countDownLatch.await(); 的结果
// close door 永远是最后一个(等待计数器归零)
// 0go out
// 5go out
// 4go out
// 2go out
// 3go out
// 1go out
// close door
}
}
package com.atlinxi.gulimall.springdemo.juc.add;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
// 加法计数器
public class CyclicBarrierDemo {
public static void main(String[] args) {
/**
* 集齐七颗龙珠召唤神龙
*
* 如果这儿是8,而下面只有7个线程,程序就会停在那儿,
* 因为永远都不能集齐八颗龙珠
*/
CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
System.out.println("召唤神龙成功");
});
for (int i = 0; i < 7; i++) {
// lambda不能操作到 i
final int temp = i;
new Thread(()->{
System.out.println(Thread.currentThread().getName() + temp);
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
// Thread-00
// Thread-55
// Thread-44
// Thread-33
// Thread-22
// Thread-11
// Thread-66
// 召唤神龙成功
}
}
package com.atlinxi.gulimall.springdemo.juc.add;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
//
public class SemaphoreDemo {
public static void main(String[] args) {
// 线程数量(停车位),
// 场景是并发限流,控制最大的线程数
// 多个共享资源互斥的使用
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 6; i++) {
new Thread(()->{
try {
// acquire() 得到
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+"抢到车位");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+"离开车位");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release(); // release() 释放
}
},String.valueOf(i)).start();
}
}
// 0抢到车位
// 2抢到车位
// 1抢到车位
// 0离开车位
// 1离开车位
// 3抢到车位
// 4抢到车位
// 2离开车位
// 5抢到车位
// 4离开车位
// 3离开车位
// 5离开车位
}
ReadWriteLock维护一对关联的locks ,一个用于只读操作,一个用于写入。 read lock可以由多个阅读器线程同时进行,只要没有作者。 write lock是独家的。
读可以被多线程同时读,写的时候只能有一个线程去写。
package com.demo.juc.rw;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* ReadWriteLock
*
* 独占锁(写锁) 一次只能被一个线程占有
* 共享锁(读锁) 多个线程可以同时占有
*/
public class ReadWriteLockDemo {
public static void main(String[] args) {
// MyCache myCache = new MyCache();
//
// for (int i = 0; i < 5; i++) {
// final int temp = i;
// new Thread(() -> {
// myCache.put(temp + "", temp);
//
// }, String.valueOf(i)).start();
// }
//
//
// for (int i = 0; i < 5; i++) {
// final int temp = i;
// new Thread(() -> {
// myCache.get(temp + "");
//
// }, String.valueOf(i)).start();
// }
//
// }
/**
* 不使用读写锁的部分结果
* 0写入0
* 4写入4
* 4写入ok4
*
* 应该是0写入,0写入ok这样,上面的话就是线程不安全了
*/
MyCacheLock myCache = new MyCacheLock();
for (int i = 0; i < 5; i++) {
final int temp = i;
new Thread(() -> {
myCache.put(temp + "", temp);
}, String.valueOf(i)).start();
}
for (int i = 0; i < 5; i++) {
final int temp = i;
new Thread(() -> {
myCache.get(temp + "");
}, String.valueOf(i)).start();
}
}
/**
*
* 可以看到,加入读写锁后,写线程是每一个线程单独执行,是线程安全的
* 读线程是线程不安全的
*
*
* 0写入0
* 0写入ok0
* 3写入3
* 3写入ok3
* 1写入1
* 1写入ok1
* 2写入2
* 2写入ok2
* 4写入4
* 4写入ok4
* 0读取0
* 0读取ok0
* 4读取4
* 1读取1
* 3读取3
* 3读取ok3
* 2读取2
* 4读取ok4
* 2读取ok2
* 1读取ok1
*
* Process finished with exit code 0
*/
}
/**
* 自定义缓存
*/
class MyCache {
private volatile Map<String, Object> map = new HashMap<>();
// 存,写
public void put(String key, Object val) {
System.out.println(Thread.currentThread().getName() + "写入" + key);
map.put(key, val);
System.out.println(Thread.currentThread().getName() + "写入ok" + key);
}
// 取,读
public void get(String key) {
System.out.println(Thread.currentThread().getName() + "读取" + key);
Object o = map.get(key);
System.out.println(Thread.currentThread().getName() + "读取ok" + key);
}
}
class MyCacheLock {
private volatile Map<String, Object> map = new HashMap<>();
// 读写锁,更加细粒度的控制
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
// 存,写,写入的时候,只希望同时只有一个线程写
public void put(String key, Object val) {
readWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "写入" + key);
map.put(key, val);
System.out.println(Thread.currentThread().getName() + "写入ok" + key);
}catch (Exception e){
e.printStackTrace();
}finally {
readWriteLock.writeLock().unlock();
}
}
// 取,读,所有人都可以读
public void get(String key) {
readWriteLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "读取" + key);
Object o = map.get(key);
System.out.println(Thread.currentThread().getName() + "读取ok" + key);
}catch (Exception e){
e.printStackTrace();
}finally {
readWriteLock.readLock().unlock();
}
}
}
读写锁(Readers-Writer Lock)顾名思义是一把锁分为两部分:读锁和写锁,其中读锁允许多个线程同时获得,因为读操作本身是线程安全的,而写锁则是互斥锁,不允许多个线程同时获得写锁,并且写操作和读操作也是互斥的。
总结来说,读写锁的特点是:读读不互斥、读写互斥、写写互斥。
在 Java 语言中,读写锁是使用 ReentrantReadWriteLock 类来实现的,其中:
ReentrantReadWriteLock.ReadLock 表示读锁,它提供了 lock 方法进行加锁、unlock 方法进行解锁。
ReentrantReadWriteLock.WriteLock 表示写锁,它提供了 lock 方法进行加锁、unlock 方法进行解锁。
package com.atlinxi.gulimall.springdemo.juc.rw;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockDemo2 {
public static void main(String[] args) {
// 1. 读读不互斥
// 多个线程可以同时获取到读锁,称之为读读不互斥
// 创建写锁
final ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
final ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
final ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
// 不能这么创建读写锁,明显不是一个对象了,怎么可能互斥呢,被自己蠢哭了
// final ReentrantReadWriteLock.ReadLock readLock = new ReentrantReadWriteLock().readLock();
// final ReentrantReadWriteLock.WriteLock writeLock = new ReentrantReadWriteLock().writeLock();
// for (int i = 0; i < 5; i++) {
// new Thread(() -> {
// try {
// readLock.lock();
//
// System.out.println(Thread.currentThread().getName() + "=》得到锁");
// TimeUnit.SECONDS.sleep(1);
// } catch (InterruptedException e) {
// e.printStackTrace();
// } finally {
// System.out.println(Thread.currentThread().getName() + "=》释放锁");
// readLock.unlock();
// }
//
// }).start();
// }
/**
* Thread-3=》得到锁
* Thread-2=》得到锁
* Thread-0=》得到锁
* Thread-4=》得到锁
* Thread-1=》得到锁
* Thread-2=》释放锁
* Thread-3=》释放锁
* Thread-1=》释放锁
* Thread-4=》释放锁
* Thread-0=》释放锁
*
*
*/
// 读锁和写锁同时使用是互斥的(也就是不能同时获得),这称之为读写互斥
// for (int i = 0; i < 5; i++) {
// new Thread(() -> {
// try {
// readLock.lock();
//
// System.out.println(Thread.currentThread().getName() + "=》读锁得到锁");
// TimeUnit.SECONDS.sleep(1);
// } catch (InterruptedException e) {
// e.printStackTrace();
// } finally {
// System.out.println(Thread.currentThread().getName() + "=》读锁释放锁");
// readLock.unlock();
// }
//
// }).start();
// }
//
//
//
// for (int i = 0; i < 5; i++) {
// new Thread(() -> {
// try {
// writeLock.lock();
//
// System.out.println(Thread.currentThread().getName() + "=》写锁得到锁");
// TimeUnit.SECONDS.sleep(1);
// } catch (InterruptedException e) {
// e.printStackTrace();
// } finally {
// System.out.println(Thread.currentThread().getName() + "=》写锁释放锁");
// writeLock.unlock();
// }
//
// }).start();
// }
/**
* Thread-0=》读锁得到锁
* Thread-1=》读锁得到锁
* Thread-4=》读锁得到锁
* Thread-0=》读锁释放锁
* Thread-1=》读锁释放锁
* Thread-4=》读锁释放锁
* Thread-5=》写锁得到锁
* Thread-5=》写锁释放锁
* Thread-8=》写锁得到锁
* Thread-8=》写锁释放锁
* Thread-9=》写锁得到锁
* Thread-9=》写锁释放锁
* Thread-2=》读锁得到锁
* Thread-3=》读锁得到锁
* Thread-3=》读锁释放锁
* Thread-2=》读锁释放锁
* Thread-6=》写锁得到锁
* Thread-6=》写锁释放锁
* Thread-7=》写锁得到锁
* Thread-7=》写锁释放锁
*
* Process finished with exit code 0
*
*
*
*
*
*
*/
// 多个线程同时使用写锁也是互斥的,这称之为写写互斥
for (int i = 0; i < 5; i++) {
new Thread(() -> {
try {
writeLock.lock();
System.out.println(Thread.currentThread().getName() + "=》写1锁得到锁");
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName() + "=》写1锁释放锁");
writeLock.unlock();
}
}).start();
}
for (int i = 0; i < 5; i++) {
new Thread(() -> {
try {
writeLock.lock();
System.out.println(Thread.currentThread().getName() + "=》写2锁得到锁");
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName() + "=》写2锁释放锁");
writeLock.unlock();
}
}).start();
}
/**
*
* Thread-0=》写1锁得到锁
* Thread-0=》写1锁释放锁
* Thread-1=》写1锁得到锁
* Thread-1=》写1锁释放锁
* Thread-4=》写1锁得到锁
* Thread-4=》写1锁释放锁
* Thread-2=》写1锁得到锁
* Thread-2=》写1锁释放锁
* Thread-3=》写1锁得到锁
* Thread-3=》写1锁释放锁
* Thread-5=》写2锁得到锁
* Thread-5=》写2锁释放锁
* Thread-8=》写2锁得到锁
* Thread-8=》写2锁释放锁
* Thread-9=》写2锁得到锁
* Thread-9=》写2锁释放锁
* Thread-6=》写2锁得到锁
* Thread-6=》写2锁释放锁
* Thread-7=》写2锁得到锁
* Thread-7=》写2锁释放锁
*
* Process finished with exit code 0
*
* Process finished with exit code 0
*
*
*
*/
}
}
提高了程序执行性能:多个读锁可以同时执行,相比于普通锁在任何情况下都要排队执行来说,读写锁提高了程序的执行性能。
避免读到临时数据:读锁和写锁是互斥排队执行的,这样可以保证了读取操作不会读到写了一半的临时数据。
读写锁适合多读少写的业务场景,此时读写锁的优势最大。
队列写入元素的时候:如果队列满了,就必须阻塞等待
队列获取元素的时候:如果队列是空的,必须阻塞等待生产
由以上类结构图可以看出,BlockingQueue不是新的东西
使用阻塞队列的场景:多线程并发处理,线程池!
package com.demo.juc.bq;
import java.util.Collection;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
public class TestBq {
public static void main(String[] args) throws InterruptedException {
// List Set Collection
// BlockingQueue 不是新的东西
// test1();
// test2();
// test3();
test4();
}
/**
* 队列满了还添加/队列空了还取出
*
* 抛出异常
*/
public static void test1(){
// 队列的大小
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
// 都是t
System.out.println(arrayBlockingQueue.add("a"));
System.out.println(arrayBlockingQueue.add("b"));
System.out.println(arrayBlockingQueue.add("c"));
// java.lang.IllegalStateException: Queue full 抛出异常!
// System.out.println(arrayBlockingQueue.add("d"));
// 先进先出 abc
System.out.println(arrayBlockingQueue.remove());
System.out.println(arrayBlockingQueue.remove());
System.out.println(arrayBlockingQueue.remove());
// java.util.NoSuchElementException
// System.out.println(arrayBlockingQueue.remove());
}
/**
* 队列满了还添加,会返回false
*
* 队列空了还取出,会返回null
*
* 两种都不抛出异常,有返回值
*/
public static void test2(){
ArrayBlockingQueue<Object> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
// t t t f
System.out.println(arrayBlockingQueue.offer("a"));
System.out.println(arrayBlockingQueue.offer("b"));
System.out.println(arrayBlockingQueue.offer("c"));
System.out.println(arrayBlockingQueue.offer("d"));
// 查看队首元素是谁,a
// 两个方法都是同样的作用
System.out.println(arrayBlockingQueue.element());
System.out.println(arrayBlockingQueue.peek());
// a b c null
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
}
/**
* 等待,阻塞(一直)
*/
public static void test3() throws InterruptedException {
ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);
blockingQueue.put("a");
blockingQueue.put("b");
blockingQueue.put("c");
// 队列没有位置了,就会一直阻塞
// blockingQueue.put("d");
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
// 没有这个元素,一直阻塞
System.out.println(blockingQueue.take());
}
/**
* 等待,阻塞(等待超时)
*/
public static void test4() throws InterruptedException {
ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);
blockingQueue.offer("a");
blockingQueue.offer("b");
blockingQueue.offer("c");
// 等待2s,如果还没有位置就超时退出
// blockingQueue.offer("d",2, TimeUnit.SECONDS);
blockingQueue.poll();
blockingQueue.poll();
blockingQueue.poll();
// 等待2s,如果还没有元素可以取出就退出
blockingQueue.poll(2,TimeUnit.SECONDS);
}
}
没有容量,进去一个元素,必须等待取出来之后,才能再往里面放一个元素。
相当于该队列容量只有1。
package com.demo.juc.bq;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
/**
* 同步队列
*
* 和其他的BlockingQueue不一样,SynchronousQueue不存储元素
* put了一个元素,必须先从里面take出来,否则不能再put进去值
*/
public class SynchronousQueueDemo {
public static void main(String[] args) {
SynchronousQueue<String> synchronousQueue = new SynchronousQueue<>(); // 同步队列
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName() + "put 1");
synchronousQueue.put("1");
System.out.println(Thread.currentThread().getName() + "put 2");
synchronousQueue.put("2");
System.out.println(Thread.currentThread().getName() + "put 3");
synchronousQueue.put("3");
} catch (InterruptedException e) {
e.printStackTrace();
}
},"t1").start();
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+synchronousQueue.take());
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+synchronousQueue.take());
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+synchronousQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
},"t2").start();
/**
* t1put 1
* t21
* t1put 2
* t22
* t1put 3
* t23
*
*
* 如果是阻塞队列就是全放进去,然后再全取出来
*/
}
}
当所有的核心线程都在干活时,新添加的任务会被添加到这个队列中等待处理,如果队列满了,则新建非核心线程执行任务。常见的workQueue类型有:
SynchronousQueue:是一个特殊的阻塞队列,它并不保存任何元素,每次插入操作必须等待另一个线程的移除操作,每次移除操作必须等待另一个线程的插入操作,因此它可以用于两个线程之间进行数据交换。
LinkedBlockingQueue:是一个无界的阻塞队列,底层是由链表实现的,可以存储任意数量的元素。当队列满时,新元素将会一直阻塞等待,直到队列中有空闲位置为止。当队列为空时,获取元素的操作将会阻塞,直到队列中有元素可用。
ArrayBlockingQueue:是一个有界的阻塞队列,底层是由数组实现的,当队列满时,新元素将无法添加到队列中,直到队列中有空闲位置为止。当队列为空时,获取元素的操作将会阻塞,直到队列中有元素可用。
DelayQueue:队列内元素必须实现Delayed接口,这就意味着你传进去的任务必须先实现Delayed接口。这个队列接收到任务时,首先先入队,只有达到了指定的延时时间,才会执行任务
PriorityBlockingQueue:是一个支持优先级的阻塞队列,底层是由堆实现的,可以根据元素的优先级顺序进行排序。当添加元素时,会根据元素的优先级自动排序,获取元素时会返回当前队列中优先级最高的元素。当队列为空时,获取元素的操作将会阻塞,直到队列中有元素可用。
线程池常问问题:三大方法、7大参数、4种拒绝策略
程序的运行,本质:占用系统的资源!优化资源的使用!
线程池、连接池、内存池、对象池。。。。。。创建和销毁十分浪费资源。
池化技术:开启和关闭线程是非常消耗资源的,我们事先准备好一些资源,有人要用,就来拿,用完之后再还回去。
线程池的好处:
降低资源的消耗
通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗。
提高响应的速度
重复利用线程池中线程,不需要每次都创建
方便管理
线程对服务器来说是稀缺资源,如果无限制的去创建,肯定会导致系统的访问速度下降。
可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。
总结
:线程复用、可以控制最大并发、管理线程。
这就类似于公司只要有一个新任务就招一个员工
,最终会将公司的资源耗尽,这和我们的业务一样,最终分配给我们的堆空间或者栈空间,内存都是有限的,
如果在我们高并发系统里,比如这个业务非常大,要进行100W的异步任务查询,现在有100W个请求进来,假设一个请求就要开启10个异步任务,直接就会new 1000W个Thread,肯定会导致我们资源耗尽而最终的系统崩溃
我们以后在业务代码中,继承Thread类,实现Runnable接口,实现Callable接口,以上三种启动线程的方式都不用
,将所有的多线程异步任务都交给线程池执行
这就好比我们公司中50个员工,有任务的话就交给其中一个,如果都有活儿,就等处理完了再处理这个任务,这样做的好处就是我们达到了资源控制
我们只有这50个人,消耗的资源也就是这50个人的资源
Java里面线程池的顶级接口是java.util.concurrent.Executor
,但是严格意义上讲 Executor 并不是一个线程池,而只是一个执行线程的工具。
真正的线程池接口是 java.util.concurrent.ExecutorService
。 要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在 java.util.concurrent.Executors 线程工厂类里面提供了一些静态工厂
,生成一些常用的线程池。官方建议使用Executors(但阿里不推荐,听阿里的)
工程类来创建线程池对象。 Executors
类中有个创建线程池的方法如下:
// ExecurtorService:真正的线程池接口。常见子类ThreadPoolExecutors
// 执行线程任务,没返回值
void execute(Runnable command);
// 执行线程任务,有返回值
<T> Future<T> submit(Callable<T> task);
// 关闭连接池
void shutdown();
Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
package com.demo.juc.pool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
// 用Executors或new ThreadPoolExecutor()创建线程池实际上都是实现ExecutorService
// newFixedThreadPool 一个固定数量的线程池
// 这个线程池不应该是每一个异步任务都创建一个线程池
// 而是应该整个系统一个线程池,大家都把自己的任务交给这个池里执行
// 当前系统中池只有一两个,可能有核心业务的或者非核心业务的
// 每个异步任务,直接提交给线程池,让它自己去执行
// Executors 工具类、3大方法
// 使用了线程池之后,使用线程池来创建线程
public class Demo01 {
public static void main(String[] args) {
// ExecutorService threadPool = Executors.newSingleThreadExecutor();// 单个线程
// ExecutorService threadPool = Executors.newFixedThreadPool(5); // 创建一个固定的线程池大小
ExecutorService threadPool = Executors.newCachedThreadPool(); // 可伸缩的线程池
try {
for (int i = 0; i < 10; i++) {
// submit可以传入Runnable接口和Callable接口,可以获取到任务的返回值
// execute 可以只能传入Runnable接口,不能获取到任务的返回值
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+"ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 线程池用完,程序结束,关闭线程池
threadPool.shutdown();
}
/**
*
* 以上三个不同的线程池操作线程的结果分别是
*
* pool-1-thread-1ok
* pool-1-thread-1ok
* pool-1-thread-1ok
* pool-1-thread-1ok
* pool-1-thread-1ok
* pool-1-thread-1ok
* pool-1-thread-1ok
* pool-1-thread-1ok
* pool-1-thread-1ok
* pool-1-thread-1ok
*
* Process finished with exit code 0
* 同一个线程
*
*
*
*
*
*
*pool-1-thread-1ok
* pool-1-thread-3ok
* pool-1-thread-2ok
* pool-1-thread-5ok
* pool-1-thread-4ok
* pool-1-thread-3ok
* pool-1-thread-1ok
* pool-1-thread-4ok
* pool-1-thread-5ok
* pool-1-thread-2ok
*
* Process finished with exit code 0
*
*
*
*
*
*
* newCachedThreadPool()
*
* 如果同时执行10个任务,它创建的就有10个线程
*
* 测试如果是100个任务,它创建的是三十多个线程(这个数量没有逻辑)
*
*
*/
}
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
// 发现三大方法的本质是ThreadPoolExecutor()
public ThreadPoolExecutor(int corePoolSize, // 核心线程池大小【一直存在,除非设置allowCoreThreadTimeOut】
int maximumPoolSize, // 最大核心线程池大小【最大线程数量】
long keepAliveTime, // 存活时间【超时了,没有人调用就会被释放,释放空闲的线程(非核心线程)】
TimeUnit unit, // 超时时间单位
BlockingQueue<Runnable> workQueue, // 阻塞队列
// 如果任务有很多,就会将目前多的任务放在队列里面。
// 只要有线程空闲,就会去队列里面取出新的任务继续执行。
ThreadFactory threadFactory, // 线程工厂:创建线程的,一般不用动
// 工厂都是默认的,也可以自定义,
// 比如每次来创建线程的时候,线程的名字有自己的约束,我们就可以写自己的创建工厂
RejectedExecutionHandler handler // 拒绝策略
// 如果队列满了,按照我们指定的拒绝策略拒绝执行任务 ) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
/** 工作顺序:
*
* 1. 线程池创建,准备好 core 数量的核心线程,准备接受任务
* 2. 新的任务进来,用 core 准备好的空闲线程执行。
* 3. core 满了,就将再进来的任务放入阻塞队列中。空闲的 core 就会自己去阻塞队列获取任务执行
* 4. 阻塞队列满了,就直接开新线程执行,最大只能开到 max 指定的数量
* max 都执行好了。Max-core 数量空闲的线程会在 keepAliveTime 指定的时间后自动销毁。
* 最终保持到 core 大小
*
* new LinkedBlockingQueue<>();默认是Integer的最大值。内存不够
* 5. 如果线程数开到了 max 的数量,还有新任务进来,就会使用 reject 指定的拒绝策略进行处理
* 6. 所有的线程创建都是由指定的 factory 创建的
*
*
* 面试题
*
* 一个线程池,core 7,max 20,queue 50,100并发进来怎么分配的?
*
* 7个会立即得到执行,50个会进入队列,再开13个进行执行。剩下的30个就使用拒绝策略。
*/
package com.atlinxi.gulimall.springdemo.juc.pool;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* ThreadPoolExecutor.CallerRunsPolicy() 哪来的去哪里,下面的测试是main线程执行的,不理解。。。
* 不启动线程池线程,直接调用run方法,这样的话实际上就是同步调用了。
*
* ThreadPoolExecutor.AbortPolicy() 超过最大线程直接报错;
* 丢弃任务并抛出RejectedExecutionException异常,默认策略
*
* ThreadPoolExecutor.DiscardPolicy() 队列满了,丢掉任务,不会抛出异常
*
* ThreadPoolExecutor.DiscardOldestPolicy() 队列满了,尝试去和最早(队列最前面)的竞争,也不会抛出异常
* 如果最早的线程执行完就会执行它,否则丢掉任务
*
* 同时jdk也为我们提供了RejectedExecutionHandler接口,可以根据实际应用场景去自定义策略
*/
public class PoolTest {
public static void main(String[] args) {
// 自定义线程池,工作中创建线程池只会用这种方式
// 最大线程到底该如何定义
// 1. cpu密集型 如果服务器是12核的,就能支持12条线程同时执行,
// 几核就定义为几,可以保证cpu效率最高
// 获取cpu的核数
// 这就是cpu密集型的逻辑
sout(Runtime.getRuntime().availableProcessors());
// 2. io密集型。判断程序中十分耗io的线程,大于这个数即可,通常会设置两倍,
// 除了这15个,还会剩下15个执行别的线程,就不会造成系统的阻塞
// 程序中有15个大型任务,io十分占用资源
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
2,
5,
3,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy());// 最大线程数和队列都满了,但是还有人进来,不处理这个线程,并抛出异常
try {
// 最大承载:队列 + 最大线程数
for (int i = 0; i < 9; i++) {
threadPoolExecutor.execute(()->{
System.out.println(Thread.currentThread().getName()+"ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPoolExecutor.shutdown();
}
/**
* 线程池的线程使用顺序
*
* 核心线程数 -》 队列 -》 最大线程数 -》 拒绝策略
*
* 6个任务以内2个线程执行
* 6个任务3个线程执行
* 7个任务4个线程执行
* 8个任务5个线程执行
* 9个任务由于超过了最大线程数,我们的拒绝策略又是AbortPolicy(),所以报java.util.concurrent.RejectedExecutionException
*
*/
}
}
ForkJoin在jdk1.7,并执行任务!提高效率,大数据量(上亿)!
大数据:Map Reduce(把大任务拆分为小任务)
ForkJoin特点:工作窃取
a线程执行到一半儿,b线程已经执行完毕,此时,b线程会把a线程的任务偷过来执行。
这个里面维护的是一个双端队列,所以从上面可以执行,从下面也可以执行。
package com.atlinxi.gulimall.springdemo.juc.forkjoin;
import java.util.concurrent.RecursiveTask;
/**
* 求和计算的任务
*
* 如何使用forkjoin
*
* 1. forkjoinpool 通过它来执行
* 2. 计算任务 forkjoinpool.execute(ForkJoinTask task)
* 3. 计算类要继承RecursiveTask
*/
public class ForkJoinDemo extends RecursiveTask<Long> {
private Long start;
private Long end;
public ForkJoinDemo(Long start, Long end) {
this.start = start;
this.end = end;
}
// 临界值,可以通过调整temp的值来进行调优
private Long temp = 10000L;
public static void main(String[] args) {
int sum = 0;
// 10_0000_0000 代表 10亿
for (int i = 0; i < 10_0000_0000; i++) {
sum += i;
}
System.out.println(sum);
}
// 计算方法
@Override
protected Long compute() {
if ((end-start)<temp){
Long sum = 0L;
for (Long i = start; i < end; i++) {
sum += i;
}
return sum;
}else { // forkjoin
long middle = (start + end) / 2; // 中间值
ForkJoinDemo task1 = new ForkJoinDemo(start, middle);
task1.fork(); // 拆分任务,把任务压入线程队列
ForkJoinDemo task2 = new ForkJoinDemo(middle+1, end);
task2.fork();
return task1.join() + task2.join();
}
}
}
package com.atlinxi.gulimall.springdemo.juc.forkjoin;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;
/**
* 同一个任务,别人效率高你几十倍
*/
public class Test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// sum=499999999500000000时间:9775
// test1();
// sum=499934463999828390时间:4985
// test2();
// sum=500000000500000000时间:355
// test3();
}
// 普通程序员
public static void test1(){
long start = System.currentTimeMillis();
Long sum = 0L;
for (Long i = 0L; i < 10_0000_0000; i++) {
sum += i;
}
long end = System.currentTimeMillis();
System.out.println("sum=" + sum + "时间:" + (end - start));
}
// 会使用ForkJoin
public static void test2() throws ExecutionException, InterruptedException {
long start = System.currentTimeMillis();
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask<Long> task = new ForkJoinDemo(0L, 10_0000_0000L);
ForkJoinTask<Long> submit = forkJoinPool.submit(task);
Long sum = submit.get();
// execute(task) 执行任务,没有返回值
// submit(task) 提交任务,有返回值
long end = System.currentTimeMillis();
System.out.println("sum=" + sum + "时间:" + (end - start));
}
// 使用stream并行流
public static void test3(){
long start = System.currentTimeMillis();
// Stream并行流
// range() 和 rangeClosed() 的区别,就是是否闭合
long sum = LongStream.rangeClosed(0L, 10_0000_0000L).parallel().reduce(0, Long::sum);
long end = System.currentTimeMillis();
System.out.println("sum=" + sum + "时间:" + (end - start));
}
}
Future设计的初衷:对将来的某个事件的结果进行建模
package com.atlinxi.gulimall.springdemo.juc.future;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/**
* 异步调用:Ajax
*
* 异步执行
* 成功回调
* 失败回调
*/
public class Demo01 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 发起一个请求
// 没有返回值的 runAsync 异步回调
// CompletableFuture future = CompletableFuture.runAsync(()->{
// try {
// TimeUnit.SECONDS.sleep(2);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// System.out.println(Thread.currentThread().getName()+"runAsync=>Void");
// });
//
// System.out.println("2222");
//
// future.get(); // 获取阻塞执行结果
// 2222
//ForkJoinPool.commonPool-worker-1runAsync=>Void
// 有返回值的异步回调
// ajax,成功和失败的回调
// 失败返回的是错误信息
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread().getName()+"supplyAsync=>Integer");
System.out.println(10/0);
return 1024;
});
//whenComplete() 正常执行的回调函数,t 正常的返回结果,u 没错误是null,有错误是错误信息
// exceptionally() 发生异常的回调函数,可以获取异常信息并返回指定的值
Integer integer = future.whenComplete((t, u) -> {
System.out.println(t);
System.out.println(u);
}).exceptionally((e) -> {
System.out.println(e.getMessage());
return 233;
}).get();
System.out.println(integer);
}
}
部分内容转载自:
https://blog.csdn.net/Saintmm/article/details/121092830
https://www.bilibili.com/video/BV1B7411L7tE/?p=8&spm_id_from=pageDriver&vd_source=64c73c596c59837e620fed47fa27ada7
https://blog.csdn.net/sufu1065/article/details/124722777
https://baijiahao.baidu.com/s?id=1763862719437069832&wfr=spider&for=pc
无论你在背后喊刘怡婷或房思琪,我都会回头的。
房思琪的初恋乐园
林奕含