JUC使用整理笔记
1.org.apache.common.io.IOUtils
1.org.apache.common.io.FileUtils
1.org.apache.common.lang.StringUtils
1.org.apache.common.lang.ArrayUtils
1.org.apache.common.lang.StringEscapeUtils
1.org.apache.http.util.EntityUtils
1.org.apache.common.lang3.StringUtils


PDF获取地址:
链接:https://pan.baidu.com/s/17hGKm4efC8di7azQy9yenA
提取码:4vwx
Java无法开启线程,开启线程使用的是native方法,调用C++
new Thread().start(); //======================== group.add(this); start0(); private native void start0();
线程的状态
public enum State {
//新建、运行、阻塞,等待,超时等待,终止
NEW, RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED;
}
wait/sleep区别
1.所属类不同 wait 是object的方法,sleep是thread的方法
sleep调用:TimeUnit.SECONDS.sleep(1);//为JUC的类
2.是否释放锁:wait 会释放锁,sleep抱着锁不会释放
3.使用范围不同 wait 需要在同步代码块中 sleep在任何地方都可以
4.wait 不需要捕获异常,sleep需要
2.Lock
1.接口
实现类:ReentrantLock ReadWriteLock
2.sychronized与lock区别
1.sychronized是Java的内置关键字,lock是类
2.sychronized无法获取锁状态,lock可以显示指定锁状态
3.sychronized会自动释放锁,lock需要手动释放【死锁】
4.sychronized会等待,lock可以通过trylock不用等待
5.sychronized是可重入锁,不可以中断,非公平,lock可重入锁,可以判断锁(tryLock),非公平【默认】
6.sychronized适合少量代码,lock适合大量代码
生产者消费者模型
//生产者消费者模型
//书写步骤
//判断是否等待 执行业务 通知
//多线程执行任务类的内容: 属性 方法
class Data{
private int num = 0;
public synchronized void increment() throws InterruptedException {
//判断是否等待
if(num != 0){
this.wait();
}
//执行业务
num++;
System.out.println(Thread.currentThread().getName()+ " --->" + num);
//进行通知
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
//判断是否等待
//======================
//这块使用if 多个线程情况下容易出现虚假唤醒问题-----
if(num == 0){
this.wait();
}
//指定业务
num--;
System.out.println(Thread.currentThread().getName()+ " --->" + num);
//通知
this.notifyAll();
}
}
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
}
B --->0
A --->1
B --->0
A --->1
B --->0
A --->1
B --->0
A --->1
B --->0
A --->1
B --->0
A --->1
B --->0
A --->1
B --->0
A --->1
B --->0
2.1虚假唤醒问题
上面代码在多个线程情况下会出现问题,如当为4个线程时,数据就不正确了,是由于虚假唤醒造成的
怎样解决?
可以使用while进行判断
//使用while来进行虚假唤醒等待判断
while (num !=0){
this.wait();
}
2.2sychronized与lock区别

