总结:
Thread.State
public enum State{
NEW, //(新建)
RUNNABLE,//(准备就绪)
BLOCKED,//(阻塞)
WAITING,//(不见不散)
TIMED_WAITING,//(过时不候)
TERMINATED;//(终结)
}
并行意味着可以同时取多个任务,并同时取执行所取得的这些任务。并行模式相当于将长长的一条队列,划分成了多条短队列,所以并行缩短了任务队列的长度。并行的效率从代码层次上依赖于多进程/多线程代码,从硬件角度上则依赖于多核CPU。
Runnable和Callable接口
class Ticket {
//票数
private int number = 30;
//操作方法:卖票
public synchronized void sale() {
//判断:是否有票
if(number > 0) {
System.out.println(Thread.currentThread().getName()+" : "+(number--)+" "+number);
}
}
}
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}
下面来逐个讲述 Lock 接口中每个方法的使
Lock lock = ...;
lock.lock();
try{
//处理任务
}catch(Exception ex){
}finally{
lock.unlock(); //释放锁
}
public class Test {
private ArrayList<Integer> arrayList = new ArrayList<Integer>();
public static void main(String[] args) {
final Test test = new Test();
new Thread(){
public void run() {
test.insert(Thread.currentThread());
};
}.start();
new Thread(){
public void run() {
test.insert(Thread.currentThread());
};
}.start();
}
public void insert(Thread thread) {
Lock lock = new ReentrantLock(); //注意这个地方
lock.lock();
try {
System.out.println(thread.getName()+"得到了锁");
for(int i=0;i<5;i++) {
arrayList.add(i);
}
} catch (Exception e) {
// TODO: handle exception
}finally {
System.out.println(thread.getName()+"释放了锁");
lock.unlock();
}
}
}
public interface ReadWriteLock {
/**
* Returns the lock used for reading.
*
* @return the lock used for reading.
*/
Lock readLock();
/**
* Returns the lock used for writing.
*
* @return the lock used for writing.
*/
Lock writeLock();
}
下面通过几个例子来看一下 ReentrantReadWriteLock 具体用法。
假如有多个线程要同时进行读操作的话,先看一下 synchronized 达到的效果:
public class Test {
private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
public static void main(String[] args) {
final Test test = new Test();
new Thread(){
public void run() {
test.get(Thread.currentThread());
};
}.start();
new Thread(){
public void run() {
test.get(Thread.currentThread());
};
}.start();
}
public synchronized void get(Thread thread) {
long start = System.currentTimeMillis();
while(System.currentTimeMillis() - start <= 1) {
System.out.println(thread.getName()+"正在进行读操作");
}
System.out.println(thread.getName()+"读操作完毕");
}
}
而改成用读写锁的话:
public class Test {
private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
public static void main(String[] args) {
final Test test = new Test();
new Thread(){
public void run() {
test.get(Thread.currentThread());
};
}.start();
new Thread(){
public void run() {
test.get(Thread.currentThread());
};
}.start();
}
public void get(Thread thread) {
rwl.readLock().lock();
try {
long start = System.currentTimeMillis();
while(System.currentTimeMillis() - start <= 1) {
System.out.println(thread.getName()+"正在进行读操作");
}
System.out.println(thread.getName()+"读操作完毕");
} finally {
rwl.readLock().unlock();
}
}
}
说明 thread1 和 thread2 在同时进行读操作。这样就大大提升了读操作的效率。
注意
在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时 Lock 的性能要远远优于synchronized。
/**
* volatile 关键字实现线程交替加减
*/
public class TestVolatile {
/**
* 交替加减
*/
public static void main(String[] args){
DemoClass demoClass = new DemoClass();
new Thread(() ->{
for (int i = 0; i < 5; i++) {
demoClass.increment();
}
}, "线程 A").start();
new Thread(() ->{
for (int i = 0; i < 5; i++) {
demoClass.decrement();
}
}, "线程 B").start();
}
}
class DemoClass{
//加减对象
private int number = 0;
/**
* 加 1
*/
public synchronized void increment() {
try {
while (number != 0){//使用while防止虚假唤醒
this.wait();
}
number++;
System.out.println("--------" + Thread.currentThread().getName() + "加一成功----------,值为:" + number);
notifyAll();
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 减一
*/
public synchronized void decrement(){
try {
while (number == 0){//使用while防止虚假唤醒
this.wait();
}
number--;
System.out.println("--------" + Thread.currentThread().getName() + "减一成功----------,值为:" + number);
notifyAll();
}catch (Exception e){
e.printStackTrace();
}
}
}
class DemoClass{
//加减对象
private int number = 0;
//声明锁
private Lock lock = new ReentrantLock();
//声明钥匙
private Condition condition = lock.newCondition();
/**
* 加 1
*/
public void increment() {
try {
lock.lock();
while (number != 0){//使用while防止虚假唤醒
condition.await();
}
number++;
System.out.println("--------" + Thread.currentThread().getName() + "加一成功----------,值为:" + number);
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
/**
* 减一
*/
public void decrement(){
try {
lock.lock();
while (number == 0){//使用while防止虚假唤醒
condition.await();
}
number--;
System.out.println("--------" + Thread.currentThread().getName() + "减一成功----------,值为:" + number);
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
问题: A 线程打印 5 次 A,B 线程打印 10 次 B,C 线程打印 15 次 C,按照此顺序循环 10 轮
class DemoClass{
//通信对象:0--打印 A 1---打印 B 2----打印 C
private int number = 0;
//声明锁
private Lock lock = new ReentrantLock();
//声明钥匙 A
private Condition conditionA = lock.newCondition();
//声明钥匙 B
private Condition conditionB = lock.newCondition();
//声明钥匙 C
private Condition conditionC = lock.newCondition();
/**
* A 打印 5 次
*/
public void printA(int j){
try {
lock.lock();
while (number != 0){
conditionA.await();
}
System.out.println(Thread.currentThread().getName() + "输出 A,第" + j + "轮开始");
//输出 5 次 A
for (int i = 0; i < 5; i++) {
System.out.println("A");
}
//开始打印 B
number = 1;
//唤醒 B
conditionB.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
/**
* B 打印 10 次
*/
public void printB(int j){
try {
lock.lock();
while (number != 1){
conditionB.await();
}
System.out.println(Thread.currentThread().getName() + "输出 B,第" + j + "轮开始");
//输出 10 次 B
for (int i = 0; i < 10; i++) {
System.out.println("B");
}
//开始打印 C
number = 2;
//唤醒 C
conditionC.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
/**
* C 打印 15 次
*/
public void printC(int j){
try {
lock.lock();
while (number != 2){
conditionC.await();
}
System.out.println(Thread.currentThread().getName() + "输出 C,第" + j + "轮开始");
//输出 15 次 C
for (int i = 0; i < 15; i++) {
System.out.println("C");
}
System.out.println("-----------------------------------------");
//开始打印 A
number = 0;
//唤醒 A
conditionA.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
测试类
/**
* volatile 关键字实现线程交替加减
*/
public class TestVolatile {
/**
* 交替加减
*/
public static void main(String[] args){
DemoClass demoClass = new DemoClass();
new Thread(() ->{
for (int i = 1; i <= 10; i++) {
demoClass.printA(i);
}
}, "A 线程").start();
new Thread(() ->{
for (int i = 1; i <= 10; i++) {
demoClass.printB(i);
}
}, "B 线程").start();
new Thread(() ->{
for (int i = 1; i <= 10; i++) {
demoClass.printC(i);
}
}, "C 线程").start();
}
}
NotSafeDemo
/**
* 集合线程安全案例
*/
public class NotSafeDemo {
/**
* 多个线程同时对集合进行修改
*/
public static void main(String[] args) {
List list = new ArrayList();
for (int i = 0; i < 100; i++) {
new Thread(() ->{
list.add(UUID.randomUUID().toString());
System.out.println(list);
}, "线程" + i).start();
}
}
}
java.util.ConcurrentModificationException
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return true (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
NotSafeDemo 代码修改
/**
* 集合线程安全案例
*/
public class NotSafeDemo {
/**
* 多个线程同时对集合进行修改
*/
public static void main(String[] args) {
List list = new Vector();
for (int i = 0; i < 100; i++) {
new Thread(() ->{
list.add(UUID.randomUUID().toString());
System.out.println(list);
}, "线程" + i).start();
}
}
}
现在没有运行出现并发异常,为什么?
查看 Vector 的 add 方法
/**
* Appends the specified element to the end of this Vector.
*
* @param e element to be appended to this Vector
* @return {@code true} (as specified by {@link Collection#add})
* @since 1.2
*/
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
add 方法被 synchronized 同步修辞,线程安全!因此没有并发异常
NotSafeDemo 代码修
/**
* 集合线程安全案例
*/
public class NotSafeDemo {
/**
* 多个线程同时对集合进行修改
*/
public static void main(String[] args) {
List list = Collections.synchronizedList(new ArrayList<>());
for (int i = 0; i < 100; i++) {
new Thread(() ->{
list.add(UUID.randomUUID().toString());
System.out.println(list);
}, "线程" + i).start();
}
}
}
没有并发修改异常
查看方法源码
/**
* Returns a synchronized (thread-safe) list backed by the specified
* list. In order to guarantee serial access, it is critical that
* all access to the backing list is accomplished
* through the returned list.
*
* It is imperative that the user manually synchronize on the returned
* list when iterating over it:
*
* List list = Collections.synchronizedList(new ArrayList());
* ...
* synchronized (list) {
* Iterator i = list.iterator(); // Must be in synchronized block
* while (i.hasNext())
* foo(i.next());
* }
*
* Failure to follow this advice may result in non-deterministic behavior.
*
* The returned list will be serializable if the specified list is
* serializable.
*
* @param the class of the objects in the list
* @param list the list to be "wrapped" in a synchronized list.
* @return a synchronized view of the specified list.
*/
public static <T> List<T> synchronizedList(List<T> list) {
return (list instanceof RandomAccess ?
new SynchronizedRandomAccessList<>(list) :
new SynchronizedList<>(list));
}
NotSafeDemo 代码修改
/**
* 集合线程安全案例
*/
public class NotSafeDemo {
/**
* 多个线程同时对集合进行修改
* @param args
*/
public static void main(String[] args) {
List list = new CopyOnWriteArrayList();
for (int i = 0; i < 100; i++) {
new Thread(() ->{
list.add(UUID.randomUUID().toString());
System.out.println(list);
}, "线程" + i).start();
}
}
}
没有线程安全问题
原因分析(重点):动态数组与线程安全
下面从“动态数组”和“线程安全”两个方面进一步对
CopyOnWriteArrayList 的原理进行说明。
class Phone {
public static synchronized void sendSMS() throws Exception {
//停留 4 秒
TimeUnit.SECONDS.sleep(4);
System.out.println("------sendSMS");
}
public synchronized void sendEmail() throws Exception {
System.out.println("------sendEmail");
}
public void getHello() {
System.out.println("------getHello");
}
}
/*
1 标准访问,先打印短信还是邮件
------sendSMS
------sendEmail
2 停 4 秒在短信方法内,先打印短信还是邮件
------sendSMS
------sendEmail
3 新增普通的 hello 方法,是先打短信还是 hello
------getHello
------sendSMS
4 现在有两部手机,先打印短信还是邮件
------sendEmail
------sendSMS
5 两个静态同步方法,1 部手机,先打印短信还是邮件
------sendSMS
------sendEmail
6 两个静态同步方法,2 部手机,先打印短信还是邮件
------sendSMS
------sendEmail
7 1 个静态同步方法,1 个普通同步方法,1 部手机,先打印短信还是邮件
------sendEmail
------sendSMS
8 1 个静态同步方法,1 个普通同步方法,2 部手机,先打印短信还是邮件
------sendEmail
------sendSMS
*/
创建新类 MyThread 实现 runnable 接口
class MyThread implements Runnable{
@Override
public void run() {
}
}
新类 MyThread2 实现 callable 接口
class MyThread2 implements Callable<Integer>{
@Override
public Integer call() throws Exception {
return 200;
}
}
当 call()方法完成时,结果必须存储在主线程已知的对象中,以便主线程可以知道该线程返回的结果。为此,可以使用 Future 对象。将 Future 视为保存结果的对象–它可能暂时不保存结果,但将来会保存(一旦Callable 返回)。Future 基本上是主线程可以跟踪进度以及其他线程的结果的一种方式。要实现此接口,必须重写 5 种方法,这里列出了重要的方法,如下:
/**
* CallableDemo 案列
*/
public class CallableDemo {
/**
* 实现 runnable 接口
*/
static class MyThread1 implements Runnable{
/**
* run 方法
*/
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + "线程进入了 run方法");
}catch (Exception e){
e.printStackTrace();
}
}
}
/**
* 实现 callable 接口
*/
static class MyThread2 implements Callable{
/**
* call 方法
* @return
* @throws Exception
*/
@Override
public Long call() throws Exception {
try {
System.out.println(Thread.currentThread().getName() + "线程进入了 call方法,开始准备睡觉");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "睡醒了");
}catch (Exception e){
e.printStackTrace();
}
return System.currentTimeMillis();
}
}
public static void main(String[] args) throws Exception{
//声明 runable
Runnable runable = new MyThread1();
//声明 callable
Callable callable = new MyThread2();
//future-callable
FutureTask<Long> futureTask2 = new FutureTask(callable);
//线程二
new Thread(futureTask2, "线程二").start();
for (int i = 0; i < 10; i++) {
Long result1 = futureTask2.get();
System.out.println(result1);
}
//线程一
new Thread(runable,"线程一").start();
}
}
场景: 6 个同学陆续离开教室后值班同学才可以关门。
/**
* CountDownLatchDemo
*/
public class CountDownLatchDemo {
/**
* 6 个同学陆续离开教室后值班同学才可以关门
* @param args
*/
public static void main(String[] args) throws Exception{
//定义一个数值为 6 的计数器
CountDownLatch countDownLatch = new CountDownLatch(6);
//创建 6 个同学
for (int i = 1; i <= 6; i++) {
new Thread(() ->{
try{
if(Thread.currentThread().getName().equals("同学 6")){
Thread.sleep(2000);
}
System.out.println(Thread.currentThread().getName() + "离开了");
//计数器减一,不会阻塞
countDownLatch.countDown();
}catch (Exception e){
e.printStackTrace();
}
}, "同学" + i).start();
}
//主线程 await 休息
System.out.println("主线程睡觉");
countDownLatch.await();
//全部离开后自动唤醒主线程
System.out.println("全部离开了,现在的计数器为" +
countDownLatch.getCount());
}
}
场景: 集齐 7 颗龙珠就可以召唤神龙
CyclicBarrierDemo
/**
* CyclicBarrierDemo 案列
*/
public class CyclicBarrierDemo {
//定义神龙召唤需要的龙珠总数
private final static int NUMBER = 7;
/**
* 集齐 7 颗龙珠就可以召唤神龙
* @param args
*/
public static void main(String[] args) {
//定义循环栅栏
CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER, () ->{
System.out.println("集齐" + NUMBER + "颗龙珠,现在召唤神龙!!!!!!!!!");
});
//定义 7 个线程分别去收集龙珠
for (int i = 1; i <= 7; i++) {
new Thread(()->{
try {
if(Thread.currentThread().getName().equals("龙珠 3 号")){
System.out.println("龙珠 3 号抢夺战开始,孙悟空开启超级赛亚人模式!");
Thread.sleep(5000);
System.out.println("龙珠 3 号抢夺战结束,孙悟空打赢了,拿到了龙珠3号!");
}else{
System.out.println(Thread.currentThread().getName() + "收集到了!!!!");
}
cyclicBarrier.await();
}catch (Exception e){
e.printStackTrace();
}
}, "龙珠" + i + "号").start();
}
}
}
场景: 抢车位, 6 部汽车 3 个停车位
SemaphoreDemo
/**
* Semaphore 案列
*/
public class SemaphoreDemo {
/**
* 抢车位, 10 部汽车 1 个停车位
* @param args
*/
public static void main(String[] args) throws Exception{
//定义 3 个停车位
Semaphore semaphore = new Semaphore(1);
//模拟 6 辆汽车停车
for (int i = 1; i <= 10; i++) {
Thread.sleep(100);
//停车
new Thread(() ->{
try {
System.out.println(Thread.currentThread().getName() + "找车位 ing");
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "汽车停车成功!");
Thread.sleep(10000);
}catch (Exception e){
e.printStackTrace();
}finally {
System.out.println(Thread.currentThread().getName() + "溜了溜了");
semaphore.release();
}
}, "汽车" + i).start();
}
}
}
ReentrantReadWriteLock 类的整体结构
public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable {
/* 读锁 */
private final ReentrantReadWriteLock.ReadLock readerLock;
/* 写锁 */
private final ReentrantReadWriteLock.WriteLock writerLock;
final Sync sync;
/*使用默认(非公平)的排序属性创建一个新的ReentrantReadWriteLock */
public ReentrantReadWriteLock() {
this(false);
}
/* 使用给定的公平策略创建一个新的 ReentrantReadWriteLock */
public ReentrantReadWriteLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
readerLock = new ReadLock(this);
writerLock = new WriteLock(this);
}
/** 返回用于写入操作的锁 */
public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
/** 返回用于读取操作的锁 */
public ReentrantReadWriteLock.ReadLock readLock() { return readerLock; }
abstract static class Sync extends AbstractQueuedSynchronizer {}
static final class NonfairSync extends Sync {}
static final class FairSync extends Sync {}
public static class ReadLock implements Lock, java.io.Serializable {}
public static class WriteLock implements Lock, java.io.Serializable {}
}
场景: 使用 ReentrantReadWriteLock 对一个 hashmap 进行读和写操作
//资源类
class MyCache {
//创建 map 集合
private volatile Map<String,Object> map = new HashMap<>();
//创建读写锁对象
private ReadWriteLock rwLock = new ReentrantReadWriteLock();
//放数据
public void put(String key,Object value) {
//添加写锁
rwLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName()+" "+key);
//暂停一会
TimeUnit.MICROSECONDS.sleep(300);
//放数据
map.put(key,value);
System.out.println(Thread.currentThread().getName()+" "+key);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//释放写锁
rwLock.writeLock().unlock();
}
}
//取数据
public Object get(String key) {
//添加读锁
rwLock.readLock().lock();
Object result = null;
try {
System.out.println(Thread.currentThread().getName()+" "+key);
//暂停一会
TimeUnit.MICROSECONDS.sleep(300);
result = map.get(key);
System.out.println(Thread.currentThread().getName()+" "+key);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//释放读锁
rwLock.readLock().unlock();
}
return result;
}
}
Concurrent 包中,BlockingQueue 很好的解决了多线程中,如何高效安全“传输”数据的问题。通过这些高效并且线程安全的队列类,为我们快速搭建高质量的多线程程序带来极大的便利。本文详细介绍了 BlockingQueue 家庭中的所有成员,包括他们各自的功能以及常见使用场景。
当队列是空的,从队列中获取元素的操作将会被阻塞
当队列是满的,从队列中添加元素的操作将会被阻塞
试图从空的队列中获取元素的线程将会被阻塞,直到其他线程往空的队列插入新的元素
试图向已满的队列中添加新元素的线程将会被阻塞,直到其他线程从队列中移除一个或多个元素或者完全清空,使队列变得空闲起来并后续新增
常用的队列主要有以下两种:
在多线程领域:所谓阻塞,在某些情况下会挂起线程(即阻塞),一旦条件满足,被挂起的线程又会自动被唤起
为什么需要 BlockingQueue
多线程环境中,通过队列可以很容易实现数据共享,比如经典的“生产者”和“消费者”模型中,通过队列可以很便利地实现两者之间的数据共享。假设我们有若干生产者线程,另外又有若干个消费者线程。如果生产者线程需要把准备好的数据共享给消费者线程,利用队列的方式来传递数据,就可以很方便地解决他们之间的数据共享问题。但如果生产者和消费者在某个时间段内,万一发生数据处理速度不匹配的情况呢?理想情况下,如果生产者产出数据的速度大于消费者消费的速度,并且当生产出来的数据累积到一定程度的时候,那么生产者必须暂停等待一下(阻塞生产者线程),以便等待消费者线程把累积的数据处理完毕,反之亦然。
方法类型 | 抛出异常 | 特殊值 | 阻塞 | 超时 |
---|---|---|---|---|
插入 | add(e) | offer(e) | put(e) | offer(e,time,unit) |
移除 | remove() | poll() | take() | poll(time,unit) |
检查 | element() | peek() | 不可用 | 不可用 |
/**
* 阻塞队列
*/
public class BlockingQueueDemo {
public static void main(String[] args) throws InterruptedException {
// List list = new ArrayList();
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
//第一组
// System.out.println(blockingQueue.add("a"));
// System.out.println(blockingQueue.add("b"));
// System.out.println(blockingQueue.add("c"));
// System.out.println(blockingQueue.element());
//System.out.println(blockingQueue.add("x"));
// System.out.println(blockingQueue.remove());
// System.out.println(blockingQueue.remove());
// System.out.println(blockingQueue.remove());
// System.out.println(blockingQueue.remove());
// 第二组
// System.out.println(blockingQueue.offer("a"));
// System.out.println(blockingQueue.offer("b"));
// System.out.println(blockingQueue.offer("c"));
// System.out.println(blockingQueue.offer("x"));
// System.out.println(blockingQueue.poll());
// System.out.println(blockingQueue.poll());
// System.out.println(blockingQueue.poll());
// System.out.println(blockingQueue.poll());
// 第三组
// blockingQueue.put("a");
// blockingQueue.put("b");
// blockingQueue.put("c");
// //blockingQueue.put("x");
// System.out.println(blockingQueue.take());
// System.out.println(blockingQueue.take());
// System.out.println(blockingQueue.take());
// System.out.println(blockingQueue.take());
// 第四组
System.out.println(blockingQueue.offer("a"));
System.out.println(blockingQueue.offer("b"));
System.out.println(blockingQueue.offer("c"));
System.out.println(blockingQueue.offer("a",3L, TimeUnit.SECONDS));
}
}
本次介绍 5 种类型的线程池
参数 | 说明 |
---|---|
corePoolSize | 线程池的核心线程数 |
maximumPoolSize | 能容纳的最大线程数 |
keepAliveTime | 空闲线程存活时间 |
unit | 存活的时间单位 |
workQueue | 存放提交但未执行任务的队列 |
threadFactory | 创建线程的工厂类 |
handler | 等待队列满后的拒绝策略 |
作用:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程.
特点:
创建方式:
/**
* 可缓存线程池
* @return
*/
public static ExecutorService newCachedThreadPool(){
/**
* corePoolSize 线程池的核心线程数
* maximumPoolSize 能容纳的最大线程数
* keepAliveTime 空闲线程存活时间
* unit 存活的时间单位
* workQueue 存放提交但未执行任务的队列
* threadFactory 创建线程的工厂类:可以省略
* handler 等待队列满后的拒绝策略:可以省略
*/
return new ThreadPoolExecutor(0,
Integer.MAX_VALUE,
60L,
TimeUnit.SECONDS,
new SynchronousQueue<>(),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
}
场景: 适用于创建一个可无限扩大的线程池,服务器负载压力较轻,执行时间较短,任务多的场景
作用:创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。在任意点,在大多数线程会处于处理任务的活动状态。如果在所有线程处于活动状态时提交附加任务,则在有可用线程之前,附加任务将在队列中等待。如果在关闭前的执行期间由于失败而导致任何线程终止,那么一个新线程将代替它执行后续的任务(如果需要)。在某个线程被显式地关闭之前,池中的线程将一直存在。
特征:
创建方式:
/**
* 固定长度线程池
* @return
*/
public static ExecutorService newFixedThreadPool(){
/**
* corePoolSize 线程池的核心线程数
* maximumPoolSize 能容纳的最大线程数
* keepAliveTime 空闲线程存活时间
* unit 存活的时间单位
* workQueue 存放提交但未执行任务的队列
* threadFactory 创建线程的工厂类:可以省略
* handler 等待队列满后的拒绝策略:可以省略
*/
return new ThreadPoolExecutor(10,
10,
0L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
}
场景: 适用于可以预测线程数量的业务中,或者服务器负载较重,对线程数有严
格限制的场景
作用:创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。(注意,如果因为在关闭前的执行期间出现失败而终止了此单个线程,那么如果需要,一个新线程将代替它执行后续的任务)。可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。与其他等效的newFixedThreadPool 不同,可保证无需重新配置此方法所返回的执行程序即可使用其他的线程。
特征: 线程池中最多执行 1 个线程,之后提交的线程活动将会排在队列中以此执行
创建方式:
/**
* 单一线程池
* @return
*/
public static ExecutorService newSingleThreadExecutor(){
/**
* corePoolSize 线程池的核心线程数
* maximumPoolSize 能容纳的最大线程数
* keepAliveTime 空闲线程存活时间
* unit 存活的时间单位
* workQueue 存放提交但未执行任务的队列
* threadFactory 创建线程的工厂类:可以省略
* handler 等待队列满后的拒绝策略:可以省略
*/
return new ThreadPoolExecutor(1,
1,
0L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
}
场景: 适用于需要保证顺序执行各个任务,并且在任意时间点,不会同时有多个
线程的场景
作用: 线程池支持定时以及周期性执行任务,创建一个 corePoolSize 为传入参
数,最大线程数为整形的最大数的线程池
特征:
(1)线程池中具有指定数量的线程,即便是空线程也将保留
(2)可定时或者延迟执行线程活动
创建方式:
public static ScheduledExecutorService newScheduledThreadPool(intcorePoolSize, ThreadFactory threadFactory){
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
场景: 适用于需要多个后台线程执行周期任务的场景
创建方式:
public static ExecutorService newWorkStealingPool(int parallelism) {
/**
* parallelism:并行级别,通常默认为 JVM 可用的处理器个数
* factory:用于创建 ForkJoinPool 中使用的线程。
* handler:用于处理工作线程未处理的异常,默认为 null
* asyncMode:用于控制 WorkQueue 的工作模式:队列---反队列
*/
return new ForkJoinPool(parallelism,
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null,
true);
}
场景: 适用于大耗时,可并行执行的场景
场景: 火车站 3 个售票口, 10 个用户买票
/**
* 入门案例
*/
public class ThreadPoolDemo1 {
/**
* 火车站 3 个售票口, 10 个用户买票
* @param args
*/
public static void main(String[] args) {
//定时线程次:线程数量为 3---窗口数为 3
ExecutorService threadService = new ThreadPoolExecutor(3,
3,
60L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy());
try {
//10 个人买票
for (int i = 1; i <= 10; i++) {
threadService.execute(()->{
try {
System.out.println(Thread.currentThread().getName() + "窗口,开始卖票");
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName() + "窗口买票结束");
}catch (Exception e){
e.printStackTrace();
}
});
}
}catch (Exception e){
e.printStackTrace();
}finally {
//完成后结束
threadService.shutdown();
}
}
}
Fork/Join 它可以将一个大的任务拆分成多个子任务进行并行处理,最后将子任务结果合并成最后的计算结果,并进行输出。Fork/Join 框架要完成两件事情:
任务分割:首先 Fork/Join 框架需要把大的任务分割成足够小的子任务,如果子任务比较大的话还要对子任务进行继续分割
执行任务并合并结果:分割的子任务分别放到双端队列里,然后几个启动线程分别从双端队列里获取任务执行。子任务执行完的结果都放在另外一个队列里,启动一个线程从队列里取数据,然后合并这些数据。
在 Java 的 Fork/Join 框架中,使用两个类完成上述操作
Fork/Join 框架的实现原理
Fork 方法的实现原理: 当我们调用 ForkJoinTask 的 fork 方法时,程序会把任务放在 ForkJoinWorkerThread 的 pushTask 的 workQueue 中,异步地执行这个任务,然后立即返回结果
public final ForkJoinTask<V> fork() {
Thread t;
if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)
((ForkJoinWorkerThread)t).workQueue.push(this);
else
ForkJoinPool.common.externalPush(this);
return this;
}
pushTask 方法把当前任务存放在 ForkJoinTask 数组队列里。然后再调用ForkJoinPool 的 signalWork()方法唤醒或创建一个工作线程来执行任务。代码如下:
final void push(ForkJoinTask<?> task) {
ForkJoinTask<?>[] a; ForkJoinPool p;
int b = base, s = top, n;
if ((a = array) != null) { // ignore if queue removed
int m = a.length - 1; // fenced write for task visibility
U.putOrderedObject(a, ((m & s) << ASHIFT) + ABASE, task);
U.putOrderedInt(this, QTOP, s + 1);
if ((n = s - b) <= 1) {
if ((p = pool) != null)
p.signalWork(p.workQueues, this);//执行
}
else if (n >= m)
growArray();
}
}
Join 方法的主要作用是阻塞当前线程并等待获取结果。让我们一起看看ForkJoinTask 的 join 方法的实现,代码如下:
public final V join() {
int s;
if ((s = doJoin() & DONE_MASK) != NORMAL)
reportException(s);
return getRawResult();
}
它首先调用 doJoin 方法,通过 doJoin()方法得到当前任务的状态来判断返回什么结果,任务状态有 4 种:
已完成(NORMAL)、被取消(CANCELLED)、信号(SIGNAL)和出
现异常(EXCEPTIONAL)
private int doJoin() {
int s;
Thread t;
ForkJoinWorkerThread wt;
ForkJoinPool.WorkQueue w;
return (s = status) < 0 ? s :
((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
(w = (wt = (ForkJoinWorkerThread)t).workQueue).
tryUnpush(this) && (s = doExec()) < 0 ? s :
wt.pool.awaitJoin(w, this, 0L) :
externalAwaitDone();
}
final int doExec() {
int s; boolean completed;
if ((s = status) >= 0) {
try {
completed = exec();
} catch (Throwable rex) {
return setExceptionalCompletion(rex);
}
if (completed)
s = setCompletion(NORMAL);
}
return s;
}
在 doJoin()方法流程如下:
场景: 生成一个计算任务,计算 1+2+3…+1000,每 100 个数切分一个子任务
/**
* 递归累加
*/
public class TaskExample extends RecursiveTask<Long> {
private int start;
private int end;
private long sum;
/**
* 构造函数
* @param start
* @param end
*/
public TaskExample(int start, int end){
this.start = start;
this.end = end;
}
/**
* The main computation performed by this task.
*
* @return the result of the computation
*/
@Override
protected Long compute() {
System.out.println("任务" + start + "=========" + end + "累加开始");
//大于 100 个数相加切分,小于直接加
if(end - start <= 100){
for (int i = start; i <= end; i++) {
//累加
sum += i;
}
}else {
//切分为 2 块
int middle = start + 100;
//递归调用,切分为 2 个小任务
TaskExample taskExample1 = new TaskExample(start, middle);
TaskExample taskExample2 = new TaskExample(middle + 1, end);
//执行:异步
taskExample1.fork();
taskExample2.fork();
//同步阻塞获取执行结果
sum = taskExample1.join() + taskExample2.join();
}
//加完返回
return sum;
}
}
/**
* 分支合并案例
*/
public class ForkJoinPoolDemo {
/**
* 生成一个计算任务,计算 1+2+3.........+1000
* @param args
*/
public static void main(String[] args) {
//定义任务
TaskExample taskExample = new TaskExample(1, 1000);
//定义执行对象
ForkJoinPool forkJoinPool = new ForkJoinPool();
//加入任务执行
ForkJoinTask<Long> result = forkJoinPool.submit(taskExample);
//输出结果
try {
System.out.println(result.get());
}catch (Exception e){
e.printStackTrace();
}finally {
forkJoinPool.shutdown();
}
}
}
场景:主线程里面创建一个 CompletableFuture,然后主线程调用 get 方法会阻塞,最后我们在一个子线程中使其终止。
/**
* 主线程里面创建一个 CompletableFuture,然后主线程调用 get 方法会阻塞,最后我们
在一个子线程中使其终止
* @param args
*/
public static void main(String[] args) throws Exception{
CompletableFuture<String> future = new CompletableFuture<>();
new Thread(() -> {
try{
System.out.println(Thread.currentThread().getName() + "子线程开始干活");
//子线程睡 5 秒
Thread.sleep(5000);
//在子线程中完成主线程
future.complete("success");
}catch (Exception e){
e.printStackTrace();
}
}, "A").start();
//主线程调用 get 方法阻塞
System.out.println("主线程调用 get 方法获取结果为: " + future.get());
System.out.println("主线程完成,阻塞结束!!!!!!");
}
/**
* 没有返回值的异步任务
* @param args
*/
public static void main(String[] args) throws Exception{
System.out.println("主线程开始");
//运行一个没有返回值的异步任务
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
try {
System.out.println("子线程启动干活");
Thread.sleep(5000);
System.out.println("子线程完成");
} catch (Exception e) {
e.printStackTrace();
}
});
//主线程阻塞
future.get();
System.out.println("主线程结束");
}
/**
* 没有返回值的异步任务
* @param args
*/
public static void main(String[] args) throws Exception{
System.out.println("主线程开始");
//运行一个有返回值的异步任务
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
System.out.println("子线程开始任务");
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
}
return "子线程完成了!";
});
//主线程阻塞
String s = future.get();
System.out.println("主线程结束, 子线程的结果为:" + s);
}
当一个线程依赖另一个线程时,可以使用 thenApply 方法来把这两个线程串行化。
private static Integer num = 10;
/**
* 先对一个数加 10,然后取平方
* @param args
*/
public static void main(String[] args) throws Exception{
System.out.println("主线程开始");
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
try {
System.out.println("加 10 任务开始");
num += 10;
} catch (Exception e) {
e.printStackTrace();
}
return num;
}).thenApply(integer -> {
return num * num;
});
Integer integer = future.get();
System.out.println("主线程结束, 子线程的结果为:" + integer);
}
thenAccept 消费处理结果, 接收任务的处理结果,并消费处理,无返回结果。
public static void main(String[] args) throws Exception{
System.out.println("主线程开始");
CompletableFuture.supplyAsync(() -> {
try {
System.out.println("加 10 任务开始");
num += 10;
} catch (Exception e) {
e.printStackTrace();
}
return num;
}).thenApply(integer -> {
return num * num;
}).thenAccept(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println("子线程全部处理完成,最后调用了 accept,结果为:" + integer);
}
});
}
exceptionally 异常处理,出现异常时触发
public static void main(String[] args) throws Exception{
System.out.println("主线程开始");
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
int i= 1/0;
System.out.println("加 10 任务开始");
num += 10;
return num;
}).exceptionally(ex -> {
System.out.println(ex.getMessage());
return -1;
});
System.out.println(future.get());
}
handle 类似于 thenAccept/thenRun 方法,是最后一步的处理调用,但是同时可以处理异常
public static void main(String[] args) throws Exception{
System.out.println("主线程开始");
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
System.out.println("加 10 任务开始");
num += 10;
return num;
}).handle((i,ex) ->{
System.out.println("进入 handle 方法");
if(ex != null){
System.out.println("发生了异常,内容为:" + ex.getMessage());
return -1;
}else{
System.out.println("正常完成,内容为: " + i);
return i;
}
});
System.out.println(future.get());
}
thenCompose 合并两个有依赖关系的 CompletableFutures 的执行结果
public static void main(String[] args) throws Exception{
System.out.println("主线程开始");
//第一步加 10
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
System.out.println("加 10 任务开始");
num += 10;
return num;
});
//合并
CompletableFuture<Integer> future1 = future.thenCompose(i ->
//再来一个 CompletableFuture
CompletableFuture.supplyAsync(() -> {
return i + 1;
}));
System.out.println(future.get());
System.out.println(future1.get());
}
thenCombine 合并两个没有依赖关系的 CompletableFutures 任务 public static void
public static void main(String[] args) throws Exception{
System.out.println("主线程开始");
CompletableFuture<Integer> job1 = CompletableFuture.supplyAsync(() -> {
System.out.println("加 10 任务开始");
num += 10;
return num;
});
CompletableFuture<Integer> job2 = CompletableFuture.supplyAsync(() -> {
System.out.println("乘以 10 任务开始");
num = num * 10;
return num;
});
//合并两个结果
CompletableFuture<Object> future = job1.thenCombine(job2, new
BiFunction<Integer, Integer, List<Integer>>() {
@Override
public List<Integer> apply(Integer a, Integer b) {
List<Integer> list = new ArrayList<>();
list.add(a);
list.add(b);
return list;
}
});
System.out.println("合并结果为:" + future.get());
}
合并多个任务的结果 allOf 与 anyOf
allOf: 一系列独立的 future 任务,等其所有的任务执行完后做一些事情
/**
* 先对一个数加 10,然后取平方
* @param args
*/
public static void main(String[] args) throws Exception{
System.out.println("主线程开始");
List<CompletableFuture> list = new ArrayList<>();
CompletableFuture<Integer> job1 = CompletableFuture.supplyAsync(() -> {
System.out.println("加 10 任务开始");
num += 10;
return num;
});
list.add(job1);
CompletableFuture<Integer> job2 = CompletableFuture.supplyAsync(() -> {
System.out.println("乘以 10 任务开始");
num = num * 10;
return num;
});
list.add(job2);
CompletableFuture<Integer> job3 = CompletableFuture.supplyAsync(() -> {
System.out.println("减以 10 任务开始");
num = num * 10;
return num;
});
list.add(job3);
CompletableFuture<Integer> job4 = CompletableFuture.supplyAsync(() -> {
System.out.println("除以 10 任务开始");
num = num * 10;
return num;
});
list.add(job4);
//多任务合并
List<Integer> collect =
list.stream().map(CompletableFuture<Integer>::join).collect(Collectors.toList());
System.out.println(collect);
}
anyOf: 只要在多个 future 里面有一个返回,整个任务就可以结束,而不需要等到每一个
future 结束
/**
* 先对一个数加 10,然后取平方
* @param args
*/
public static void main(String[] args) throws Exception{
System.out.println("主线程开始");
CompletableFuture<Integer>[] futures = new CompletableFuture[4];
CompletableFuture<Integer> job1 = CompletableFuture.supplyAsync(() -> {
try{
Thread.sleep(5000);
System.out.println("加 10 任务开始");
num += 10;
return num;
}catch (Exception e){
return 0;
}
});
futures[0] = job1;
CompletableFuture<Integer> job2 = CompletableFuture.supplyAsync(() -> {
try{
Thread.sleep(2000);
System.out.println("乘以 10 任务开始");
num = num * 10;
return num;
}catch (Exception e){
return 1;
}
});
futures[1] = job2;
CompletableFuture<Integer> job3 = CompletableFuture.supplyAsync(() -> {
try{
Thread.sleep(3000);
System.out.println("减以 10 任务开始");
num = num * 10;
return num;
}catch (Exception e){
return 2;
}
});
futures[2] = job3;
CompletableFuture<Integer> job4 = CompletableFuture.supplyAsync(() -> {
try{
Thread.sleep(4000);
System.out.println("除以 10 任务开始");
num = num * 10;
return num;
}catch (Exception e){
return 3;
}
});
futures[3] = job4;
CompletableFuture<Object> future = CompletableFuture.anyOf(futures);
System.out.println(future.get());
}