JUC
1. JUC的概念
JUC是java.util.concurrent的简写。在jdk官方手册中可以看到juc相关的jar包有三个
2. 进程和线程(回顾)
进程
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
线程
线程(thread)是操作系统能够进行运算调度的最小单位。一个进程可以有很多线程,每条线程并行执行不同的任务。Java中默认有两条线程(main线程和GC线程)。
Java可以开启线程吗?
// 开启线程的方法start()
public class Test01 {
public static void main(String[] args) {
new Thread().start();
}
}
// 进入start()方法源码
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this); // 加入线程组
boolean started = false;
try {
start0(); // 调用start0
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
// 查看start0()方法
private native void start0(); // native
并发 & 并行
并发:CPU单核,多条线程快速交替进行。
并发:CPU多核,多条线程可以同时进行。
public static void main(String[] args) {
System.out.println(Runtime.getRuntime().availableProcessors());
}
2.1. 线程的六个状态
public enum State {
// 新生
NEW,
// 运行
RUNNABLE,
// 阻塞
BLOCKED,
// 等待
WAITING,
// 超时等待
TIMED_WAITING,
// 终止
TERMINATED;
}
2.2. wait / sleep
- 来自不同的类
- wait ==> Object
- sleep ==> Thread
- 锁的释放
- wait: 释放锁
- sleep: 不释放锁
- 使用的范围
- wait: 在同步代码块中才能使用
- sleep:任何地方都可以使用
3. Lock锁
3.1. 传统的多线程锁(Synchronized)
Synchronized本质: 队列、锁
public class Test01 {
public static void main(String[] args) {
Ticket ticket = new Ticket();
// 线程A
new Thread(()->{
for (int i = 0;i < 40; i++){
ticket.sale();
}
},"Thread A").start();
// 线程B
new Thread(()->{
for (int i = 0;i < 40; i++){
ticket.sale();
}
},"Thread B").start();
// 线程C
new Thread(()->{
for (int i = 0;i < 40; i++){
ticket.sale();
}
},"Thread C").start();
}
}
class Ticket {
private int count = 50;
public synchronized void sale(){
if (count > 0) {
System.out.println("剩余" + count-- +"张票===>购买的线程名:"+Thread.currentThread().getName());
}
}
}
- Synchronized原理
3.2. Interface Lock
3.2.1. ReentrantLock(可重入锁)
- 查看ReentrantLock的构造器
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
// 使用无参构造器时,是非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
// 可传入Boolean值,如果参数是true,则创建公平锁,如果参数是false,则创建非公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
// 公平锁: 不可以进行插队
// 非公平锁: 可以进行插队(默认)
- 测试使用
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Test02 {
public static void main(String[] args) {
Ticket2 ticket = new Ticket2();
// 线程A
new Thread(()->{
for (int i = 0;i < 40; i++){
ticket.sale();
}
},"Thread A").start();
// 线程B
new Thread(()->{
for (int i = 0;i < 40; i++){
ticket.sale();
}
},"Thread B").start();
// 线程C
new Thread(()->{
for (int i = 0;i < 40; i++){
ticket.sale();
}
},"Thread C").start();
}
}
class Ticket2 {
private int count = 50;
private Lock lock = new ReentrantLock();
public void sale(){
lock.lock();
try{
if (count > 0) {
System.out.println(
"剩余" + count-- +"张票===>购买的线程名:"+Thread.currentThread().getName());
}
} finally {
lock.unlock();
}
}
}
3.2.2. Synchronized 和 Lock的区别
-
Synchronized 是内置的Java关键字,Lock是一个Java类
-
Synchronized 无法判断获取锁的状态,Lock可以判断获取锁的状态。
-
Synchronized 会自动释放锁(自动挡),Lock必须手动释放锁(手动档)。(Lock更加安全,但是如果不释放锁,就会引发“死锁”)
-
Synchronized 线程1获得锁之后,如果阻塞了,线程2就会一直等待。Lock锁就不一定会继续等待。
-
Synchronized 是可重入锁,不可中断,非公平锁。Lock是可重入锁,可以判断中断,可以设置为公平锁,也可为非公平锁(Lock的使用比Synchronized 更加灵活)
-
Synchronized 适合锁少量的代码块,而Lock适合锁大量的同步代码块。
4. 生产者和消费者
4.1.Synchronized版本
(两条线程的时候运行正常,如果多条线程运行时,会出问题 【虚假唤醒】)
package com.mashiro.demo02;
public class SyncPC {
public static void main(String[] args) {
Number number = new Number();
// 生产者
new Thread(()->{
try {
while(true){
number.incrment();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"Per").start();
// 消费者
new Thread(()->{
try {
while (true){
number.decrment();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"Cer").start();
}
}
class Number{
private int count=0;
public synchronized void incrment() throws InterruptedException {
if (count != 0){
this.wait(); // 线程等待,释放锁
}
count++;
System.out.println("增加库存,现有库存:"+count);
this.notifyAll(); // 唤醒其它所有线程
}
public synchronized void decrment() throws InterruptedException {
if (count == 0){
this.wait(); // 线程等待,释放锁
}
count--;
System.out.println("减少库存,现有库存:"+count);
this.notifyAll(); // 唤醒其它所有线程
}
}
- 解决虚假唤醒问题(讲上面的if判断改为while循环判断即可)
产生虚假唤醒的原因: 进入了if代码块中,执行了wait()方法,程序等待唤醒。当程序唤醒是,讲从wait()之后开始运行,不会再进行if判断。如果讲if改成while,就可以解决这个问题
class Number{
private int count=0;
public synchronized void incrment() throws InterruptedException {
while(count != 0){
this.wait(); // 线程等待,释放锁
}
count++;
System.out.println(Thread.currentThread().getName()+ "==>" +count);
this.notifyAll(); // 唤醒其它所有线程
}
public synchronized void decrment() throws InterruptedException {
while(count == 0){
this.wait(); // 线程等待,释放锁
}
count--;
System.out.println(Thread.currentThread().getName()+ "==>" +count);
this.notifyAll(); // 唤醒其它所有线程
}
4.2. Lock版本
package com.mashiro.demo02;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockPC {
public static void main(String[] args) {
Data data = new Data();
// 生产者
new Thread(()->{
try {
for (int i = 0;i < 40; i++){
data.incrment();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"Per1").start();
// 生产者
new Thread(()->{
try {
for (int i = 0;i < 40; i++){
data.incrment();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"Per2").start();
// 消费者
new Thread(()->{
try {
for (int i = 0;i < 40; i++){
data.decrment();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"Cer1").start();
new Thread(()->{
try {
for (int i = 0;i < 40; i++){
data.decrment();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"Cer2").start();
}
}
class Data{
private int count=0;
private Lock lock = new ReentrantLock(); // 锁
private Condition condition = lock.newCondition(); // 监视器
public void incrment() throws InterruptedException {
lock.lock();
try {
while(count != 0){
condition.await(); // 等待
}
count++;
System.out.println(Thread.currentThread().getName()+ "==>" +count);
condition.signalAll(); // 唤醒全部
} finally {
lock.unlock();
}
}
public void decrment() throws InterruptedException {
lock.lock();
try {
while(count == 0){
condition.await(); // 等待
}
count--;
System.out.println(Thread.currentThread().getName()+ "==>" +count);
condition.signalAll(); // 唤醒全部
} finally {
lock.unlock();
}
}
}
4.3. Condition指定唤醒线程
package com.mashiro.demo02;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionTest {
public static void main(String[] args) {
Data2 data2 = new Data2();
new Thread(()->{
for (int i = 0; i < 40; i++){
data2.printA();
}
},"Thread A").start();
new Thread(()->{
for (int i = 0; i < 40; i++){
data2.printB();
}
},"Thread B").start();
new Thread(()->{
for (int i = 0; i < 40; i++){
data2.printC();
}
},"Thread C").start();
}
}
class Data2 {
private int count = 0;
private Lock lock = new ReentrantLock();
private Condition conditionA = lock.newCondition();
private Condition conditionB = lock.newCondition();
private Condition conditionC = lock.newCondition();
private int flag = 0;
public void printA(){
lock.lock();
try {
while(flag != 0){
conditionA.await();
}
System.out.println(Thread.currentThread().getName() + "执行了printA");
flag = 1;
conditionB.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
while(flag != 1){
conditionB.await();
}
System.out.println(Thread.currentThread().getName() + "执行了printB");
flag = 2;
conditionC.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
while(flag != 2){
conditionC.await();
}
System.out.println(Thread.currentThread().getName() + "执行了printC");
flag = 0;
conditionA.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
5. 集合不安全
5.1.List
- 测试
package com.mashiro.demo03;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class UnsafeList {
public static void main(String[] args) {
// 多线程向集合插入值
List<String> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
},String.valueOf(i+1)).start();
}
}
}
结果抛出异常:java.util.ConcurrentModificationException 【并发修改异常】
- 解决方案
方案1:List list = new Vector<>();
// Vector的Add方法是一个synchronized修饰的同步方法
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
方案2:Collections.synchronizedList(list);
// 使用Collections工具类讲不安全的ArrayList转换为安全的ArrayList
List<String> list = new ArrayList<>();
List<String> safeList = Collections.synchronizedList(list);
方案3:List list = new CopyOnWriteArrayList<>();
/** The array, accessed only via getArray/setArray. */
// 查看CopyOnWriteArrayList源码内部的数组
// CopyOnWrite写入时复制。 COW 计算机程序设计领域的一种优化策略。
// 在多线程读取的时候是固定的,但是在同时写入的时候,容易出现覆盖,造成数据问题。
// CopyOnWrite写入时复制一份出来,写入完成时再导回去,避免了写入覆盖问题。【读写分离】
private transient volatile Object[] array;
5.2. Set
- 测试
package com.mashiro.demo03;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
public class UnsafeSet {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
for (int i = 0; i < 1000; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(set);
},String.valueOf(i+1)).start();
}
}
}
- 解决方案
Set<String> set1 = Collections.synchronizedSet(set);
Set<String> set2 = new CopyOnWriteArraySet<>();
- HashSet底层
/**
* Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
* default initial capacity (16) and load factor (0.75).
*/
// HashSet底层就是HashMap
public HashSet() {
map = new HashMap<>();
}
/**
* Adds the specified element to this set if it is not already present.
* More formally, adds the specified element <tt>e</tt> to this set if
* this set contains no element <tt>e2</tt> such that
* <tt>(e==null ? e2==null : e.equals(e2))</tt>.
* If this set already contains the element, the call leaves the set
* unchanged and returns <tt>false</tt>.
*
* @param e element to be added to this set
* @return <tt>true</tt> if this set did not already contain the specified
* element
*/
// HashSet的Add方法
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
// Dummy value to associate with an Object in the backing Map
// PRESENT就是是个空对象
private static final Object PRESENT = new Object();
5.3. HashMap
5.3.1. Volatile关键字
// volatile
// volatile也是变量修饰符,只能用来修饰变量。\
// volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。
// 当成员变量发生变化时,强迫线程将变化值回写到共享内存。
// 在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
5.3.2. HashMap的负载因子和初始化容量
// 默认加载因子 0.75f
// static final float DEFAULT_LOAD_FACTOR = 0.75f;
// 默认初始化容量大小 16
// static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
/**
* Constructs an empty <tt>HashMap</tt> with the specified initial
* capacity and load factor.
*
* @param initialCapacity the initial capacity
* @param loadFactor the load factor
* @throws IllegalArgumentException if the initial capacity is negative
* or the load factor is nonpositive
*/
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
}
/**
* Constructs an empty <tt>HashMap</tt> with the specified initial
* capacity and the default load factor (0.75).
*
* @param initialCapacity the initial capacity.
* @throws IllegalArgumentException if the initial capacity is negative.
*/
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
/**
* Constructs an empty <tt>HashMap</tt> with the default initial capacity
* (16) and the default load factor (0.75).
*/
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
5.3.3. ConcurrentHashMap
public class UnsafeHashmap {
public static void main(String[] args) {
Map<String,String> map = new HashMap<>();
// Map<String, String> mapSync = Collections.synchronizedMap(map);
// Map<String, String> map = new ConcurrentHashMap<>();
for (int i = 0; i < 1000; i++) {
new Thread(()->{
map.put(UUID.randomUUID().toString().substring(0,5),UUID.randomUUID().toString().substring(0,5));
System.out.println(map);
},String.valueOf(i+1)).start();
}
}
}
6. Callable
6.1. Callable比较Runnable
// Callable接口类似于Runnable ,因为它们都是为其实例可能由另一个线程执行的类设计的。
// A Runnable ,但是,不返回结果,也不能抛出被检查的异常。
- Callable可以有返回值
- Callable可以抛出异常
- Callable用的是call()方法
- Callable可以使用泛型
6.2. 原理探究
// Thread(Runnable target)----> new Thread中只能传入Runnable
// public interface Runnable ----> 已知实现类中有一个 FutureTask类
// public class FutureTask<V> implements RunnableFuture<V>
// FutureTask可用于包装Callable或Runnable对象。
// FutureTask(Runnable runnable, V result)
// 所以Thread(Runnable target)中可以传入Callable<T>
6.3.测试
public class CallableTest {
public static void main(String[] args) {
// 使用Runnable
new Thread(new MyThread1(),"A").start();
// 使用Callable
MyThread2 myThread = new MyThread2();
FutureTask<Integer> futureTask = new FutureTask<Integer>(myThread);
new Thread(futureTask,"C").start();
// futureTask.get();会产生阻塞现象,建议放在代码最后
// 或者采用异步通信的方式来处理
Integer integer = futureTask.get();
System.out.println(integer);
}
}
class MyThread1 implements Runnable{
@Override
public void run() {
System.out.println("Runnable");
}
}
class MyThread2 implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("Callable<Integer>");
return 1;
}
}
7. CountDownLatch、CycliBarrier、Samaphore
7.1. CountDownLatch
CountDownLatch是一种通用的同步工具
public class CountDownLatchTest {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(6); // 创建计数器,初始化为6
for (int i = 1; i <= 6; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName() + " get out");
countDownLatch.countDown(); // 每条线程执行完毕后,计数器 -1
},String.valueOf(i)).start();
}
countDownLatch.await(); // 等待计数器归零
System.out.println("CountDownLatchTest Finish"); // 结束
}
}
7.2. CycliBarrier
允许一组线程全部等待彼此达到共同屏障点的同步辅助。
public class CycliBarrierTest {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(6,()->{
System.out.println("CycliBarrierTest Finish");
});
for (int i = 1; i <= 6; i++) {
final int temp = i;
new Thread(()->{
try {
System.out.println("CycliBarrierTest Start" + temp);
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
7.3. Semaphore
Semaphore : 信号量
public class SemaphoreTest {
public static void main(String[] args) {
// 适用于限流
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <= 6 ; i++) {
new Thread(()->{
// acquire() 得到
// release() 释放
try {
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();
}
},String.valueOf(i)).start();
}
}
}
8. 读写锁
独占锁(写锁):只能被一个线程占有
共享锁(读锁):可以被多个线程占有
ReentrantReadWriteLock
Read-Read 可共存
Read-Write 不可共存
Write-Write 不可共存
public class RWLockTest {
public static void main(String[] args) {
// MyCache myCache = new MyCache();
MyCacheLock lock = new MyCacheLock();
for (int i = 1; i <= 10; i++) {
final int temp = i;
new Thread(()->{
lock.write(temp+"",temp+"");
},String.valueOf(i)).start();
}
for (int i = 1; i <= 10; i++) {
final int temp = i;
new Thread(()->{
lock.read(String.valueOf(temp+""));
},String.valueOf(i)).start();
}
}
}
class MyCache {
private volatile Map<String,Object> cacheMap = new HashMap<>();
public void write(String key,Object value) {
System.out.println(Thread.currentThread().getName() + "==>进行写入操作 Start");
cacheMap.put(key,value);
System.out.println(Thread.currentThread().getName() + "==>进行写入操作 Finish");
}
public void read(String key) {
System.out.println(Thread.currentThread().getName() + "==>进行读取操作 Start");
cacheMap.get(key);
System.out.println(Thread.currentThread().getName() + "==>进行读取操作 Finish");
}
}
class MyCacheLock {
// private Lock lock = new ReentrantLock();
private ReadWriteLock lock = new ReentrantReadWriteLock(); // 比普通的ReentrantLock粒度更细
private volatile Map<String,Object> cacheMap = new HashMap<>();
public void write(String key,Object value) {
lock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "==>进行写入操作 Start");
cacheMap.put(key,value);
System.out.println(Thread.currentThread().getName() + "==>进行写入操作 Finish");
} finally {
lock.writeLock().unlock();
}
}
public void read(String key) {
lock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "==>进行读取操作 Start");
cacheMap.get(key);
System.out.println(Thread.currentThread().getName() + "==>进行读取操作 Finish");
} finally {
lock.readLock().unlock();
}
}
}
9. 队列
9.1.阻塞队列(BlockingQueue)
适用场景:
- 多线程并发
- 线程池
Queue族谱
- Collection
- Queue
- BlockingDeque
- LinkedBlockingDeque
- BlockingQueue
- ArrayBlockingQueue
- DelayQueue
- LinkedBlockingQueue
- LinkedBlockingQueue
- LinkedTransferQueue
- PriorityBlockingQueue
- SynchronousQueue
- Deque
- ArrayDeque
- ConcurrentLinkedDeque
- LinkedBlickingDeque
- LinkedList
- TransferQueue
- LinkedTransferQueue
- BlockingDeque
- Queue
四组API
- 抛出异常
- 不抛出异常
- 阻塞等待
- 超时等待
方式 | 抛出异常 | 不抛出异常 | 阻塞等待 | 超时等待 |
---|---|---|---|---|
添加 | add() | offer() | put() | offer() 有参重载 |
移除 | remove() | poll() | take() | poll() 有参重载 |
判断队列头部 | element() | peek() |
// 抛出异常
public static void test1(){
ArrayBlockingQueue queue = new ArrayBlockingQueue<Integer>(3);
System.out.println(queue.add(1)); // true
System.out.println(queue.add(2)); // true
System.out.println(queue.add(3)); // true
// Exception in thread "main" java.lang.IllegalStateException: Queue full
// System.out.println(queue.add(4));
System.out.println(queue.remove()); // 1
System.out.println(queue.remove()); // 2
System.out.println(queue.remove()); // 3
// Exception in thread "main" java.util.NoSuchElementException
// System.out.println(queue.remove());
// Exception in thread "main" java.util.NoSuchElementException
System.out.println(queue.element());
}
// 不跑出异常
public static void test2(){
ArrayBlockingQueue queue = new ArrayBlockingQueue<Integer>(3);
System.out.println(queue.offer(1)); // true
System.out.println(queue.offer(2)); // true
System.out.println(queue.offer(3)); // true
System.out.println(queue.offer(4)); // false
System.out.println(queue.poll()); // 1
System.out.println(queue.poll()); // 2
System.out.println(queue.poll()); // 3
System.out.println(queue.poll()); // null
System.out.println(queue.peek());
}
// 阻塞等待
public static void test3() throws InterruptedException {
ArrayBlockingQueue queue = new ArrayBlockingQueue<Integer>(3);
queue.put(1);
queue.put(2);
queue.put(3);
// 一直等待阻塞
// queue.put(4);
System.out.println(queue.take());
System.out.println(queue.take());
System.out.println(queue.take());
// 一直等待阻塞
// System.out.println(queue.take());
}
// 等待超时
public static void test4() throws InterruptedException {
ArrayBlockingQueue queue = new ArrayBlockingQueue<Integer>(3);
queue.offer(1,2, TimeUnit.SECONDS); // 超时等待2s
queue.offer(2,2, TimeUnit.SECONDS); // 超时等待2s
queue.offer(3,2, TimeUnit.SECONDS); // 超时等待2s
queue.offer(4,2, TimeUnit.SECONDS); // 超时等待2s
System.out.println("offer超时等待结束");
System.out.println(queue.poll(2, TimeUnit.SECONDS));
System.out.println(queue.poll(2, TimeUnit.SECONDS));
System.out.println(queue.poll(2, TimeUnit.SECONDS));
System.out.println(queue.poll(2, TimeUnit.SECONDS));
System.out.println("poll超时等待结束");
}
9.2. 同步队列(SynchronousQueue)
- 同步队列容量只能为1 ,存入一个元素之后,必须取出来之后才能再存
public class SynchronousQueueTest {
public static void main(String[] args) {
SynchronousQueue<Integer> queue = new SynchronousQueue<>();
/************************************************************************/
new Thread(()->{
for (int i = 1;i < 10 ; i++) {
try {
queue.put(i);
System.out.println(Thread.currentThread().getName() + "存入了" + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"提供者").start();
/************************************************************************/
new Thread(()->{
for (int i = 1;i < 10 ; i++) {
try {
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName() + "取出了" + queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"消费者").start();
}
}
10. 线程池(*)
池化技术
线程池,连接池,内存池…
好处
- 降低资源的消耗
- 提高响应的速度
- 统一在池中,方便管理
- 线程复用,可以控制最大并发数,管理线程
线程池:
三大方法、七大参数、四种拒绝策略
10.1. 三大方法
- Executors.newSingleThreadExecutor();
- Executors.newCachedThreadPool();
- Executors.newFixedThreadPool(3);
package com.mashiro.demo08;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolTest {
public static void main(String[] args) {
// Executors工具类三大方法
// 1. 获得单一的线程池(单个线程)
// ExecutorService threadPool = Executors.newSingleThreadExecutor();
/**
* 结果:
* pool-1-thread-1线程被创建了
* pool-1-thread-1线程被创建了
* pool-1-thread-1线程被创建了
* pool-1-thread-1线程被创建了
* pool-1-thread-1线程被创建了
*/
// 2.大小可伸缩的线程池
// ExecutorService threadPool = Executors.newCachedThreadPool();
/**
* 结果:
* pool-1-thread-1线程被创建了
* pool-1-thread-2线程被创建了
* pool-1-thread-3线程被创建了
* pool-1-thread-4线程被创建了
* pool-1-thread-5线程被创建了
*/
// 3. 创建一个固定线程数量的线程池
ExecutorService threadPool = Executors.newFixedThreadPool(3);
/**
* 结果:
* pool-1-thread-1线程被创建了
* pool-1-thread-2线程被创建了
* pool-1-thread-1线程被创建了
* pool-1-thread-3线程被创建了
* pool-1-thread-2线程被创建了
*/
// 使用线程池创建线程
try {
for (int i = 1; i <= 5 ; i++) {
threadPool.execute( ()->{
System.out.println(Thread.currentThread().getName() + "线程被创建了");
});
}
} finally {
// Tips: 线程池用完之后要关闭
threadPool.shutdown();
}
}
}
10.2. 七大参数
查看源码
// Executors.newSingleThreadExecutor()
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
// Executors.newCachedThreadPool()
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
// Executors.newFixedThreadPool(nThreads)
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
// new ThreadPoolExecutor()
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @param threadFactory the factory to use when the executor
* creates a new thread
* @param handler the handler to use when execution is blocked
* because the thread bounds and queue capacities are reached
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue}
* or {@code threadFactory} or {@code handler} is null
*/
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;
}
七大参数
- int corePoolSize 核心线程数量
- int maximumPoolSize 最大线程数量
- long keepAliveTime 空闲存活时间
- TimeUnit unit 空闲存活时间单位
- BlockingQueue workQueue 阻塞队列
- ThreadFactory threadFactory 线程工厂,用于创建线程
- RejectedExecutionHandler handler 拒绝策略
10.3. 手动创建线程池
/**
*
* int corePoolSize
* int maximumPoolSize
* long keepAliveTime
* TimeUnit unit
* BlockingQueue<Runnable> workQueue
* ThreadFactory threadFactory
* RejectedExecutionHandler handler
*/
Executors.newSingleThreadExecutor()
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
2,
5,
3,
TimeUnit.SECONDS,
new LinkedBlockingDeque<Runnable>(),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy() // 拒绝策略: 默认为AbortPolicy()
);
// private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
10.3. 四种拒绝策略
/**
* A handler for rejected tasks that runs the rejected task
* directly in the calling thread of the {@code execute} method,
* unless the executor has been shut down, in which case the task
* is discarded.
*/
public static class CallerRunsPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code CallerRunsPolicy}.
*/
public CallerRunsPolicy() { }
/**
* Executes task r in the caller's thread, unless the executor
* has been shut down, in which case the task is discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
/**
* A handler for rejected tasks that throws a
* {@code RejectedExecutionException}.
*/
public static class AbortPolicy implements RejectedExecutionHandler {
/**
* Creates an {@code AbortPolicy}.
*/
public AbortPolicy() { }
/**
* Always throws RejectedExecutionException.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
* @throws RejectedExecutionException always
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
/**
* A handler for rejected tasks that silently discards the
* rejected task.
*/
public static class DiscardPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardPolicy}.
*/
public DiscardPolicy() { }
/**
* Does nothing, which has the effect of discarding task r.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
/**
* A handler for rejected tasks that discards the oldest unhandled
* request and then retries {@code execute}, unless the executor
* is shut down, in which case the task is discarded.
*/
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardOldestPolicy} for the given executor.
*/
public DiscardOldestPolicy() { }
/**
* Obtains and ignores the next task that the executor
* would otherwise execute, if one is immediately available,
* and then retries execution of task r, unless the executor
* is shut down, in which case task r is instead discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
- AbortPolicy
public static void abortPolicyTest() {
// 创建线程池
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
2,
5,
3,
TimeUnit.SECONDS,
new LinkedBlockingDeque<Runnable>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
try {
// 最大承载并发数量: MaxThread = 阻塞队列体积 + 最大线程数量 3+5 = 8
// 超出最大承载力抛出异常:
// Exception in thread "main" java.util.concurrent.RejectedExecutionException
// 比较关键的业务,推荐使用此拒绝策略
// 在系统不能承载更大的并发量的时候,能够及时的通过异常发现。
for (int i = 1 ; i < 60 ; i++) {
poolExecutor.execute(()->{
System.out.println(Thread.currentThread().getName() + " GO");
});
}
} finally {
poolExecutor.shutdown();
}
}
- CallerRunsPolicy
public static void callerRunsPolicyTest(){
// 创建线程池
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
2,
5,
3,
TimeUnit.SECONDS,
new LinkedBlockingDeque<Runnable>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy()
);
try {
// 最大承载并发数量: MaxThread = 阻塞队列体积 + 最大线程数量 3+5 = 8
// 哪来的回哪去:
// main GO
// main GO
// pool-1-thread-2 GO
// pool-1-thread-2 GO
// 调用线程处理该任务
// 如果任务被拒绝了,则由调用线程(提交任务的线程)直接执行此任务。
for (int i = 1 ; i < 60 ; i++) {
poolExecutor.execute(()->{
System.out.println(Thread.currentThread().getName() + " GO");
});
}
} finally {
poolExecutor.shutdown();
}
}
- DiscardPolicy []
public static void discardPolicyTest(){
// 创建线程池
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
2,
5,
3,
TimeUnit.SECONDS,
new LinkedBlockingDeque<Runnable>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardPolicy()
);
try {
// 最大承载并发数量: MaxThread = 阻塞队列体积 + 最大线程数量 3+5 = 8
// 丢弃任务,但是不抛出异常。如果线程队列已满,则后续提交的任务都会被丢弃,且是静默丢弃。
// 发现系统的异常状态。建议是一些无关紧要的业务采用此策略。
for (int i = 1 ; i < 60 ; i++) {
final int temp = i;
poolExecutor.execute(()->{
System.out.println(Thread.currentThread().getName() +":"+ temp);
});
}
} finally {
poolExecutor.shutdown();
}
}
- DiscardOldestPolicy []
public static void discardOldestPolicyTest(){
// 创建线程池
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
2,
5,
3,
TimeUnit.SECONDS,
new LinkedBlockingDeque<Runnable>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy()
);
try {
// 最大承载并发数量: MaxThread = 阻塞队列体积 + 最大线程数量 3+5 = 8
// 丢弃队列最前面的任务,然后重新提交被拒绝的任务
// 喜新厌旧的拒绝策略。是否要采用此种拒绝策略,还得根据实际业务是否允许丢弃老任务来认真衡量。
for (int i = 1 ; i < 60 ; i++) {
final int temp = i;
poolExecutor.execute(()->{
System.out.println(Thread.currentThread().getName() + " : " + temp);
});
}
} finally {
poolExecutor.shutdown();
}
}
10.4. 最大线程数量的定义方式
- CPU密集型:
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
2,
Runtime.getRuntime().availableProcessors(), // 获取电脑CPU的最大核心数
3,
TimeUnit.SECONDS,
new LinkedBlockingDeque<Runnable>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy()
);
- IO密集型:
统计程序中有N个大型任务,十分占用IO资源。一般将最大线程数设置为X ( >N )。推荐为 2N
11. 四大函数式接口
11.1. Function 函数型接口
查看源码
@FunctionalInterface
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); // 泛型:可传入参数T,返回值类型R
/**
* Returns a composed function that first applies the {@code before}
* function to its input, and then applies this function to the result.
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*
* @param <V> the type of input to the {@code before} function, and to the
* composed function
* @param before the function to apply before this function is applied
* @return a composed function that first applies the {@code before}
* function and then applies this function
* @throws NullPointerException if before is null
*
* @see #andThen(Function)
*/
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
/**
* Returns a composed function that first applies this function to
* its input, and then applies the {@code after} function to the result.
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*
* @param <V> the type of output of the {@code after} function, and of the
* composed function
* @param after the function to apply after this function is applied
* @return a composed function that first applies this function and then
* applies the {@code after} function
* @throws NullPointerException if after is null
*
* @see #compose(Function)
*/
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
/**
* Returns a function that always returns its input argument.
*
* @param <T> the type of the input and output objects to the function
* @return a function that always returns its input argument
*/
static <T> Function<T, T> identity() {
return t -> t;
}
}
测试
/**
* 函数型接口
* 可传入参数T,返回值类型R
*/
public static void testFunction(){
// 内部类写法
Function f = new Function<String, String>() {
@Override
public String apply(String s) {
return s;
}
};
System.out.println(f.apply("123456"));
// 匿名内部类写法
System.out.println(new Function<String,String>() {
@Override
public String apply(String s) {
return s;
}
}.apply("123456"));
// lambda
Function<String,String> f1 = (s)->{return s;};
System.out.println(f1.apply("123456"));
}
11.2. Predicate 断定型接口
查看源码
@FunctionalInterface
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);
/**
* Returns a composed predicate that represents a short-circuiting logical
* AND of this predicate and another. When evaluating the composed
* predicate, if this predicate is {@code false}, then the {@code other}
* predicate is not evaluated.
*
* <p>Any exceptions thrown during evaluation of either predicate are relayed
* to the caller; if evaluation of this predicate throws an exception, the
* {@code other} predicate will not be evaluated.
*
* @param other a predicate that will be logically-ANDed with this
* predicate
* @return a composed predicate that represents the short-circuiting logical
* AND of this predicate and the {@code other} predicate
* @throws NullPointerException if other is null
*/
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
/**
* Returns a predicate that represents the logical negation of this
* predicate.
*
* @return a predicate that represents the logical negation of this
* predicate
*/
default Predicate<T> negate() {
return (t) -> !test(t);
}
/**
* Returns a composed predicate that represents a short-circuiting logical
* OR of this predicate and another. When evaluating the composed
* predicate, if this predicate is {@code true}, then the {@code other}
* predicate is not evaluated.
*
* <p>Any exceptions thrown during evaluation of either predicate are relayed
* to the caller; if evaluation of this predicate throws an exception, the
* {@code other} predicate will not be evaluated.
*
* @param other a predicate that will be logically-ORed with this
* predicate
* @return a composed predicate that represents the short-circuiting logical
* OR of this predicate and the {@code other} predicate
* @throws NullPointerException if other is null
*/
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
/**
* Returns a predicate that tests if two arguments are equal according
* to {@link Objects#equals(Object, Object)}.
*
* @param <T> the type of arguments to the predicate
* @param targetRef the object reference with which to compare for equality,
* which may be {@code null}
* @return a predicate that tests if two arguments are equal according
* to {@link Objects#equals(Object, Object)}
*/
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
测试
/**
* 断定型接口:
* 只能由一个传入参数,返回值为Boolean值
*/
public static void testPredicate(){
Predicate<String> predicate1 = (s)->{
return s.equals("123456");
};
// 极致偷工减料
Predicate<String> predicate2 = (s)->s.equals("123456");
System.out.println(predicate1.test("123456"));
System.out.println(predicate2.test("123456"));
}
11.3. Consumer 消费型结构
查看源码
@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
/**
* Returns a composed {@code Consumer} that performs, in sequence, this
* operation followed by the {@code after} operation. If performing either
* operation throws an exception, it is relayed to the caller of the
* composed operation. If performing this operation throws an exception,
* the {@code after} operation will not be performed.
*
* @param after the operation to perform after this operation
* @return a composed {@code Consumer} that performs in sequence this
* operation followed by the {@code after} operation
* @throws NullPointerException if {@code after} is null
*/
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
测试
public static void testConsumer(){
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s.toUpperCase());
}
};
consumer.accept("abcd");
Consumer<String> consumer1 = (s)->System.out.println(s.toUpperCase());
consumer1.accept("abcde");
}
11.4. Supplier 供给型接口
查看源码
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
测试
public static void testSupplier(){
Supplier<String> supplier = new Supplier<String>() {
@Override
public String get() {
return "Return Funcation Supplier String";
}
};
System.out.println(supplier.get());
Supplier<String> supplier1 = ()->"Return Funcation Supplier String";
System.out.println(supplier1.get());
}
12.Stream
- Stream流 配合 Lambda表达式(函数时接口)测试
public class StreamTest {
public static void main(String[] args) {
testStream();
}
public static void testStream(){
User u1 = new User(1,"AAAA",20);
User u2 = new User(2,"BBBB",21);
User u3 = new User(3,"CCCC",22);
User u4 = new User(4,"DDDD",23);
User u5 = new User(5,"EEEE",24);
User u6 = new User(6,"FFFF",25);
List<User> userList = Arrays.asList(u1, u2, u3, u4, u5, u6);
/**
* 查询id为偶数的人
* 查询年龄大于22
* 名字转换为小写字母
* 用户名倒序
* 只输出一个人
*/
// 将集合转化为Stream流
// userList.stream().filter((user)->{return user.id % 2 == 0;});
// userList.stream().filter((user)->user.id % 2 == 0);
// Stream<T> filter(Predicate<? super T> predicate); ===> filter中传入断定型接口
// <R> Stream<R> map(Function<? super T, ? extends R> mapper); ===> map中传入函数型接口
// Stream<T> sorted(Comparator<? super T> comparator); ===> sorted中传入Comparator函数式接口
// Stream<T> limit(long maxSize); ===> 分页(查询数量)
userList.stream()
.filter( user -> user.id % 2 == 0 )
.filter( user -> user.age > 22 )
.map( user -> { user.name = user.name.toLowerCase(); return user;})
.sorted((user1,user2)->user2.name.compareTo(user1.name))
.limit(1)
.forEach(System.out::println);
}
}
class User {
public Integer id;
public String name;
public Integer age;
public User() {
}
public User(Integer id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
13. 分支合并 ForkJoin
-
ForkJoin
- RecursiveAction 递归事件(无返回值)
- RecursiveTask 递归任务(有返回值)
-
编写ForkJoin计算方法
package com.mashiro.demo09;
import java.util.concurrent.RecursiveTask;
public class ForkJoinTest extends RecursiveTask<Long> {
private long begin;
private long End;
private long temp = 10000L;
public ForkJoinTest(long begin, long end) {
this.begin = begin;
this.End = end;
}
// 计算方法
@Override
protected Long compute() {
long sum = 0L;
if ( (End-begin) < temp ){
for (long i = begin; i <= End; i++) {
sum += i;
}
return sum;
} else {
// ForkJoin
long middle = (begin + End) / 2;
ForkJoinTest joinTest1 = new ForkJoinTest(begin, middle);
joinTest1.fork();
ForkJoinTest joinTest2 = new ForkJoinTest(middle + 1, End);
joinTest2.fork();
return joinTest1.join() + joinTest2.join();
}
}
}
- 计算效率测试
package com.mashiro.demo09;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;
public class ComputeTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Long begin = 1L;
Long end = 1_000_000_000L;
// for循环计算结果
test1(begin,end); // 计算结果为:500000000500000000====>运行时间:4287
// ForkJoin计算
test2(begin,end); // 计算结果为:500000000500000000====>运行时间:2912
// Stream并行计算
test3(begin,end); // 计算结果为:500000000500000000====>运行时间:212
// 算法计算
test4(begin,end); // 计算结果为:500000000500000000====>运行时间:0
}
// 普通计算方法
public static void test1(Long begin, Long end){
long start = System.currentTimeMillis();
long sum = 0L;
for (Long i = begin; i <= end; i++) {
sum += i;
}
long finish = System.currentTimeMillis();
System.out.println("计算结果为:" + sum + "====>运行时间:" + (finish-start));
}
// ForkJoin
public static void test2(Long begin, Long end) throws ExecutionException, InterruptedException {
long start = System.currentTimeMillis();
ForkJoinTest joinTest = new ForkJoinTest(begin, end);
ForkJoinPool pool = new ForkJoinPool();
// pool.execute(()->joinTest.compute()); 执行任务,无返回值
ForkJoinTask<Long> task = pool.submit(() -> joinTest.compute());
Long sum = task.get();
long finish = System.currentTimeMillis();
System.out.println("计算结果为:" + sum + "====>运行时间:" + (finish-start));
}
// Stream
public static void test3(Long begin, Long end){
long start = System.currentTimeMillis();
// rangeClosed: (] 左:开区间,右:闭区间
// range: () 左:开区间,右:开区间
long sum = LongStream.rangeClosed(begin, end).parallel().reduce(0, Long::sum);
long finish = System.currentTimeMillis();
System.out.println("计算结果为:" + sum + "====>运行时间:" + (finish-start));
}
// Math
public static void test4(Long begin, Long end){
long start = System.currentTimeMillis();
long sum = ((begin + end) * (end - begin + 1) / 2);
long finish = System.currentTimeMillis();
System.out.println("计算结果为:" + sum + "====>运行时间:" + (finish-start));
}
}
14. 异步回调 Future< V >
- Future< V >
- CompletavleFuture< T >
- 当两个或多个线程试图complete , completeExceptionally ,或cancel一个CompletableFuture,只有一个成功。
- CompletavleFuture< T >
14.1. 测试无返回值的异步回调
public static void testASync(){
//public static CompletableFuture<Void> runAsync(Runnable runnable) {
// return asyncRunStage(asyncPool, runnable);
//}
CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()->{
try {
TimeUnit.SECONDS.sleep(5);
System.out.println("completableFuture is coming");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
for (int i = 0; i < 10; i++) {
try {
System.out.println(i);
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 结果:
* 0
* 1
* 2
* 3
* 4
* completableFuture is coming
* 5
* 6
* 7
* 8
* 9
*/
}
14.2.测试有返回值的异步回调
public static void testSupplyAsync() throws ExecutionException, InterruptedException {
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(()->{
// int x = 10 / 0 ; 错误异常时,打印错误信息
// t===> null
// u===> java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
// java.lang.ArithmeticException: / by zero
System.out.println(Thread.currentThread().getName() + "执行了, return Integer");
// 运行成功时,打印正确信息
// ForkJoinPool.commonPool-worker-1执行了, return Integer
//t===> 1
//u===> null
return 1;
});
completableFuture
// whenComplete( BiConsumer<? super T, ? super Throwable> action)
// @FunctionalInterface
// public interface BiConsumer<T, U>
// 当编译成功时:
.whenComplete((t,u)->{
System.out.println("t===> " + t);
System.out.println("u===> " + u);
})
// CompletableFuture<T> exceptionally( Function<Throwable, ? extends T> fn)
// 当编译失败时:
.exceptionally((e)->{
System.out.println(e.getMessage());
return 0; // 错误时的返回值
})
.get();
}
14.3. CompletableFuture API
static CompletableFuture<Void> runAsync(Runnable runnable)
返回一个新的CompletableFuture,它在运行给定操作后由在 ForkJoinPool.commonPool()中运行的任务异步完成。
static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
返回一个新的CompletableFuture,它在运行给定操作之后由在给定执行程序中运行的任务异步完成。
static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
返回一个新的CompletableFuture,它由在 ForkJoinPool.commonPool()中运行的任务异步完成,并通过调用给定的供应商获得的值。
static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
返回一个新的CompletableFuture,由给定执行器中运行的任务异步完成,并通过调用给定的供应商获得的值。
15. JMM
Volatile
Volatile是Java虚拟机提供的轻量级同步机制
- 保证可见性
- 不保证原子性
- 禁止指令重排
JMM
Java内存模型(非物理)
关于 JMM 同步约定
-
线程操作共享变量时,是将主内存中的共享变量拷贝到自己的工作内存中,对工作内存中拷贝的这一份变量进行操作。
-
线程加锁前,必须将主内存中的共享变量拷贝一份最新更新的值到自己的工作内存中
-
线程解锁前,必须将工作内存中的变量立刻更新到主内存中的共享变量。
-
加锁和解锁必须保证是同一把锁
-
1 & 2 ; 3 & 4; 5 & 6;Lock & UnLock 4组操作必须成对出现。
如果线程B更新了falg的值为false,A中依旧使用的是falg = true (flag的修改 A不可见)。那么程序就可能会出现错误。
测试不可见变量问题
private static int num = 0;
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
int i = 0;
while (num == 0){
}
}).start();
TimeUnit.SECONDS.sleep(5);
// 将num设为1
num = 1;
System.out.println("num设置成功,当前值为:" + num);
// 当num变为1之后,程序没有结束。
}
解决问题: 保证可见性 (使用关键字volatile)
private static volatile int num = 0;
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
int i = 0;
while (num == 0){
}
}).start();
TimeUnit.SECONDS.sleep(5);
// 将num设为1
num = 1;
System.out.println("num设置成功,当前值为:" + num);
// num设置成功,当前值为:1
//
// Process finished with exit code 0
// 程序正常停止了
}
关于不加volatile关键字,while()中加了println语句程序会停止运行问题
// System.out.println中有synchronized代码块,会造成线程工作内存的变量更新。
public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}
16. Volatile
- 保证可见性
- 不保证原子性
public class VolatileTest {
public static int number = 0;
public static void add(){
number++;
}
public static void main(String[] args) {
for (int i = 1; i <= 20; i++) {
new Thread(()->{
for (int j = 0; j < 1000; j++) {
add();
}
}).start();
}
while (Thread.activeCount() > 2){
Thread.yield();
}
System.out.println(number); // 19790
}
}
- 不加锁如何保证原子性
public class VolatileTest {
// public static int number = 0;
// 原子类型
public static AtomicInteger number = new AtomicInteger();
public static void add(){
// number++;
number.getAndIncrement();
}
public static void main(String[] args) {
for (int i = 1; i <= 20; i++) {
new Thread(()->{
for (int j = 0; j < 1000; j++) {
add();
}
}).start();
}
while (Thread.activeCount() > 2){
Thread.yield();
}
System.out.println(number); // 20000
}
}
17. 深入单例模式
饿汉式、懒汉式
- 饿汉式
public class Hungry {
private static final Hungry HUNGRY = new Hungry();
public static final Hungry getHungry(){
return HUNGRY;
}
}
- 懒汉式 (线程不安全)
public class Lazy {
private static Lazy lazy;
// 私有构造器
private Lazy(){}
public static Lazy getLazy(){
if (lazy == null){
lazy = new Lazy();
}
return lazy;
}
}
- 懒汉式(线程安全解决方案:双重检测锁)
public class Lazy {
// 加volatile关键字的作用:new Lazy()是非原子性操作,极端情况下,有出现问题的可能。
private volatile static Lazy lazy;
// 私有构造器
private Lazy(){}
public static Lazy getLazy(){
if (lazy == null){
synchronized (Lazy.class){
if (lazy == null){
lazy = new Lazy(); // 非原子性操作
}
}
}
return lazy;
}
}
- 反射破坏单例模式的解决方案 【枚举】
18. 深入理解CAS
什么是CAS?
Java 并发机制实现原子操作有两种: 一种是锁,一种是CAS。
CAS是Compare And Swap(比较并替换)的缩写。
java.util.concurrent.atomic中的很多类
(AtomicInteger AtomicBoolean AtomicLong等)都使用了CAS。
CAS: 比较当前工作内存中的期望值和主内存中的值是否一致,如果一致,则执行操作。如果不一致,就一直循环
CAS的缺点
- 循环耗时
- 一次性只能保证一个共享变量的原子性
- 会出现ABA问题
ABA问题:
public class ABATest {
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(10);
// ABA问题 Start
System.out.println(atomicInteger.compareAndSet(10, 100));
System.out.println(atomicInteger); // 100
System.out.println(atomicInteger.compareAndSet(100, 10));
System.out.println(atomicInteger); // 10
// ABA问题 End
// 此时,atomicInteger已经被其它线程修改过了。
System.out.println(atomicInteger.compareAndSet(10, 1234));
System.out.println(atomicInteger); // 1234
}
}
19. 原子引用(AtomicReference)
带版本号的原子操作
20.锁进阶
20.1. 可重入锁
20.2. 公平锁、非公平锁
20.3. 自旋锁
20.4. 死锁
死锁问题定位
-
- 使用
jps -l
查看当前正在运行的Java进程 - 使用
jstack 进程号
查看进程的堆栈信息
- 使用