在多线程中,多个线程访问synchronized代码块,线程之间需要通信,此时可以使用wait和notify进行线程间的通信。
而在Lock中,也存在同样的通信操作,Condition取代了对象监视器的使用,使用Condition进行线程间的通信操作,condition.await();//等待操作。condition.signal()通知操作
2.2.1 使用Lock结合condition进行判断
public class PC {
//生产者消费者模型
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
class Data{
private int num = 0;
//使用lock实现
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void increment() throws InterruptedException {
try{
lock.lock();
//================执行业务代码
//判断是否等待
while (num !=0){
condition.await();
}
//执行业务
num++;
System.out.println(Thread.currentThread().getName()+ " --->" + num);
//进行通知
condition.signalAll();
}catch (Exception e){
}finally {
lock.unlock();
}
}
public void decrement() throws InterruptedException {
lock.lock();
try{
//判断是否等待
while (num ==0){
condition.await();
}
//指定业务
num--;
System.out.println(Thread.currentThread().getName()+ " --->" + num);
//通知
condition.signalAll();
}catch(Exception e){
}finally{
lock.unlock();
}
}
}
使用这种方式结果是随机的
2.2.2 condition可以精准的通知和唤醒线程
构造多个condition进行符合条件的唤醒
public class PC {
//生产者消费者模型
public static void main(String[] args) {
Data data = new Data();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.increment1();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "C").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.decrement1();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "D").start();
}
}
class Data {
private int num = 1;
//使用lock实现
Lock lock = new ReentrantLock();
Condition conditionA = lock.newCondition();
Condition conditionB = lock.newCondition();
Condition conditionC = lock.newCondition();
Condition conditionD = lock.newCondition();
public void increment() throws InterruptedException {
try {
lock.lock();
//================执行业务代码
//判断是否等待
while (num != 1) {
conditionA.await();
}
//执行业务
System.out.println(Thread.currentThread().getName() + " --->" + num);
num = 2;
//进行通知
conditionB.signal();
} catch (Exception e) {
} finally {
lock.unlock();
}
}
public void increment1() throws InterruptedException {
try {
lock.lock();
//================执行业务代码
//判断是否等待
while (num != 2) {
conditionB.await();
}
//执行业务
System.out.println(Thread.currentThread().getName() + " --->" + num);
num=3;
//进行通知
conditionC.signal();
} catch (Exception e) {
} finally {
lock.unlock();
}
}
public void decrement() throws InterruptedException {
lock.lock();
try {
//判断是否等待
while (num != 3) {
conditionC.await();
}
//指定业务
System.out.println(Thread.currentThread().getName() + " --->" + num);
num = 4;
//通知
conditionD.signal();
} catch (Exception e) {
} finally {
lock.unlock();
}
}
public void decrement1() throws InterruptedException {
lock.lock();
try {
//判断是否等待
while (num != 4) {
conditionD.await();
}
//指定业务
System.out.println(Thread.currentThread().getName() + " --->" + num);
num = 1;
//通知
conditionA.signal();
} catch (Exception e) {
} finally {
lock.unlock();
}
}
}
2.3锁的8个问题
1.synchronized 锁的对象是方法的调用者
集合不安全
List不安全-CopyOnWriteArrayList
List<String> list = new ArrayList<>();
for (int i = 0; i <= 10; i++) {
new Thread(
() -> {
/**
* 并发下的ArrayList是不安全的
* 会报:java.util.ConcurrentModificationException
*/
list.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(list);
}, String.valueOf(i)
).start();
}
//解决方法
//1.使用Vector,底层是使用synchronized
List<String> vector = new Vector<>();
for (int i = 0; i <= 10; i++) {
new Thread(
() -> {
vector.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(vector);
}, String.valueOf(i)
).start();
}
//2.使用List的顶级接口Collections.synchronizedList
/**
* public void add(int index, E element) {
* synchronized (mutex) {list.add(index, element);}
* }
*/
List<String> vector = Collections.synchronizedList(new ArrayList<>());
for (int i = 0; i <= 10; i++) {
new Thread(
() -> {
vector.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(vector);
}, String.valueOf(i)
).start();
}
/**
* CoryOnWrite COW :写入时复制,计算机领域的一种优化策略
* 多线程读时,其是固定的
* 多线程写时,其是使用复制,从而避免覆盖,造成数据并发修改问题
*/
//3.使用CoryOnWriteArrayList
/**
* private transient volatile Object[] array;
* final ReentrantLock lock = this.lock;
*/
List<String> vector = new CopyOnWriteArrayList<>();
for (int i = 0; i <= 10; i++) {
new Thread(
() -> {
vector.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(vector);
}, String.valueOf(i)
).start();
}
Set不安全-CopyOnWriteArraySet
public static void main(String[] args) {
//Set<String> set = new HashSet<>();
//Set<String> set = Collections.synchronizedSet(new HashSet<>());
Set<String> set = new CopyOnWriteArraySet<>();
for (int i = 0; i <= 10; i++) {
new Thread(
() -> {
/**
* 并发下的HashSet是不安全的
* 会报:java.util.ConcurrentModificationException
*/
set.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(set);
}, String.valueOf(i)
).start();
}
}
Map不安全-ConcurrentHashMap
/**
* 1.一般不使用HashMap
* 2.默认HashMap<>(16,0.75)
*/
//Map<String,String> map = new HashMap<>();
Map<String,String> map = new ConcurrentHashMap<>();
for (int i = 0; i <= 10; i++) {
new Thread(
() -> {
/**
* 并发下的HashMap是不安全的
* 会报:java.util.ConcurrentModificationException
*/
map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0, 5));
System.out.println(map);
}, String.valueOf(i)
).start();
}
}
Callable
1.有返回值 2.会抛出检查异常 3.call()调用
Callable中call方法的调用时,需要使用FutureTask通过中间方式进行调用
new Thread(new Runnable()).start();
new Thread(new FutureTask(new Callable(){})).start(); //FutureTask是Runnable的实现类
1.task.get(): 会阻塞 2.多线程调用多次,任务只会执行一次:有缓存
public class CallableTest {
/**
* 1.task.get(): 会阻塞
* 2.多线程调用多次,任务只会执行一次:有缓存
*/
public static void main(String[] args) throws Exception {
//new Thread(new Runnable()).start();
//new Thread(new FutureTask<String>(new CallTest()),"A").start();
CallTest test = new CallTest();
//FutureTask构造方法中有两种方法 1.Callable 2. Runnable
FutureTask<String> task = new FutureTask<String>(test);
new Thread(task,"A").start();
//call方法只会调用一次
new Thread(task,"B").start();
//get是阻塞操作,异步处理
String str = task.get();
System.out.println(str);
}
}
class CallTest implements Callable<String>{
@Override
public String call() throws Exception {
System.out.println("---------");
return "13";
}
}
常用的辅助类
1.CountDownLatch
允许一个线程或多个线程进行等待,直到其他线程操作完成,它是一个辅助类
如业务:下订单-扣减库存-使用优惠劵-付款-增加积分:使用CountDownLatch初始5个线程,5个线程执行完成后再执行其他方法
/**
* 下订单-扣减库存-使用优惠劵-付款-增加积分-> 通知发货
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
CountDownLatch cdl = new CountDownLatch(5);
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "-- 下订单");
//执行一个减一
cdl.countDown();
},"下订单").start();
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "-- 扣减库存");
//执行一个减一
cdl.countDown();
},"扣减库存").start();
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "-- 使用优惠劵");
//执行一个减一
cdl.countDown();
},"使用优惠劵").start();
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "-- 付款");
//执行一个减一
cdl.countDown();
},"付款").start();
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "-- 增加积分");
//执行一个减一
cdl.countDown();
},"增加积分").start();
//计速器未归零时阻塞
cdl.await();
//计数器归零后向下执行
System.out.println("================");
System.out.println("执行 通知发货");
}
2.CyclicBarrier
允许一组线程全部等待彼此达到共同屏障点的同步辅助类
new CyclicBarrier(线程数,完成后需要执行的任务)
如:批量更新1000万条数据,数据更新完成后写入到ES中
/**
* 批量更新1000万条数据,更新完成后将数据写入ES中
* @param args
*/
public static void main(String[] args) {
CyclicBarrier cb = new CyclicBarrier(5,()->{
System.out.println("更新完成,开始将数据写入到ES中");
});
for (int i = 1; i <=5 ; i++) {
//拿到i
new Thread(
()->{ System.out.println("当前"+Thread.currentThread().getName()+"数据库更新操作完成");
try {
cb.await();
} catch (Exception e) {
e.printStackTrace();
}
},String.valueOf(i)
).start();
}
}
3.Semaphore
信号量【抢车位(5个停车位,10个人来抢)】
限流中用的比较多
方法
semaphore.acquire();//获取资源,如果满了则等待,直到有资源释放为止
semaphore.release();//会将当前信号量释放,然后唤醒等待的线程
作用:多个共享资源互斥使用,【并发限流,控制最大的线程数】
/**
* 10个车主抢5个车位
*/
public class SemaphoreTest {
public static void main(String[] args) {
//5个车位
Semaphore semaphore = new Semaphore(5);
//10个车主抢车位
for (int i = 1; i <=10 ; i++) {
new Thread(()->{
try {
//获取车位
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+"获得车位!");
//等待
TimeUnit.SECONDS.sleep(5);
//离开车位
System.out.println(Thread.currentThread().getName()+"离开车位!");
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
semaphore.release();
}
},String.valueOf(i)).start();
}
}
}
4.读写锁
ReadWriteLock
public class ReadWriteLockTest {
public static void main(String[] args) {
MyCache cache = new MyCache();
/**
* 多线程下出现写多次问题,不能保证写只有一次
*/
for (int i = 1; i <= 10; i++) {
final String key = i + "";
new Thread(() -> {
cache.put(key, Thread.currentThread().getName());
}, String.valueOf(i)).start();
}
for (int i = 1; i <= 10; i++) {
final String key = i + "";
new Thread(() -> {
cache.get(key);
}, String.valueOf(i)).start();
}
}
}
/**
* 自定义缓存
*/
class MyCache {
private volatile Map<String, Object> cache = new HashMap<>();
//使用ReadWriteLock进行更加细粒度的控制
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//写的过程
public void put(String key, Object value) {
readWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "写入" + key);
cache.put(key, value);
System.out.println(Thread.currentThread().getName() + "写入完成");
} catch (Exception e) {
} finally {
readWriteLock.writeLock().unlock();
}
}
//读的过程
public void get(String key) {
readWriteLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "读取" + key);
Object o = cache.get(key);
System.out.println(Thread.currentThread().getName() + "读取完成");
} catch (Exception e) {
} finally {
readWriteLock.readLock().unlock();
}
}
}
5.阻塞队列
如果队里满了,写入时则会阻塞
如果队里是空的,读取时则会阻塞
BlockingQueue


