JUC
1.线程和进程
进程、线程
java中默认存在两个线程:main、GC
线程创建 :Thread、Runnable(效率比Callable低)、Callable
java是没有权限开启线程的,它是通过本地方法实现的(通过底层C++实现对硬件的操作)
并发、并行
并发(多线程操作同一资源)
- 单个cpu快速交替
并行(多个人一起走)
- 多核cpu执行多个线程;线程池
public class Test {
public static void main(String[] args) {
//获取cpu的核数
//cpu密集型,IO密集型
System.out.println(Runtime.getRuntime().availableProcessors());
}
}
并发本质:充分利用CPU的资源
线程状态
public enum State {
//新生
NEW,
//运行
RUNNABLE,
//阻塞
BLOCKED,
//等待
WAITING,
//超时等待
TIMED_WAITING,
//死亡
TERMINATED;
}
wait/sleep区别
1.父类区别
wait=>Object
sleep=>Thread
睡眠常用
import java.util.concurrent.TimeUnit;
//企业开发睡眠常用
TimeUnit.DAYS.sleep();
2.关于锁的释放
wait==>会释放锁
sleep==>不会释放锁
3.使用范围
wait==>必须在同步代码块中
sleep==>任何范围
4.异常捕获
- wait不需要捕获异常
- sleep必须捕获异常
2.Lock(重点)
Synchronize
线程就是一个单独的资源类,没有任何属性操作
属性、方法
//传统的synchronize锁
package com.xx.demo01;
import com.sun.scenario.animation.shared.TimelineClipCore;
/**
* 资源类
*/
public class TicketSale {
public static void main(String[] args) {
Ticket ticket = new Ticket();
new Thread(() -> {
for (int i = 0; i < 40; i++) {
ticket.sale();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 40; i++) {
ticket.sale();
}
}, "B").start();
}
}
class Ticket {
//属性、方法
private int number = 50;
//方法
public synchronized void sale() {
if (number > 0) {
System.out.println(Thread.currentThread().getName() + "卖了" + number + "张票;剩余" + --number + "张票");
}
}
}
注意点:遇见函数式接口可以考虑使用lamda表达式进行简化
Lock
使用方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ThazVfm9-1687276916853)(C:\Users\NM\AppData\Roaming\Typora\typora-user-images\image-20230217110211796.png)]
package com.xx.demo01;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TicketSleDemo {
public static void main(String[] args) {
Ticket ticket = new Ticket();
new Thread(() -> {for(int i = 0; i < 40; i++) ticket.sale();}, "A").start();
new Thread(() -> {for(int i = 0; i < 40; i++) ticket.sale();}, "B").start();
}
}
/**
* lock三部曲
* 1、new ReentrantLock();
* 2、lock.lock();并且try{}catch(){}finally{}
* 3、finally=>lock.unlock()
*/
class Ticket01 {
//属性、方法
private int number = 50;
//Lock锁
Lock lock = new ReentrantLock();
//方法
public void sale() {
//开锁
lock.lock();
try {
if (number > 0) {
System.out.println(Thread.currentThread().getName() + "卖了" + number + "张票;剩余" + --number + "张票");
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
//解锁
lock.unlock();
}
}
}
非公平锁:先来后到,排队
公平锁:可以插队(默认使用)
synchronized和lock区别
1、synchronized 内置的java关键字,lock一个类
2、synchronized 无法判断获取锁的状态,lock可以判断
3、synchronized 自动释放锁,lock必须手动释放!不释放照成死锁
4、synchronized 线程阻塞其他线程会傻傻的等待,lock不一定会一直等待下去
5、synchronized 可重入锁,不可中断,非公平;lock,可重入锁,可以判断锁,非公平(自己设置)
6、synchronized 适用少量同步代码;lock 适用大量同步代码
4.生产者和消费者
传统的synchronized 生产者消费者问题
package com.xx.demo01;
//线程通信
public class TestPc {
public static void main(String[] args) {
Data data = new Data();
new Thread(() -> {for (int i = 0; i < 20; i++) data.increment();},"A").start();
new Thread(() -> {for (int i = 0; i < 20; i++) data.decrement();},"B").start();
}
}
/**
* 生产者消费者问题:
* 判断等待、业务、通知唤醒
*/
class Data {
//属性
private int number = 0;
//方法
//加法
public synchronized void increment() {
if (number != 0) {
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
number++;
System.out.println(Thread.currentThread().getName() + "==>" + number);
//唤醒
this.notifyAll();
}
//减法
public synchronized void decrement() {
if (number == 0) {
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
number--;
System.out.println(Thread.currentThread().getName() + "==>" + number);
//唤醒
this.notifyAll();
}
}
面试点:存在A,B,C,D 4个线程!注意虚假唤醒
注意点:防止虚假唤醒
解决方案:将if判断变为while判断
变更后的代码
package com.xx.demo01;
//线程通信
public class TestPc {
public static void main(String[] args) {
Data data = new Data();
new Thread(() -> {for (int i = 0; i < 20; i++) data.increment();},"A").start();
new Thread(() -> {for (int i = 0; i < 20; i++) data.decrement();},"B").start();
}
}
/**
* 生产者消费者问题:
* 判断等待、业务、通知唤醒
*/
class Data {
//属性
private int number = 0;
//方法
//加法
public synchronized void increment() {
while (number != 0) {
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
number++;
System.out.println(Thread.currentThread().getName() + "==>" + number);
//唤醒
this.notifyAll();
}
//减法
public synchronized void decrement() {
while (number == 0) {
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
number--;
System.out.println(Thread.currentThread().getName() + "==>" + number);
//唤醒
this.notifyAll();
}
}
JUC
官方文档使用方法:
实现代码
package com.xx.demo01;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TestPcA {
public static void main(String[] args) {
DataA data = new DataA();
new Thread(() -> {for (int i = 0; i < 20; i++) data.increment();},"A").start();
new Thread(() -> {for (int i = 0; i < 20; i++) data.decrement();},"B").start();
new Thread(() -> {for (int i = 0; i < 20; i++) data.decrement();},"C").start();
}
}
class DataA {
//属性
private int number = 0;
Lock lock = new ReentrantLock();
//同步监视器
Condition notFull = lock.newCondition();
//方法
public void increment() {
lock.lock();
try {
while (number != 0) {
//等待
notFull.await();
}
number++;
System.out.println(Thread.currentThread().getName() + "的值为==>" + number);
//唤醒
notFull.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decrement() {
lock.lock();
try {
while (number == 0) {
//等待
notFull.await();
}
number--;
System.out.println(Thread.currentThread().getName() + "的值为==>" + number);
//唤醒
notFull.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
Condition 实现精准唤醒
package com.xx.demo01;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TestPcB {
public static void main(String[] args) {
DataB data = new DataB();
new Thread(() -> {
for (int i = 0; i < 10; i++) data.printA();
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) data.printB();
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) data.printC();
}, "C").start();
}
}
class DataB {
//属性
Lock lock = new ReentrantLock();
Condition A = lock.newCondition();
Condition B = lock.newCondition();
Condition C = lock.newCondition();
//1A 2B 3C
private int number = 1;
//方法
public void printA() {
lock.lock();
try {
while (number != 1) {
//等待
A.await();
}
number = 2;
System.out.println(Thread.currentThread().getName() + "的值为==>" + "A");
//唤醒
B.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB() {
lock.lock();
try {
while (number != 2) {
//等待
B.await();
}
number = 3;
System.out.println(Thread.currentThread().getName() + "的值为==>" + "B");
//唤醒
C.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC() {
lock.lock();
try {
while (number != 3) {
//等待
C.await();
}
number = 1;
System.out.println(Thread.currentThread().getName() + "的值为==>" + "C");
//唤醒
A.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
5.8锁现象
package com.xx.lock;
import java.util.concurrent.TimeUnit;
/**
* 1.标准情况,两个线程先打印发短信还是打电话? 先发短息后打电话
* 2.延迟2秒,两个线程先打印发短信还是打电话? 先发短息后打电话
*/
public class Test1 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(() -> {
phone.sendSms();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone.call();
}, "B").start();
}
}
class Phone {
/**
* synchronized 锁的是方法的调用者
* 两个方法用的都是同一个锁,谁(方法)先得到谁先执行
*/
public synchronized void sendSms() {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("sendSms");
}
public synchronized void call() {
System.out.println("call");
}
}
package com.xx.lock;
import java.util.concurrent.TimeUnit;
/**
* 3、增加两个静态同步方法,只有一个对象,先打印发短信还是打电话? 发短息
* 解释:static是静态方法,在类加载的时候就存在,所以它是一个Class模板,使用synchronized锁的时候,锁的就是Class
* 对于phone这个对象来说,它存在唯一一个Class Phone3.class;
* 4、增加两个静态同步方法,两个对象,先打印发短信还是打电话? 发短息
*/
public class Test3 {
public static void main(String[] args) {
Phone3 phone1 = new Phone3();
Phone3 phone2 = new Phone3();
new Thread(() -> {
phone1.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(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("sendSms");
}
public static synchronized void call() {
System.out.println("call");
}
public void hello(){
System.out.println("hello");
}
}
package com.xx.lock;
import java.util.concurrent.TimeUnit;
/**
* 5、增加一个普通方法,先打印普通方法还是同步方法? 普通方法
* 6、两个对象,两个同步方法,发短信增加延迟,先打印发短息还是打电话? 打电话
*/
public class Test2 {
public static void main(String[] args) {
Phone2 phone1 = new Phone2();
Phone2 phone2 = new Phone2();
new Thread(() -> {
phone1.sendSms();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone2.call();
}, "B").start();
}
}
class Phone2 {
/**
* synchronized 锁的是方法的调用者
* 两个方法用的都是同一个锁,谁(方法)先得到谁先执行
*/
public synchronized void sendSms() {
try {
TimeUnit.SECONDS.sleep(2);
} 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.xx.lock;
import java.util.concurrent.TimeUnit;
/**
* 7、1个静态同步方法,1普通个同步方法,先打印打电话还是发短息? 打电话
* 解析:静态方法锁的是Class模板,普通同步方法锁的是方法的调用者,两者锁的对象不同
* 又发短信有延迟,故先打印打电话
* 8、1个静态同步方法,1普通个同步方法,两个对象,先打印打电话还是发短息? 打电话
*/
public class Test4 {
public static void main(String[] args) {
Phone4 phone1 = new Phone4();
Phone4 phone2 = new Phone4();
new Thread(() -> {
phone1.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(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("sendSms");
}
public synchronized void call() {
System.out.println("call");
}
}
小结
- synchronized 锁的对象只有一个的时候,程序按照顺序执行
- synchronized 锁的对象为两个的时候(即出现两把锁),程序优先执行没有延迟的方法
- 普通同步方法和静态同步方法两者锁的对象是不同的,其一锁的是方法的调用者,其二锁的是的对象的Class模板,优先执行没有延迟的方法
6.集合类不安全
List不安全解决方案
package com.xx.unsafe;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* 解决并发条件下list不安全的问题
* java.util.ConcurrentModificationException 并发修改异常
* 解决方案:
* 1、List<String> list = new Vector<String>(); vector是线程安全的
* 2、List<String> list = Collections.synchronizedList(new ArrayList<>()); 使用collections工具类
* 3、List<String> list = new CopyOnWriteArrayList<>();
* CopyOnWrite 写入时复制,在写入list集合的时候,会发生覆盖,因此通过copy复制一份
* 在写入的时候避免覆盖,防止造成数据丢失问题
* CopyOnWriteArrayList相比较Vector好
* 原因:Vector使用的是synchronized
* CopyOnWriteArrayList使用的是lock锁效率比较高
*/
public class ListTest {
public static void main(String[] args) {
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);
}, String.valueOf(i)).start();
}
}
}
vector add源码
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
CopyOnWriteArrayList add源码
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();
}
}
Set不安全解决方案
package com.xx.unsafe;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* Set 不安全
* java.util.ConcurrentModificationException 并发修改异常
* 解决方案:
*1、Set<String> set = Collections.synchronizedSet(new HashSet<>());
* 2、Set<String> set = new CopyOnWriteArraySet<>();
*/
public class SetTest {
public static void main(String[] args) {
Set<String> set = new CopyOnWriteArraySet<>(new HashSet<>());
for (int i = 1; i < 30; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
hashSet 底层
public HashSet() {
map = new HashMap<>();
}
//add
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
Map不安全解决方案
package com.xx.unsafe;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
* map不安全
* Map<String, String> map = new ConcurrentHashMap<>();
*/
public class MapTest {
public static void main(String[] args) {
Map<String, String> map = new ConcurrentHashMap<>();
for (int i = 1; i < 30; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,5));
System.out.println(map);
},String.valueOf(i)).start();
}
}
}
HashMap<Object, Object> objectObjectHashMap = new HashMap<>(16, 0.75F);
7.Callable
- 有返回值
- 可以抛出异常
- run()方法不同,它使用call()
- 泛型的参数就是方法的返回值
如何启动一个Callable线程
等价关系
new Thread(new Runnable).start();<===>new Thread(new FutureTask<>(new Callable)).start();
package com.xx.callable;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyThread myThread = new MyThread();
FutureTask<Integer> futureTask = new FutureTask<>(myThread);
new Thread( futureTask).start();
//获取返回值 futureTask.get()用于接收返回值
Integer integer = futureTask.get();
System.out.println(integer);
/**
* 等价推演
* new Thread(new Runnable).start();<===>new Thread(new FutureTask<>(new Callable)).start();
* 解析:FutureTask是Runnable的实现类,而Callable是FutureTask其中一个构造方法的参数
*/
// new Thread(futureTask).start();
}
}
class MyThread implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("call()");
return 1;
}
}
注意点
- 如果开启两个线程,也只会打印一个结果。**解析:**结果会被缓存,提高效率
- futureTask.get()方法可能会出现阻塞,需要放到最后,可以通过异步通信来处理
8.常用的辅助类
1、CountDownLatch
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q7Ab8Wqx-1687276916863)(C:\Users\NM\AppData\Roaming\Typora\typora-user-images\image-20230221163030046.png)]
package com.xx.add;
import java.util.concurrent.CountDownLatch;
/**
* 计数器
*/
public class CountDownLatchTest {
public static void main(String[] args) throws InterruptedException {
//计数的总数为 6
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 1; i <= 6; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "go out");
//总数-1
countDownLatch.countDown();
}, String.valueOf(i)).start();
}
//计数器清零
countDownLatch.await();
System.out.println("close");
}
}
原理
countDownLatch.countDown();
//总数-1
countDownLatch.await();
//等待计数器归零再向下执行
每次有线程调用countDown() 数量-1,假设计数器为0时,countDownLatch.await()会被唤醒,继续执行
2、CyclicBarrier
加法计数器
package com.xx.add;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
//加法计数器
public class CyclicBarrierTest {
public static void main(String[] args) {
//parties 总数
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
System.out.println("线程数为7");
});
for (int i = 1; i <= 7; i++) {
final int param = i;
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "--->" + param);
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (BrokenBarrierException e) {
throw new RuntimeException(e);
}
}).start();
}
}
}
3.Semaphore
信号量
package com.xx.add;
import java.util.concurrent.Semaphore;
public class SemaphoreTest {
public static void main(String[] args) {
//限流数为3 3个车位
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <=6 ; i++) {
new Thread(()->{
//获得
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+"获得了");
System.out.println(Thread.currentThread().getName()+"释放了");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}finally {
//释放
semaphore.release();
}
},String.valueOf(i)).start();
}
}
}
semaphore.acquire();获得,假如满了,等待释放
semaphore.release();释放,唤醒等待线程
作用:多个共享资源互斥,限流
9.ReadWriteLock
package com.xx.rw;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 读写锁
*/
public class ReadWriteLockTest {
public static void main(String[] args) {
MyCacheLock myCacheLock = new MyCacheLock();
//5个线程写
for (int i = 1; i < 5; i++) {
final int temp = i;
new Thread(()->{
myCacheLock.put(temp+"",temp);
},String.valueOf(i)).start();
}
//5个线程读
for (int i = 1; i < 5; i++) {
final int temp = i;
new Thread(()->{
myCacheLock.get(temp+"");
},String.valueOf(i)).start();
}
}
}
//缓存
class MyCacheLock {
private volatile Map<String, Object> map = new HashMap<>();
//读写锁:更加细粒度的控制
private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
/**
* put 存,写
* 单线程写入
*/
public void put(String k, Object v) {
readWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "写入");
map.put(k, v);
System.out.println(Thread.currentThread().getName() + "写入完成");
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
readWriteLock.writeLock().unlock();
}
}
/**
* get 取,读
* 多线程读取
*/
public void get(String k) {
readWriteLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "开始读取");
map.get(k);
System.out.println(Thread.currentThread().getName() + "读取完成");
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
readWriteLock.readLock().unlock();
}
}
}
独占锁(写锁,一次只能被一个线程占用),共享锁(读锁,多个线程可以占用同一个资源)
10.阻塞队列
队列阻塞条件:队满和队空
//参数为队列里面元素的数量
BlockingQueue<Object> queue = new ArrayBlockingQueue<>(3);
四组API
方式 | 抛出异常 | 不抛出异常,但存在返回值 | 阻塞等待 | 超时等待 |
---|---|---|---|---|
add | add | offer()超出返回false | put() | offer(E e, long timeout, TimeUnit unit) |
remove | remove | poll()超出返回null | take() | poll(long timeout, TimeUnit unit) |
检测队首元素 | elemnt | peek | - | - |
package com.xx.bq;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
/**
* 同步队列
* SynchronousQueue 不能存储元素
* put一个元素必须取出来才能再put
*/
public class SynchronousQueueTest {
public static void main(String[] args) {
BlockingQueue<String> queue = new SynchronousQueue<>();
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + "put a");
queue.put("a");
System.out.println(Thread.currentThread().getName() + "put b");
queue.put("b");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}, "t1").start();
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + "=>" + queue.take());
System.out.println(Thread.currentThread().getName() + "=>" + queue.take());
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}, "T2").start();
}
}
11.线程池(三大方法,七个参数,4种拒接策略)
优点:线程复用、可以控制最大并发数、管理线程
三大方法
- 1、Executors.newSingleThreadExecutor();单个线程
- 2、Executors.newFixedThreadPool(3);创建一个固定线程池的大小
- 3、Executors.newCachedThreadPool();创建一个可伸缩的线程池
- 本质都是:ThreadPoolExecutor
package com.xx.pool;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 线程池
* 1、Executors.newSingleThreadExecutor();单个线程
* 2、Executors.newFixedThreadPool(3);创建一个固定线程池的大小
* 3、Executors.newCachedThreadPool();创建一个可伸缩的线程池
*/
public class Demo01 {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newSingleThreadExecutor();
// ExecutorService threadPool = Executors.newFixedThreadPool(3);
// ExecutorService threadPool = Executors.newCachedThreadPool();
try {
for (int i = 1; i <= 10; i++) {
//使用线程池创建
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + "--->ok");
});
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
//用完线程池需要关闭
threadPool.shutdown();
}
}
}
七大参数
- int corePoolSize,//核心线程大小
- int maximumPoolSize,//最大核心线程池大小
- long keepAliveTime,//超时时间
- TimeUnit unit,//超时单位
- BlockingQueue workQueue,//阻塞队列
- ThreadFactory threadFactory,//线程池工厂,一般不需要修改.
- RejectedExecutionHandler handler//拒绝策略,
ThreadPoolExecutor源码
public ThreadPoolExecutor(int corePoolSize,
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;
}
四种拒绝策略
- 在ThreadPoolExecutor.AbortPolicy 不处理,并且抛出异常
- 在ThreadPoolExecutor.CallerRunsPolicy 从哪来就回到那里去,例如在本程序中,该策略回答主线程中
- 在ThreadPoolExecutor.DiscardPolicy 简单地删除无法执行的任务。(不执行该任务,也不抛出异常)
- 在ThreadPoolExecutor.DiscardOldestPolicy 如果执行程序没有关闭,则工作队列头部的任务被删除,然后重试执行(可能会再次失败,导致重复)
生产中最好使用自定义线程池
package com.xx.pool;
import java.util.concurrent.*;
/**
* 七大参数:
* - int corePoolSize,//核心线程大小
* - int maximumPoolSize,//最大核心线程池大小
* - long keepAliveTime,//超时时间
* - TimeUnit unit,//超时单位
* - BlockingQueue<Runnable> workQueue,//阻塞队列
* - ThreadFactory threadFactory,//线程池工厂,一般不需要修改.
* - RejectedExecutionHandler handler//拒绝策略,
* 拒绝策略:
* 在ThreadPoolExecutor.AbortPolicy 不处理,并且抛出异常
* 在ThreadPoolExecutor.CallerRunsPolicy 从哪来就回到那里去,例如在本程序中,该策略回答主线程中
* 在ThreadPoolExecutor.DiscardPolicy 简单地删除无法执行的任务。(不执行该任务,也不抛出异常)
* 在ThreadPoolExecutor.DiscardOldestPolicy 如果执行程序没有关闭,则工作队列头部的任务被删除,然后重试执行(可能会再次失败,导致重复)。
*/
public class Demo02 {
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, Runtime.getRuntime().availableProcessors(), 3,
TimeUnit.SECONDS, new LinkedBlockingQueue<>(3), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
//最大线程数=maximumPoolSize+BlockingQueue
//使用线程池要捕获异常
try {
for (int i = 1; i <=9 ; i++) {
threadPoolExecutor.execute(()->{
System.out.println(Thread.currentThread().getName()+"-->OK");
});
}
}catch (Exception e){
e.printStackTrace();
}finally {
//关闭线程池
threadPoolExecutor.shutdown();
}
}
}
注意点:使用线程池创建线程需要捕获异常,并且在使用完以后要关闭线程池,否则会造成资源的浪费
maximumPoolSize 的设置:(优化)
1、IO密集型(看最大的IO为几个 一般设置为该IO的两倍)
2、CPU密集型 (看设备为多少和就设置为多少)Runtime.getRuntime().availableProcessors();//获取当前设备的核数
12.四大函数式接口
1、function(函数型)
public interface Function<T, R> {
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);
package com.xx.function;
import java.util.function.Function;
/**
* 函数型接口
*/
public class Demo01 {
public static void main(String[] args) {
/* Function<String, String> function = new Function<String, String>() {
@Override
public String apply(String o) {
return o;
}
};*/
//lamda简化
Function<String, String> function=(o)->{return o;};
System.out.println(function.apply("abc"));
}
}
2、Predicate(断定型)
public interface Predicate<T> {
/**
* Evaluates this predicate on the given argument.
*
* @param t the input argument
* @return {@code true} if the input argument matches the predicate,
* otherwise {@code false}
*/
boolean test(T t);
package com.xx.function;
import java.util.function.Predicate;
/**
* 段定型接口:有一个参数,返回值为布尔值
*/
public class Demo02 {
public static void main(String[] args) {
/* Predicate<String> predicate= new Predicate<String>(){
@Override
public boolean test(String o) {
return o.isEmpty();
}
};*/
//lamda简化
Predicate<String> predicate = (o) -> {
return o.isEmpty();
};
System.out.println(predicate.test("abc"));
}
}
3、Consumer 消费型
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
package com.xx.function;
import java.lang.invoke.ConstantCallSite;
import java.util.function.Consumer;
/**
* 消费型接口
* 没有返回值,但存在参数
*/
public class Demo03 {
public static void main(String[] args) {
/* Consumer<String> consumer= new Consumer<String>(){
@Override
public void accept(String s) {
System.out.println(s);
}
};*/
//lamda表达式
Consumer<String> consumer = (o) -> {
System.out.println(o);
};
consumer.accept("abc");
}
}
4、Supplier 供给型
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
package com.xx.function;
import java.util.function.Supplier;
/**
* 供给型接口
* 有返回值,但没有参数
*/
public class Demo04 {
public static void main(String[] args) {
/* Supplier<Integer> supplier = new Supplier<Integer>() {
@Override
public Integer get() {
return 1;
}
};*/
Supplier<Integer> supplier = () -> {
return 1;
};
System.out.println(supplier.get());
}
}
13.Stream流式计算
package com.xx.stream;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Stream;
/**
* 筛选:
* 1、ID全部为偶数
* 2、年龄大于23
* 3、用户名转化为大写字母
* 4、用户名字母倒着排序
* 5、只输出一个用户
*/
public class Demo {
public static void main(String[] args) {
User u1 = new User(1, "a", 21);
User u2 = new User(2, "b", 22);
User u3 = new User(3, "c", 23);
User u4 = new User(4, "d", 24);
User u5 = new User(5, "e", 25);
//将用户放入集合中
List<User> list = Arrays.asList(u1, u2, u3, u4, u5);
list.stream().filter(u -> {return u.getId() % 2 == 0;})
.filter(u->{return u.getAge()>23;})
.map((User u)->{return u.getName().toUpperCase();})
.sorted(new Comparator<String>() {
@Override
public int compare(String user1, String user2) {
return user2.compareTo(user1);
}
})
//(user1,user2)->{return user2.compareTo(user1);}
.limit(1)
.forEach(System.out::println);
/*
* forEach(u -> {
System.out.println(u);
});
* <===>forEach(System.out::println)
*
* (user1,user2)->{return user2.compareTo(user1);}
*
* new Comparator<String>() {
@Override
public int compare(String user1, String user2) {
return user2.compareTo(user1);
}
}
* */
}
}
14.ForkJoin
1、将大任务拆分为多个小任务
2、工作窃取:
假如存在A、B两个线程,当A执行完一部分的时候B已经执行完毕,则B会窃取A的部分任务继续执行,提高效率
维护的都是双端队列
![image-20230303165402715](https://img-blog.csdnimg.cn/img_convert/7f91a39f53b1b9992f12c2bd44534bc9.png)
Demo(ForkJoinTask)
package com.xx.forkjoin;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
/**
* 加法计算
* ForkJoin的使用
* 1、ForkJoinPool 来之sing
* 2、计算任务 ForkJoinTask<?> task
* 3、计算类要继承 ForkJoinTask
*/
class ForkJoinDemo extends RecursiveTask<Long> {
private Long start;
private Long end;
private static final Long TEMP = 100L;
public ForkJoinDemo(Long start, Long end) {
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
//如果小于temp的值就使用普通for循环,否则使用forkJoin
if ((end - start) < TEMP) {
Long sum = 0L;
for (Long i = start; i < end; i++) {
sum += i;
}
return sum;
} else {
Long mid = (end + start) / 2;
//
ForkJoinDemo f1 = new ForkJoinDemo(start, mid);
f1.fork();
ForkJoinDemo f2 = new ForkJoinDemo(mid + 1, end);
//拆分任务
f2.fork();
//返回计算结果
return f1.join() + f2.join();
}
}
}
Test
package com.xx.forkjoin;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
public class Test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// test1();
test2();
}
public static void test1() {
long start = System.currentTimeMillis();
Long sum = 0L;
for (Long i = 1L; i < 1_0000_0000L; i++) {
sum += i;
}
long end = System.currentTimeMillis();
System.out.println("执行结果为:" + sum + ";执行时间为" + (end - start));
}
public static void test2() throws ExecutionException, InterruptedException {
long start = System.currentTimeMillis();
//ForkJoin的执行必须new ForkJoinPool
ForkJoinPool forkJoinPool = new ForkJoinPool();
//提交任务
ForkJoinTask<Long> submit = forkJoinPool.submit(new ForkJoinDemo(0L, 1_0000_0000L));
// forkJoinPool.execute(forkJoin);执行任务 没有返回值
//获取结果
Long result = submit.get();
// Long result = forkJoinPool.submit(new ForkJoinDemo(0L, 1_0000L)).get();
long end = System.currentTimeMillis();
System.out.println("执行结果为:" + result + ";执行时间为" + (end - start));
/* try {
Long result = forkJoinPool.submit(new ForkJoinDemo(0L, 1_0000_0000L)).get();
long end = System.currentTimeMillis();
System.out.println("执行结果为:" + result + ";执行时间为" + (end - start));
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}*/
// ForkJoinPool pool = new ForkJoinPool();
// ForkJoinTask<Long> submit = pool.submit(new ForkJoinDemo(0L, 1_0000_0000L));
// try {
// Long result = submit.get();
// System.out.println("result = " + result);
// } catch (InterruptedException | ExecutionException e) {
// e.printStackTrace(System.out);
// }
}
public void test3() {
long start = System.currentTimeMillis();
long end = System.currentTimeMillis();
Long sum = 0L;
System.out.println("执行结果为:" + sum + ";执行时间为" + (end - start));
}
/* public static void test4() {
// 这是Fork/Join框架的线程池
ForkJoinPool pool = new ForkJoinPool();
ForkJoinTask<Integer> submit = pool.submit(new ForkJoinDemo(1, 1001));
try {
Integer result = submit.get();
System.out.println("result = " + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace(System.out);
}
}*/
}
Could not initialize class java.util.concurrent.locks.AbstractQueuedSynchronizer$Node解析:
[(2条消息) Java 运行时发生 NoClassDefFoundError: Could not initialize class 的解决方法_java noclassdeffounderror_亦碎流年的博客-CSDN博客
[(2条消息) java.lang.NoClassDefFoundError: Could not initialize class xxx 原因及解决方法_轻松囧的博客-CSDN博客](https://blog.csdn.net/shenlf_bk/article/details/124847939)
//问题代码
Long mid = (end - start) / 2;
假如开始值为0,结束值为100;
第一次拆分任务的时候前面的为0-50,后面为51-100
第二次拆分任务的时候就不是按照平均值进行拆分了,就会出现多次初始化
public class TestNoClassDefFoundError {
public static void main(String[] args) throws InterruptedException {
TestNoClassDefFoundError sample = new TestNoClassDefFoundError();
sample.getClassWithInitErrors();
}
private void getClassWithInitErrors() throws InterruptedException {
System.out.println("第一次new");
Thread.sleep(500);
try {
//第一次new ClassWithInitErrors类,JVM会加载该类,初始化该类的静态变量或执行静态块
new ClassWithInitErrors();
} catch (Throwable t) {
//因为初始化静态变量失败,所以加载类失败。
t.printStackTrace();
}
Thread.sleep(500);
System.out.println("-----------------------------------------------------");
System.out.println("第二次new");
Thread.sleep(500);
try {
//第二次new ClassWithInitErrors类,JVM不会再加载该类,而是抛出NoClassDefFoundError异常
new ClassWithInitErrors();
} catch (Throwable t) {
t.printStackTrace();
}
Thread.sleep(500);
System.out.println("-----------------------------------------------------");
System.out.println("第三次new");
Thread.sleep(500);
try {
//第三次new ClassWithInitErrors类,JVM不会再加载该类,而是抛出NoClassDefFoundError异常
new ClassWithInitErrors();
} catch (Throwable t) {
t.printStackTrace();
}
}
}
class ClassWithInitErrors {
static int data = 1 / 0;
}
并行流(parallel()流的计算)
- range()方法,创建一个以1为增量步长,从startInclusive(包括)到endExclusive(不包括)的有序的数字流。(区间:[))
- 如果想要数字是闭区间,可以使用 rangeClosed() 方法(区间:[])
range:https://blog.csdn.net/qq_38974634/article/details/81347604
redece:https://blog.csdn.net/lijingronghcit/article/details/108348728
//返回一个流
LongStream longStream = LongStream.rangeClosed(0L, 1_0000_0000L);
//返回一个并行流
LongStream parallel = longStream.parallel();
//并行计算
/*
参数:
Identity : 定义一个元素代表是归并操作的初始值,如果Stream 是空的,也是Stream 的默认结果
Accumulator: 定义一个带两个参数的函数,第一个参数是上个归并函数的返回值,第二个是Strem 中下一个元素。
Combiner: 调用一个函数来组合归并操作的结果,当归并是并行执行或者当累加器的函数和累加器的实现类型不匹配时才会调用此函数。
(subtotal, element) -> subtotal + element<===>Integer::sum
"::"表示方法的引用
*/
long sum = parallel.reduce(0, Long::sum);
//简化
LongStream.rangeClosed(1L, 1_0000_0000L).parallel().reduce(0, Long::sum);
15.异步回调
同步:同步就是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去;执行完一个操作才能执行下一个
异步:异步是指进程不需要一直等下去,而是继续执行下面的操作,不管其他进程的状态。同时执行多个操作
有返回值回调 supplyAsync
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
// System.out.println(Thread.currentThread().getName() + "==>runAsync");
int i = 10 / 0;
return 1;
});
/*
成功回调和失败回调
*/
System.out.println(completableFuture.whenCompleteAsync((t, u) -> {
System.out.println(t);//成功的返回值
System.out.println(u);//失败的异常
}).exceptionally((e) -> {
//参数类型是异常 返回值为Integer
System.out.println(e.getMessage());
return 500;
}).get());
没有返回值的回调 runAsync
//发起请求 没有返回值的回调
CompletableFuture<Void> voidCompletableFuture = CompletableFuture.runAsync(() -> {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + "==>runAsync");
});
System.out.println("main");
//获取结果
voidCompletableFuture.get();
package com.xx.future;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/**
* 异步回调
* CompletableFuture 是Future接口的实现类
* 1、异步执行
* 2、成功回调
* 3、失败回调
*/
public class Demo01 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//发起请求 没有返回值的回调
CompletableFuture<Void> voidCompletableFuture = CompletableFuture.runAsync(() -> {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + "==>runAsync");
});
System.out.println("main");
//获取结果
voidCompletableFuture.get();
// CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName() + "==>runAsync");
// int i = 10 / 0;
// return 1;
// });
// /*
// 成功回调和失败回调
// */
// System.out.println(completableFuture.whenCompleteAsync((t, u) -> {
// System.out.println(t);//成功的返回值
// System.out.println(u);//失败的异常
// }).exceptionally((e) -> {
// //参数类型是异常 返回值为Integer
// System.out.println(e.getMessage());
// return 500;
// }).get());
}
}
}
16.JMM
Volatile是虚拟机提供的
轻量级的同步机制
三大特性:
- 保证可见性
- 不保证原子性
- 禁止指令重排
JMM:java内存模型,一个概念,一个约定
约定:
- 线程解锁前,必须吧共享变量立刻刷会主存
- 线程加锁前,必须读取主存中的最新值到工作内存中
- 加锁和解锁必须为同一把锁
4组,8个操作
存在问题
17.Volatile
可见性(解决当JMM主存中的值发生变化的时候,其他线程也可见)
package com.xx.tvolatile;
import java.util.concurrent.TimeUnit;
//可见性
public class Demo01 {
private volatile static boolean flag = true;
public static void main(String[] args) {
new Thread(() -> {
while (flag) {
}
}).start();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
flag = false;
System.out.println(flag);
}
}
不保证原子性
package com.xx.tvolatile;
/**
* 不保证原子性验证
*/
public class Demo02 {
private volatile static int num = 0;
public static void add(){
num++;
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(()->{
for (int i1 = 0; i1 < 1000; i1++) {
add();
}
}).start();
}
//判断线程是否都存活了
while (Thread.activeCount()>2){
Thread.yield();
}
System.out.println(Thread.currentThread().getName()+num);
}
}
不加锁(lock和synchronized),采用原子类来保证它的原子性
package com.xx.tvolatile;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 不保证原子性验证
*/
public class Demo02 {
//使用原子类来保证原子性
private volatile static AtomicInteger num = new AtomicInteger();
public static void add() {
num.getAndIncrement();
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(() -> {
for (int i1 = 0; i1 < 1000; i1++) {
add();
}
}).start();
}
//判断线程是否都存活了
while (Thread.activeCount() > 2) {
Thread.yield();
}
System.out.println(Thread.currentThread().getName() + num);
}
}
禁止指令重排
指令重排:程序并不是按照所写的那样执行