什么情况下需要使用阻塞队列?
多线程并发处理,线程池

阻塞队列的操作:添加,移除
阻塞队列的API
1.抛出异常
2.不抛出异常
3.阻塞等待
4.超时等待
| 方式 | 抛出异常 | 有返回值,不抛出异常 | 阻塞等待 | 超时等待 |
|---|---|---|---|---|
| 添加 | add | offer[false] | put | offer(o,timeout,unit) |
| 移除 | remove | poll[null] | take | pool(int,unit) |
| 判断队列首个元素 | element[NoSuchElementException] | peek[null] |
/**
* 添加删除元素有异常:add remove
*/
@Test
public void hasExc() {
Queue queue = new ArrayBlockingQueue(3);
System.out.println(queue.add(1));
System.out.println(queue.add(2));
System.out.println(queue.add(3));
//java.lang.IllegalStateException: Queue full
System.out.println("---------------------");
//System.out.println(queue.add(4));
System.out.println(queue.remove());
System.out.println(queue.remove());
System.out.println(queue.remove());
System.out.println("*****************");
System.out.println(queue.remove());
}
/**
* 添加删除元素无异常 offer,poll
*/
@Test
public void noExc() {
Queue queue = new ArrayBlockingQueue(3);
System.out.println(queue.offer(1));
System.out.println(queue.offer(2));
System.out.println(queue.offer(3));
System.out.println("---------------------");
System.out.println(queue.offer(4));
System.out.println(queue.poll());
System.out.println(queue.poll());
System.out.println(queue.poll());
System.out.println("*****************");
System.out.println(queue.poll());
}
/**
* 添加删除元素阻塞
* put take
* @throws InterruptedException
*/
@Test
public void block() throws InterruptedException {
ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<>(3);
queue.put(1);
queue.put(2);
queue.put(3);
System.out.println("---------------------");
queue.put(4);
System.out.println(queue.take());
System.out.println(queue.take());
System.out.println(queue.take());
System.out.println("*****************");
System.out.println(queue.take());
}
/**
* 添加删除元素超时
* offer(o,int,timeunit) poll(int,timeunit)
* @throws InterruptedException
*/
@Test
public void noBlock() throws InterruptedException {
ArrayBlockingQueue queue = new ArrayBlockingQueue(3);
System.out.println(queue.offer(1));
System.out.println(queue.offer(2));
System.out.println(queue.offer(3));
System.out.println("---------------------");
System.out.println(queue.offer(4,2,TimeUnit.SECONDS));
System.out.println(queue.poll());
System.out.println(queue.poll());
System.out.println(queue.poll());
System.out.println("*****************");
System.out.println(queue.poll(2,TimeUnit.SECONDS));
}
/**
* 获取队列首个元素
* element 抛异常, peek 不抛异常
* @throws InterruptedException
*/
@Test
public void obtainFirstObj() throws InterruptedException {
ArrayBlockingQueue queue = new ArrayBlockingQueue(3);
//为空时抛异常 NoSuchElementException
System.out.println(queue.element());
//为空时返回null
System.out.println(queue.peek());
}
6.同步队列SynchronizedQueue
没有容量,放入的元素必须等待取出后才能接着放入元素。
/**
* 放入元素A
* 取出元素A
* 放入元素B
* 取出元素B
* 放入元素C
* 取出元素C
*/
BlockingQueue<String> strings = new SynchronousQueue<>();
new Thread(()->{
try {
System.out.println("放入元素A");
strings.put("A");
System.out.println("放入元素B");
strings.put("B");
System.out.println("放入元素C");
strings.put("C");
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
new Thread(()->{
try {
System.out.println("取出元素A");
strings.take();
System.out.println("取出元素B");
strings.take();
System.out.println("取出元素C");
strings.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"B").start();
线程池
1.池化技术
池化技术:对系统资源的优化。事先准备好一些资源,需要使用直接取
程序的运行本质:占用系统的资源!
线程池,连接池,内存池,对象池,字符串池
线程池的好处 1.降低资源的消耗 2.提高响应速度 3.方便管理
线程复用的好处:可以控制最大并发数,管理线程
@Test
public void exe() {
//创建一个可伸缩的缓存线程池
Executors.newCachedThreadPool();
//创建一个固定线程池
Executors.newFixedThreadPool(1);
//创建一个线程执行器
Executors.newSingleThreadExecutor();
//创建周期性的线程池
Executors.newScheduledThreadPool(1);
}
@Test
public void exeCachedThreadPool() {
//创建一个可伸缩的缓存线程池
/*
当前线程为: pool-1-thread-1
当前线程为: pool-1-thread-1
当前线程为: pool-1-thread-1
当前线程为: pool-1-thread-3
当前线程为: pool-1-thread-2
当前线程为: pool-1-thread-4
当前线程为: pool-1-thread-5
当前线程为: pool-1-thread-6
当前线程为: pool-1-thread-7
当前线程为: pool-1-thread-8
*/
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
executorService.execute(() -> {
System.out.println("当前线程为: " + Thread.currentThread().getName());
});
}
}
@Test
public void exeSingleThreadExecutor() {
//创建一个线程
/**
* 当前线程为: pool-1-thread-1
*
* 当前线程为: pool-1-thread-1
* 当前线程为: pool-1-thread-1
* 当前线程为: pool-1-thread-1
* 当前线程为: pool-1-thread-1
* 当前线程为: pool-1-thread-1
* 当前线程为: pool-1-thread-1
* 当前线程为: pool-1-thread-1
* 当前线程为: pool-1-thread-1
*
* 当前线程为: pool-1-thread-1
*/
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
executorService.execute(() -> {
System.out.println("当前线程为: " + Thread.currentThread().getName());
});
}
}
@Test
public void exeFixedThreadPool() {
//创建一个固定线程池
/*
当前线程为: pool-1-thread-1
当前线程为: pool-1-thread-2
当前线程为: pool-1-thread-2
当前线程为: pool-1-thread-3
当前线程为: pool-1-thread-1
当前线程为: pool-1-thread-1
当前线程为: pool-1-thread-1
当前线程为: pool-1-thread-2
当前线程为: pool-1-thread-4
当前线程为: pool-1-thread-5
*/
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
executorService.execute(() -> {
System.out.println("当前线程为: " + Thread.currentThread().getName());
});
}
}
2.线程池参数
ThreadPoolExecutor int corePoolSize:核心线程数 int maximumPoolSize:最大线程数【任务数>核心线程数+队列容量】 long keepAliveTime:超时后未调用就释放线程 TimeUnit unit:存活时间单位 BlockingQueue<Runnable> workQueue:队列 ThreadFactory threadFactory:线程工厂 RejectedExecutionHandler handler):拒绝策略
3.线程池存在问题
FixedThreadPool 和 SingleThreadPool
其允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM
CacheThreadPool与ScheduleThreadPool
允许创建的线程数量为Ingeter.MAX_VALUE,可能会创建大量的线程,从而导致OOM
4.自定义线程池
最大线程数:任务数>核心线程数+队列容量
拒绝策略:任务数>最大线程数+队列容量
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
1,//核心线程数
2,//最大线程数[]
3,//超时后释放
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(2),//有界队列
Executors.defaultThreadFactory(),
//抛出异常条件:任务数> max+队列容量
new ThreadPoolExecutor.AbortPolicy()//拒绝策略
);
for (int i = 0; i <6 ; i++) {
threadPoolExecutor.execute(() -> {
System.out.println("当前线程为: " + Thread.currentThread().getName());
});
}
}
5.四种拒绝策略[拒绝条件:任务数>最大线程数+队列容量]
AbortPolicy:默认拒绝策略,任务满了,不处理,抛出异常 CallerRunsPolicy:谁调用交给谁处理,线程池不处理 DiscardPolicy:队列满了,丢掉任务,不抛出异常 DiscardOldestPolicy:队列满了,尝试和最早的任务竞争【优化】,也不会抛出异常
6.最大线程池如何定义
IO密集型:大于IO线程数量【节假日时最大请求为1万,此时设置线程数大于1万】
CPU密集型:几核定义为几【Runtime.getRuntime().availableProcessors()】
四大函数式接口
函数式接口:只有一个方法的接口
Runnable接口
@FunctionalInterface
作用:简化编程模型
1.函数式接口
1.1Function<T, R>
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);//传入一个参数T,返回一个参数R
}
@Test
public void function() {
//Function<T, R>
//new 时 需要传入一个 String 并指定返回值类型
Function function = new Function<String,Object>() {
@Override
public String apply(String o) {
return "------";
}
};
}
lambda进行简化
Function f = new Function<Stirng,Object>obj -> "------"
Function f = (str)->{return str}
1.2Predicate<T>
@Test
public void predicate() {
//判断字符串是否为空
Predicate<String> predicate = new Predicate<String>() {
@Override
public boolean test(String str) {
return str.isEmpty();
}
};
}
@Test
public void predicateLambda() {
//判断字符串是否为空
Predicate<String> predicate = str -> str.isEmpty();
predicate.test("a");
}
@Test
public void comsumer() {
//只有参数,没有返回值
Consumer<String> cons = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
cons.accept("aaa");
}
@Test
public void supplier() {
//只有返回值,没有参数
Supplier su = new Supplier() {
@Override
public Object get() {
return 1;
}
};
System.out.println(su.get());
}
| 接口类型 | 说明 | 参数/返回值 | lambda表达式 | |
|---|---|---|---|---|
| Function<T,R> | 输入一个参数,输出一个参数 | T/R | T->{return R} | |
| Predicate<T> | 输入一个参数,对其进行判断 | T/B | T - >{return Boolean} | |
| Supplier<R> | 没有参数,有返回值 | R | ()->{return R} | |
| Consumer<T> | 只有输入,没有返回值 | T | T->{sout()} |
2.Stream流式计算
Person person1 = new Person("A", 12, 1);
Person person2 = new Person("B", 22, 2);
Person person3 = new Person("C", 33, 3);
Person person4 = new Person("D", 44, 4);
Person person5 = new Person("E", 55, 5);
Person person6 = new Person("F", 66, 6);
List<Person> people = Arrays.asList(person1, person2, person3, person4, person5, person6);
people.stream()//转化为Stream
.filter(p-> p.getId()%2 ==0) //过滤
.filter(p->p.getAge()>40)
.map(p->p.getAge()+1)//操作
.sorted((n1,n2)->n2.compareTo(n1))//排序
.limit(1)
.forEach(System.out::println);
3.ForkJoin
特点:工作窃取
原理:内部维护的是双端队列,A线程完成后,可以窃取B线程未完成的任务
ForkJoinTask:有两个实现子类
RecursiveAction:递归事件,没有返回值
RecursiveTask:递归任务,又返回值 task.get()
/**
* 求和计算
* 使用分支合并计算
* 1.ForkJoinPool
*/
public class ForkJoinTest extends RecursiveTask<Long> {
private static long startTime;
private static long endTime;
//临界值
private static long tmp;
private static long startValue;
private static long endValue;
private static long sum = 0;
public ForkJoinTest(long startValue, long endValue) {
this.startValue = startValue;
this.endValue = endValue;
}
@Override
protected Long compute() {
if (endValue - startValue < tmp) {
for (Long i = startValue; i < endValue; i++) {
sum += i;
}
} else {
long middle = (endValue - startValue) / 2;
//1.拆分任务
ForkJoinTest task1 = new ForkJoinTest(startValue, middle);
ForkJoinTest task2 = new ForkJoinTest(middle + 1, endValue);
//2.将任务压入到队列中
task1.fork();
task2.fork();
//3.合并计算结果
sum = task1.join() + task2.join();
}
return sum;
}
public static void test1() {
//1177
startTime = System.currentTimeMillis();
for (Long i = 0L; i < 10_0000_0000; i++) {
sum += i;
}
endTime = System.currentTimeMillis();
System.out.println("一共耗时: " + (endTime - startTime));
}
/**
* 使用ForkJoinPool执行 ForkJoinTask 任务
*
* @throws ExecutionException
* @throws InterruptedException
*/
public static void test2() throws ExecutionException, InterruptedException {
startTime = System.currentTimeMillis();
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask task = new ForkJoinTest(0L, 10_0000_0000);
//execute 阻塞的 执行任务 无返回值
// forkJoinPool.execute(task);
//异步的 提交任务 又返回值
ForkJoinTask joinTask = forkJoinPool.submit(task);
joinTask.get();//会发生阻塞
endTime = System.currentTimeMillis();
System.out.println("一共耗时: " + (endTime - startTime));
}
/**
* 使用LongStream parallel进行并行流式计算
*/
public static void test3() {
startTime = System.currentTimeMillis();
LongStream.rangeClosed(0,10_0000_0000).parallel().reduce(0,Long::sum);
endTime = System.currentTimeMillis();
System.out.println("一共耗时: " + (endTime - startTime));
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
//test1();
//test2();
test3();
}
}
使用LongStream进行并行流计算
public static void test3() {
startTime = System.currentTimeMillis();
LongStream.rangeClosed(0,10_0000_0000).parallel().reduce(0,Long::sum);
endTime = System.currentTimeMillis();
System.out.println("一共耗时: " + (endTime - startTime));
}
4.异步回调
1.异步执行
2.成功回调
3.失败回调
举例: CompletableFuture :
异步执行: runAsync(Runnable runnable)
成功回调:whenComplete(BiConsumer<? super T, ? super Throwable> action)
失败回调:BiConsumer(T,U) T:成功时返回值,U:失败时返回值
@Test
public void test1() throws ExecutionException, InterruptedException {
//无返回值的异步执行
CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
try {
TimeUnit.SECONDS.sleep(2);
System.out.println("异步执行无返回值");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println("任务执行……");
completableFuture.get();//获取阻塞结果
}
@Test
public void test2() {
//异步执行,又返回值结果
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println("有返回值的异步任务执行");
int i = 1 / 0;
return 201;
});
completableFuture.whenComplete((t, u) -> {
System.out.println("t-> " + t);//执行成功时获取的返回码 201
System.out.println("u-> " + u);//执行失败时获取的返回码 400
}).exceptionally((e) -> {
System.out.println(e.getMessage());
return 400;
});
}
JMM
Java内存模型,不存在,它是一种约定
JMM中关于内存的约定:
1.线程解锁前,必须将共享变量立刻刷会主存
2.线程加锁前,必须将主存中的共享变量的最新值读取到工作内存中
3.加锁和解锁必须是同一把锁

Volatile
//此程序会出现的问题:不会停止
//使用 volatile关键字
private static int num =0;
public static void main(String[] args) {
new Thread(()->{
while(num == 0){
System.out.println("------");
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
num = 1;
}
volatile:Java提供的轻量级的同步机制
1.CPU的可见性
2.不保证原子性
num++:
不是一个原子操作:底层会经过三步:1.读取num值,2.计算,3.写入num值
如何解决
使用原子类 AtomicInteger保证原子性:底层使用的是CAS操作
3.禁止指令重排
指令重排序时需要考虑其依赖性
源代码->编译器优化重排->指令并行也可能重排->内存系统也会重排
// private volatile int num = 0;
private volatile AtomicInteger num = new AtomicInteger();
@Test
//volatile不保证原子性测试
public void test() {
//计算结果应该为100000,但是结果不正确
for (int i = 1; i <= 50; i++) {
new Thread(() -> {
for (int j = 1; j <= 2000; j++) {
add();
}
}).start();
}
while (Thread.activeCount() > 2) { //main gc
Thread.yield();
}
System.out.println(Thread.currentThread().getName() + " 计算结果为: " + num);
}
/**
* num++:
* 不是一个原子操作:底层会经过三步:1.读取num值,2.计算,3.写入num值
* 如何解决
* 使用原子类 AtomicInteger保证原子性:底层使用的是CAS操作
*/
public void add() {
//num++;
num.getAndIncrement();
}
volatile如何保证禁重排序的?
通过内存屏障实现的
1.保证特定操作的执行顺序
2.保证某些变量的可见性

单例模式
饿汉式,懒汉式,静态内部类,枚举
[在DCL饿汉式中使用了volatile]
枚举Enum如何保证单例模式被破坏?
1.饿汉式
/**
* 饿汉式
* 1.保证构造器私有
* 2.提供一个对象
* 缺点:可能会浪费空间
*/
public class Hungry {
private Hungry() {
}
private final static Hungry hungry = new Hungry();
public static Hungry getInstance() {
return hungry;
}
}
1.2懒汉式
/**
* 懒汉式
* 特点:使用时创建
*/
public class LazyMan {
private LazyMan() {
System.out.println(Thread.currentThread().getName());
}
private static LazyMan lazyMan;
//使用volatile关键字确保在对象创建过程中,不会进行对象创建的重排序
private static volatile LazyMan lazyManV;
//单线程不会出现问题
/**
* 多线程下会有多个对象被创建
* @return
*/
public static LazyMan getInstance() {
if (lazyMan == null) {
lazyMan = new LazyMan();
}
return lazyMan;
}
/**
* 使用dubble check进行检查:DCL
* @return
*/
public static LazyMan getInstance1(){
if(lazyMan == null){
synchronized (LazyMan.class){
if(lazyMan == null){
//初始化不是一个原子操作
/**
* 1.分配内存空间
* 2.执行构造方法,初始化对象
* 3.把这个对象指向这个空间
* 可能的过程为:123 ,也可能为132
* 在132过程中,如果这个时候如果另一个线程对其进行访问,就会出现对象已经存在,但是在使用时出现空指针问题
* 所以此时需要使用volatile关键字进行禁止指令重排序
*/
lazyMan = new LazyMan();
}
}
}
return lazyMan;
}
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
new Thread(LazyMan::getInstance,String.valueOf(i)).start();
new Thread(LazyMan::getInstance1,String.valueOf(i)).start();
}
}
}
1.3内部静态类
/**
* 内部类实现单例模式
*/
public class Outter {
/**
* 构造器私有,确保对象单例
*/
private Outter() {
}
public Outter getInstance(){
return Inner.OUTTER;
}
public static class Inner {
private static final Outter OUTTER = new Outter();
}
}
1.4使用反射破坏单例模式
1.4.1饿汉式
/**
* 饿汉式
* 1.保证构造器私有
* 2.提供一个对象
* 缺点:可能会浪费空间
*/
public class Hungry {
private Hungry() {
/**
* 解决:在使用反射进行对象创建时,抛出异常
*/
synchronized (Hungry.class){
if(hungry != null){
throw new RuntimeException("禁止使用反射创建对象!!!");
}
}
}
private final static Hungry hungry = new Hungry();
public static Hungry getInstance() {
return hungry;
}
public static void main1(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//使用反射破坏单例模式
Hungry hungry = Hungry.getInstance();
Constructor<Hungry> constructor = Hungry.class.getDeclaredConstructor(null);
constructor.setAccessible(true);
Hungry hungry1 = constructor.newInstance();
System.out.println(hungry == hungry1);//false
}
}
1.4.2懒汉式
/**
* 懒汉式
* 特点:使用时创建
*/
public class LazyMan {
private static boolean proInstance =false;
private LazyMan() {
synchronized (LazyMan.class){
if(proInstance == false){
proInstance = true;
}else{
throw new RuntimeException("禁止使用发射创建对象!");
}
}
}
private static LazyMan lazyMan;
//使用volatile关键字确保在对象创建过程中,不会进行对象创建的重排序
private static volatile LazyMan lazyManV;
//单线程不会出现问题
/**
* 多线程下会有多个对象被创建
* @return
*/
public static LazyMan getInstance() {
if (lazyMan == null) {
lazyMan = new LazyMan();
}
return lazyMan;
}
/**
* 使用dubble check进行检查:DCL
* @return
*/
public static LazyMan getInstance1(){
if(lazyMan == null){
synchronized (LazyMan.class){
if(lazyMan == null){
//初始化不是一个原子操作
/**
* 1.分配内存空间
* 2.执行构造方法,初始化对象
* 3.把这个对象指向这个空间
* 可能的过程为:123 ,也可能为132
* 在132过程中,如果这个时候如果另一个线程对其进行访问,就会出现对象已经存在,但是在使用时出现空指针问题
* 所以此时需要使用volatile关键字进行禁止指令重排序
*/
lazyMan = new LazyMan();
}
}
}
return lazyMan;
}
public static void main1(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//使用反射破坏懒汉式单例
Constructor<LazyMan> constructor = LazyMan.class.getDeclaredConstructor(null);
constructor.setAccessible(true);
LazyMan lazyMan1 = constructor.newInstance();
LazyMan lazyMan2 = constructor.newInstance();
System.out.println(lazyMan1 == lazyMan2);//false
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
//使用反射获取指定的字段
Field proInstance = LazyMan.class.getDeclaredField("proInstance");
proInstance.setAccessible(false);
Constructor<LazyMan> constructor = LazyMan.class.getDeclaredConstructor(null);
constructor.setAccessible(true);
LazyMan lazyMan1 = constructor.newInstance();
proInstance.set(lazyMan1,false);
LazyMan lazyMan2 = constructor.newInstance();
System.out.println(lazyMan1 == lazyMan2);//false
}
1.5Enum中的解决方式
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
class EnumT{
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
EnumSingle instance1 = EnumSingle.INSTANCE;
EnumSingle instance2 = EnumSingle.INSTANCE;
System.out.println(instance1 == instance2);//true
//使用反射破坏单例
//java.lang.NoSuchMethodException
//Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null);
//java.lang.IllegalArgumentException: Cannot reflectively create enum objects
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
EnumSingle enumSingle = declaredConstructor.newInstance();
EnumSingle enumSingle1 = declaredConstructor.newInstance();
System.out.println(enumSingle== enumSingle1) ;
}
}
CAS机制
CAS:compareandset:CPU并发原语
如果符合期望的值就更新,否则就不更新
public static void main(String[] args) {
AtomicInteger integer = new AtomicInteger(1);
//int expect, int update
System.out.println(integer.compareAndSet(1, 2));//true
System.out.println(integer.get());
System.out.println(integer.compareAndSet(1, 2));//false
System.out.println(integer.get());
}

Unsafe类
java无法操作内存,但是可以通过调用native实现对内存的操作,这个类就是操作内存的
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
private volatile int value;
CAS的ABA问题
CAS:比较当前值与主内存中方的值,如果这个值是期望的值,则执行操作;如果不是期望的值,则一直循环。
缺点:
1.循环耗时
2.一次性只能保证一个共享变量的原子性
3.ABA问题【乐观锁】
原子引用
带版本号的原子操作
AtomicReference类
/**
* 解决CAS-ABA 问题方法:AtomicStampedReference
* @param args
*/
public static void main(String[] args) {
AtomicStampedReference<Integer> reference = new AtomicStampedReference<>(1,666);
new Thread(()->{
//获取版本号
int stamp = reference.getStamp();
System.out.println("A1 版本号" + stamp);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(reference.compareAndSet(1, 2, reference.getStamp(), reference.getStamp() + 1));
System.out.println("A2版本号"+ reference.getStamp());
System.out.println(reference.compareAndSet(2, 1, reference.getStamp(), reference.getStamp() + 1));
System.out.println("A3版本号"+ reference.getStamp());
},"A").start();
new Thread(()->{
//获取版本号
int stamp = reference.getStamp();
System.out.println("B1 版本号" + stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(reference.compareAndSet(1, 3, reference.getStamp(), reference.getStamp() + 1));
System.out.println("B2版本号"+ reference.getStamp());
},"B").start();
}
锁
1.公平锁,非公平锁
公平锁:公平,不可以插队 new ReentrantLock(){new NonfairSync()}
非公平锁:不公平,允许插队(默认都是非公平)new ReentrantLock(boolean fair){new FairSync()}
2.可重入锁
所有的锁都是可重入锁【递归锁】,即获取外部的锁,就会自动获得内部的锁
/**
* 测试可重入锁
*/
public class ReentrantLockTest {
public static void main(String[] args) {
Book book = new Book();
/**
* 当A、B开始调用read方法,总是A -> read() ->write()
*/
new Thread(book::read, "A").start();
new Thread(book::read, "B").start();
new Thread(book::read1, "C").start();
new Thread(book::read1, "D").start();
}
}
class Book {
public synchronized void read() {
System.out.println(Thread.currentThread().getName() + " read()");
//此时锁未释放,还存在锁
write();
}
public synchronized void write() {
System.out.println(Thread.currentThread().getName() + " write()");
}
//非公平锁
//锁要成对出现: lock.lock() lock.unlock() ,否则会出现死锁
Lock lock = new ReentrantLock();
public synchronized void read1() {
lock.lock();
//lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " read1()");
write1();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
// lock.unlock();
}
}
public synchronized void write1() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " write1()");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
3.自旋锁
Spinlock:
do { var5 = this.getIntVolatile(var1, var2); } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
自定义自旋锁
/**
* 自定义自旋锁
*/
public class CustomSpinlock {
private AtomicReference<Thread> reference = new AtomicReference<>();
public void lock(){
Thread currentThread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"通过自旋加锁");
while(!reference.compareAndSet(null,currentThread)){
// System.out.println(Thread.currentThread().getName()+" lock() ...");
}
}
public void unlock(){
Thread currentThread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"释放锁 unlock()");
reference.compareAndSet(currentThread,null);
}
}
测试自定义的自旋锁
public class CustomSpinlockTest {
public static void main(String[] args) {
/**
* Lock lock = new ReentrantLock();
* lock.lock();
* lock.unlock();
*
*
* T1通过自旋加锁
* T2通过自旋加锁
* T1释放锁 unlock()
* T2释放锁 unlock()
*/
CustomSpinlock customSpinlock = new CustomSpinlock();
new Thread(() -> {
customSpinlock.lock();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
customSpinlock.unlock();
}
}, "T1").start();
new Thread(() -> {
customSpinlock.lock();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
customSpinlock.unlock();
}
}, "T2").start();
}
}
4.死锁

模拟死锁出现
public class DeadLockDemo {
public static void main(String[] args) {
String lockA = "lockA";
String lockB = "lockB";
new Thread(new MyDeadLock(lockA,lockB),"T1").start();
new Thread(new MyDeadLock(lockB,lockA),"T2").start();
}
}
class MyDeadLock implements Runnable{
private String lockA;
private String lockB;
public MyDeadLock(String lockA, String lockB) {
this.lockA = lockA;
this.lockB = lockB;
}
@Override
public void run() {
synchronized (lockA){
System.out.println(Thread.currentThread().getName() + " lock: " + lockA + "获取 " + lockB);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB){
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " lock: " + lockB + "获取 " + lockA);
}
}
}
}
死锁解决
1.使用jps -l定位进程号
2.使用jstack 进程号

浙公网安备 33010602011771号