1)进程:是一个程序运行的最小单位(巧记:先"进"来,然后才能看到线程)
2)线程:是进程里最小的运行单位(例如idea进程,里面有多个微服务在运行,就是线程)
3)线程的6种状态:
NEW(新生态),RUNNABLE(运行态),BLOCKED(阻塞态),WAITING(无限等待),TIMED_WAITING(有限等待),TERMINATED(死亡态)
并发:
在相对的一段时间内,其实是很短,对计算机有感,对人无感,多个线程抢占资源来执行自
己 ,执行A一段时间,马上执行B,然后执行C,以此类推,这几个线程对人来说可以看成
是"同时"完成的,也即并发,但对机器来说就是时间错开运行的.
并行:
多个线程在相同的时间内同时运行。
java.util.concuurent
java.util.concuurent.atomic
java.util.concuurent.locks
原理:countDownLatch主要有两个方法,当一个或多个线程调用await方法时,这些线程会阻塞.
其他线程调用countDown方法时,会将结果减1(调用countDown的线程不会阻塞)
当计数器的值变为0是,因为await阻塞的线程会被唤醒,继续执行
package com.itheima.juc;
import java.util.concurrent.CountDownLatch;
public class CountDownDemo {
public static void main(String[] args) throws Exception {
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 1; i <= 6; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "离开教室");
countDownLatch.countDown();
}, String.valueOf(i)).start();
}
countDownLatch.await();
System.out.println("班长锁门了-------------->");
}
}
和countdownlatch是相反的操作,可以理解为人到齐了再开会
package com.itheima.juc;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo {
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(7, () -> { System.out.println("召唤神龙"); });
for (int i = 1; i <= 7 ; i++) {
final int tmpInt = i;
new Thread(() -> {
System.out.println("收集到第"+tmpInt+"颗");
try {
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
},String.valueOf(i)).start();
}
}
}
在信号量上我们定义两种操作:
acquire(获取),当一个线程调用acquire是,它要么成功获取信号量(信号量-1)要么一直等下去,直到有线程释放信号量或者超时.
release(释放)实际上会将信号量值+1,然后唤醒等待的线程
信号量主要两个目的:1.用于多个共享资源的互斥作用,另一个用于控制并发线程数的控制
package com.itheima.juc;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class SemaphoreDemo {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <= 10; i++) {
final int tem = i;
new Thread(() -> {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "开始服务");
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName() + "结束");
} catch (Exception e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}, String.valueOf(i)).start();
}
}
}
1.原始构成
synchronized是关键字属于jvm层面,
monitorenter(底层是通过monitor对象来完成,其实wait、notify等方法也是依赖于monitor对象只有在同步块或方法中才能调wait、notify等方法)
monitorexit
lock是具体类(java.util.concurrent.Locks.lock)是api层面的锁
2.使用方法
synchronized不需要用户手动释放锁,当synchronized代码执行完后系统会自动让线程释放对锁的占用
reentrantlock则需要手动释放锁若没有主动释放锁,就有可能导致出现死锁现象(需要lock()和unlock()方法配合try、finally语句块来完成)
3.等待是否可中断
synchronized不可中断,除非抛出异常或者正常运行完成
reentrantlock可中断,
1.设置超时方法trylock(Long timeout ,TimeUtil unit)
2.lockInterruptibly()放代码块中,调用interrupt()方法可中断
4.加锁是否公平
synchronized非公平锁(非公平锁第一次未抢到资源,会变成公平锁去排队)
reentrantlock两者都可以,默认公平锁,构造方法可以传入Boolean值,true为公平锁,false为非公平锁
5.锁板顶多个条件condition
synchronized没有
reentrantlock用来实现分组唤醒需要唤醒的线程们,可以精确唤醒,而不是像synchronized要么随即唤醒一个线程要么唤醒全部线程。
1)volatile是java虚拟机提供的轻量级的同步机制
2)保证可见性,禁止指令重排,不保证原子性
1)JMM(Java内存模型Java Memory Model,简称JMM)本身是一种抽象的概念并不真实存在,它描述的是一组规则或规范,通过这组规范定义了程序中各个变量(包括实例字段、静态字段和构成数组对象的元素)的访问方式
2)JMM关于同步的规定:
1.线程解锁前,必须把共享变量的值刷新回主内存
2.线程加锁前,必须读取主内存的最新值到自己的工作内存
3.加锁解锁是同一把锁,由于JVM运行程序的实体是线程,而每个线程创建是JVm都会为其创建一个工作内存(有些地方成为栈空间),工作内存是每个线程的私有数据区域,而java内存模型中规定所有变量都存储在主内存,主内存是共享内存区域,所有线程都可以访问,**但线程对变量的操作(读取赋值等)必须在工作内存中进行,首先要将变量从主内存拷贝的自己的工作内存空间,然后对变量进行操作,操作完成后再降变量写回主内存,**不能直接操作主内存中的变量,各个线程中的工作内存中存储着主内存中的变量副本拷贝,因此不同的线程间无法访问对方的工作内存,线程间的通信(传值)必须通过主内存来完成,其简单访问过程如下图
package com.itheima.juc;
class SingleTon{
private static SingleTon instance;
private SingleTon (){
}
public static SingleTon getInstance(){
if (instance == null) {
synchronized (SingleTon.class){
if (instance == null) {
instance = new SingleTon();
}
}
}
return instance;
}
}
DCL(双端检测)机制不一定线程安全,原因是指令重排序的存在,加入volatile可以禁止指令重排
原因在于一个线程执行到第一次检测,读取到instance不为null时,instance的引用对象可能没有完成初始化。
instance = new SingleTon();可以分成以下3步完成(伪代码)
memory = allocate();1.分配对象内存空间
instance(memory);2.初始化对象
instance = memory;3.设置instance指向刚分配的内存地址,此时instance!= null
步骤2和步骤3不存在依赖关系,而且无论重排前还是重排后程序的执行结果在单线程中并没有改变。
因此这种重排优化是允许的。
memory = allocate();1.分配对象内存空间
instance = memory;3.设置instance指向刚分配的内存地址,此时instance!= null ,但是对象还没有初始化完成!
instance(memory);2.初始化对象
但是指令重排只会保证串行语义的执行的一致性(单线程),但并不关心多线程间的语义一致性。所以当一条线程访问instance不为空时,由于instance实例未必已经初始化完成,也就造成了线程安全问题
![25](assert/25.jpg)package com.itheima.juc;
import java.util.concurrent.TimeUnit;
public class LockThread implements Runnable {
private String lockA;
private String lockB;
public LockThread(String lockA, String lockB) {
this.lockA = lockA;
this.lockB = lockB;
}
@Override
public void run() {
synchronized (lockA){
System.out.println(Thread.currentThread().getName()+"\t 持有"+lockA + "试图获取"+ lockB);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB){
System.out.println(Thread.currentThread().getName()+"\t 持有"+lockB + "试图获取"+ lockA);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
String lockA = "lockA";
String lockB = "lockB";
new Thread(new LockThread(lockA,lockB),"AAAA").start();
new Thread(new LockThread(lockB,lockA),"BBBB").start();
}
}
1.jps(相当于linux系统下的ps -ef |grep XXXXX)是window下的java PS
定位进程号
2.jstack找到死锁查看
1)循环时间开销大
2)只能保证一个共享变量原子操作
3)存在ABA问题
题目:三个售票员,卖出,30张票
笔记:如何编写企业级的多线程代码
固定的编程套路+模板是什么?
1)在高内聚低耦合的前提下,建立线程操作资源类。
2)一个线程start之后不是马上就启动,调度跟底层的cup有关系
package com.itheima.juc;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TicketResource { //资源类 = 实例变量+实例方法
private int number = 120;
Lock lock = new ReentrantLock();
public void sale() {
lock.lock();
try {
if (number > 0) {
System.out.println(Thread.currentThread().getName() + "\t卖出第:" + (number--) + "\t还剩:" + number);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
package com.itheima.juc;
public class SaleTicket {
public static void main(String[] args) {
TicketResource ticket = new TicketResource();
/*new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i <=40 ; i++) {
ticket.sale();
}
}
}, "A").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i <=40 ; i++) {
ticket.sale();
}
}
}, "B").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i <=40 ; i++) {
ticket.sale();
}
}
}, "C").start();*/
//使用lambda表达式之后只用三行代码
new Thread(() -> { for (int i = 0; i <= 40; i++) ticket.sale(); }, "A").start();
new Thread(() -> { for (int i = 0; i <= 40; i++) ticket.sale(); }, "B").start();
new Thread(() -> { for (int i = 0; i <= 40; i++) ticket.sale(); }, "C").start();
}
}
package com.itheima.juc;
public class BakeryResource {
private int cake = 0;
public synchronized void increment() throws InterruptedException {
while (cake != 0) {
this.wait();
}
cake++;
System.out.println(Thread.currentThread().getName() + "\t 数量:" + cake);
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
while (cake == 0) {
this.wait();
}
cake--;
System.out.println(Thread.currentThread().getName() + "\t 数量:" + cake);
this.notifyAll();
}
}
package com.itheima.juc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class BakeryResource2 {
private int cake = 0;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void increment() throws InterruptedException {
lock.lock();
try {
if (cake != 0) {
condition.await();
}
cake++;
System.out.println(Thread.currentThread().getName() + "\t 数量:" + cake);
condition.signalAll();
} finally {
lock.unlock();
}
}
public void decrement() throws InterruptedException {
lock.lock();
try {
if (cake == 0) {
condition.await();
}
cake--;
System.out.println(Thread.currentThread().getName() + "\t 数量:" + cake);
condition.signalAll();
} finally {
lock.unlock();
}
}
}
package com.itheima.juc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class BakeryResource3 {
private int cake = 0;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void increment() throws InterruptedException {
lock.lock();
try {
while (cake != 0) {
condition.await();
}
cake++;
System.out.println(Thread.currentThread().getName() + "\t 数量:" + cake);
condition.signalAll();
} finally {
lock.unlock();
}
}
public void decrement() throws InterruptedException {
lock.lock();
try {
while (cake == 0) {
condition.await();
}
cake--;
System.out.println(Thread.currentThread().getName() + "\t 数量:" + cake);
condition.signalAll();
} finally {
lock.unlock();
}
}
}
package com.itheima.juc;
public class ProducerAndConsumerDemo {
public static void main(String[] args) {
BakeryResource bakery = new BakeryResource();
new Thread(()->{
try {
for(int i =1;i<=10;i++) bakery.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"蛋糕师1").start();
new Thread(()->{
try {
for(int i =1;i<=10;i++) bakery.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"蛋糕师2").start();
new Thread(()->{
try {
for(int i =1;i<=10;i++) bakery.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"消费者1").start();
new Thread(()->{
try {
for(int i =1;i<=10;i++) bakery.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"消费者2").start();
}
}
1)虚假唤醒的原因: 可能因为多个进程进入if后被wait,而if只判断一次,然后因为某个线程的notifyAll后再执行下去,导致连续多次++或—
解决:把原来的if换成while,让他们即使被唤醒了,仍要重新判断
2)使用标志位可以实现精准通知(我做完了你做,实现多线程之间的顺序调用)
class SharedResource {
private int sharedreference = 1;
private Lock lock = new ReentrantLock();
//condition1 != condition2 != condition3
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
private int num = 1;
public void share01() throws InterruptedException {
lock.lock();
try {
while (num != 1) {
condition1.await();
}
System.out.println(Thread.currentThread().getName() + "\t进入");
num = 2;
condition2.signal();
} finally {
lock.unlock();
}
}
public void share02() throws InterruptedException {
lock.lock();
try {
while (num != 2) {
condition2.await();
}
System.out.println(Thread.currentThread().getName() + "\t进入");
num = 3;
condition3.signal();
} finally {
lock.unlock();
}
}
public void share03() throws InterruptedException {
lock.lock();
try {
while (num != 3) {
condition3.await();
}
System.out.println(Thread.currentThread().getName() + "\t进入");
num = 1;
condition1.signal();
} finally {
lock.unlock();
}
}
}
public class SequenceSignal {
public static void main(String[] args) {
SharedResource sharedResource = new SharedResource();
new Thread(() -> {
try {
for (int i = 1; i <= 10; i++) sharedResource.share01();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "Thread 1").start();
new Thread(() -> {
try {
for (int i = 1; i <= 10; i++) sharedResource.share02();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "Thread 2").start();
new Thread(() -> {
try {
for (int i = 1; i <= 10; i++) sharedResource.share03();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "Thread 3").start();
}
}
为了搞清楚精准通知的原理,应该先要弄懂condition是到底是个什么东西
一个Condition实例本质上绑定到一个锁
https://segmentfault.com/a/1190000037693391
class Phone {
public synchronized void sendEmail() throws InterruptedException {
TimeUnit.SECONDS.sleep(2);
System.out.println("============sendEmail");
}
public synchronized void sendSMS() {
System.out.println("============sendSMS");
}
public void hello(){
System.out.println("======HelloWorld");
}
}
public class EightLock {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(() -> {
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
}, "A").start();
new Thread(() -> {
try {
phone.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
}, "B").start();
}
}
邮件在先,短信在后
1 标准访问,先打印短信还是邮件-----------------------------------邮件
2 停4秒在邮件方法内,先打印短信还是邮件---------------------------邮件
3 普通的hello方法,是先打邮件还是hello--------------------------hello
4 现在有两部手机,先打印短信还是邮件-----------------------------短信
5 两个静态同步方法,1部手机,先打印短信还是邮件-------------------邮件
6 两个静态同步方法,2部手机,先打印短信还是邮件-------------------邮件
7 1个静态同步方法,1个普通同步方法,1部手机,先打印短信还是邮件----短信
8 1个静态同步方法,1个普通同步方法,2部手机,先打印短信还是邮件----短信
导致原因: 前提得锁的对象是一致才能发挥锁的作用
静态方法锁对象时整个类(类锁),普通方法锁的是实例对象(对象锁)
public static void main(String[] args) {
ArrayList<Object> list = new ArrayList<>();
for (int i = 0; i <50 ; i++) {
new Thread( () -> {
list.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(list);
},String.valueOf(i)).start();
}
// java.util.ConcurrentModificationException 并发修改异常
}
并发修改导致的线程不安全问题
new Vector<>(); 不推荐使用,加了锁,影响性能
Collections.synchronizedList(new ArrayList<>());
new CopyOnWriteArrayList<>();
HashMap -> ConcurrentHashMap
HashSet -> CopyOnWriteArraySet
ArrayList -> CopyOnWriteArrayList
package com.itheima.juc;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
public class Mythread implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("******进入future方法");
TimeUnit.SECONDS.sleep(2);
return 123;
}
}
package com.itheima.juc;
import java.util.concurrent.FutureTask;
public class CallableDemo {
public static void main(String[] args) throws Exception {
Mythread mythread = new Mythread();
FutureTask<Integer> integerFutureTask = new FutureTask<>(mythread);
new Thread(integerFutureTask, String.valueOf(1)).start();
System.out.println(integerFutureTask.get());
System.out.println("计算完成");
}
}
1)get不到会阻塞主线程,所以get一般放在最后,一个futuretask 多个线程调用只会调用一次,内部有缓存。
2)futuretask.get 帮忙计算买水的例子
class MyCache {
private volatile Map<String, Object> map = new HashMap<>();
private ReentrantReadWriteLock readwriteLock=new ReentrantReadWriteLock();
public void put(String key, Object value) {
readwriteLock.writeLock().lock();
System.out.println(Thread.currentThread().getName() + "\t 正在写" + key);
//暂停一会儿线程
try {
//TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
}
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "\t 写完了" + key);
readwriteLock.writeLock().unlock();
}
public Object get(String key) {
readwriteLock.readLock().lock();
Object result = null;
System.out.println(Thread.currentThread().getName() + "\t 正在读" + key);
try {
TimeUnit.SECONDS.sleep(2);
result = map.get(key);
System.out.println(Thread.currentThread().getName() + "\t 读完了" + result);
} catch (Exception e) {
e.printStackTrace();
}finally {
readwriteLock.readLock().unlock();
}
return result;//return 前会执行finally
}
}
public class ReadwriteLock {
public static void main(String[] args) {
MyCache myCache = new MyCache();
for (int i = 1; i <= 5; i++) {
final int num = i;
new Thread(() -> {
myCache.get(String.valueOf(num));
}, String.valueOf(i)).start();
}
for (int i = 1; i <= 5; i++) {
final int num = i;
new Thread(() -> {
myCache.put(String.valueOf(num), String.valueOf(num));
}, String.valueOf(i)).start();
}
}
}
add throwing an IllegalStateException if this queue is full
offer returning true upon success and false if this queue is full. This method is generally preferable to method add(E)
put 阻塞无返回值
offer(e,time,unit) 带返回值,阻塞超时退出 (这个好诶)
1)降低资源消耗.通过重复利用已创建的线程降低, 线程创建和销毁造成的消耗.
2)提高响应速度.当任务到达时,任务可以不需要等到线程创建就能立即执行.
3)提高线程的可管理性.线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控.
1)定长线程池(FixedThreadPool)
特点:只有核心线程,线程数量固定,执行完立即回收,任务队列为链表结构的有界队列.
应用场景:控制线程最大并发数
2)定时线程池(ScheduledThreadPool)
特点:核心线程数量固定,非核心线程数量无限,执行完闲置 10ms 后回收,任务队列为延时阻塞队列
应用场景: 执行定时或周期性的任务
3)可缓存线程池(CachedThreadPool)
特点:无核心线程,非核心线程数量无限,执行完闲置 60s 后回收,任务队列为不存储元素的阻塞队列
应用场景:执行大量,耗时少的任务
4)单线程化线程池(SingleThreadExecutor)
特点:只有 1 个核心线程,无非核心线程,执行完立即回收,任务队列为链表结构的有界队列
应用场景:不适合并发但可能引起 IO 阻塞性及影响 UI 线程响应的操作,如数据库操作、文件操作等
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
// 1. 创建定长线程池对象 & 设置线程池线程数量固定为3
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
// 2. 创建好Runnable类线程对象 & 需执行的任务
Runnable task =new Runnable(){
public void run() {
System.out.println("执行任务啦");
}
};
// 3. 向线程池提交任务
fixedThreadPool.execute(task);
private static final long DEFAULT_KEEPALIVE_MILLIS = 10L;
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
public static ScheduledExecutorService newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue(), threadFactory);
}
// 1. 创建 定时线程池对象 & 设置线程池线程数量固定为5
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
// 2. 创建好Runnable类线程对象 & 需执行的任务
Runnable task =new Runnable(){
public void run() {
System.out.println("执行任务啦");
}
};
// 3. 向线程池提交任务
scheduledThreadPool.schedule(task, 1, TimeUnit.SECONDS); // 延迟1s后执行任务
scheduledThreadPool.scheduleAtFixedRate(task,10,1000,TimeUnit.MILLISECONDS);// 延迟10ms后、每隔1000ms执行任务
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}
// 1. 创建可缓存线程池对象
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
// 2. 创建好Runnable类线程对象 & 需执行的任务
Runnable task =new Runnable(){
public void run() {
System.out.println("执行任务啦");
}
};
// 3. 向线程池提交任务
cachedThreadPool.execute(task);
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}
// 1. 创建单线程化线程池
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
// 2. 创建好Runnable类线程对象 & 需执行的任务
Runnable task =new Runnable(){
public void run() {
System.out.println("执行任务啦");
}
};
// 3. 向线程池提交任务
singleThreadExecutor.execute(task);
Executors 的 4 个功能线程池虽然方便,但现在已经不建议使用了,而是建议直接通过使用 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险
1)FixedThreadPool 和 SingleThreadExecutor:主要问题是堆积的请求处理队列均采用 LinkedBlockingQueue,可能会耗费非常大的内存,甚至 OOM.
2)CachedThreadPool 和 ScheduledThreadPool:主要问题是线程数最大数是 Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至 OOM.
package com.itheima.juc;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolExecutorDemo {
private ThreadPoolExecutor threadpool;
/**
* Param:
* corePoolSize - 池中所保存的线程数,包括空闲线程。
* maximumPoolSize - 池中允许的最大线程数(采用LinkedBlockingQueue时没有作用)。
* keepAliveTime -当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间,线程池维护线程所允许的空闲时间。
* unit - keepAliveTime参数的时间单位,线程池维护线程所允许的空闲时间的单位:秒 。
* workQueue - 执行前用于保持任务的队列(缓冲队列)。此队列仅保持由execute 方法提交的 Runnable 任务。
* RejectedExecutionHandler -线程池对拒绝任务的处理策略(重试添加当前的任务,自动重复调用execute()方法)
*/
public ThreadPoolExecutorDemo(){
threadpool=new ThreadPoolExecutor(2, 10, 20, TimeUnit.SECONDS, new ArrayBlockingQueue(10),
new ThreadPoolExecutor.DiscardOldestPolicy());
}
//add task into thread pool
public void submit(final int flag){
threadpool.execute(new Runnable(){
public void run(){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(flag + " Hello");
}
});
}
/**
* close thread pool
*/
public void shutdown() {
threadpool.shutdown();
}
public static void main(String[] args) {
ThreadPoolExecutorDemo t = new ThreadPoolExecutorDemo();
for (int i = 0; i < 20; i++) {
System.out.println("time:" + i);
t.submit(i);
}
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
}
/**
* 当一个任务通过execute(Runnable)方法欲添加到线程池时:
* 1.如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
* 2.如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
* 3.如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
* 4.如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过
* handler所指定的策略来处理此任务。也就是:处理任务的优先级为:核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize
* ,如果三者都满了,使用handler处理被拒绝的任务。
*
* 5.当线程池中的线程数量大于corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。
*/
}
等待队列也已经满了,再也塞不下新任务了,同时,
线城池中的max线城也达到了,无法继续为新任务服务。
这个时候我们就需要拒绝策略机制合理的处理这个问题
Fork:把一个复杂任务进行分拆,大事化小
Join:把分拆任务的结果进行合并主要还是分治的思想,只不过这里是多个线程分治
1)ForkJoinPool:分支合并池(类似线程池)
2)ForkJoinTask:(类似FutureTask的构造方法接callable),这里要用Pool的submit方法
3)RecursiveTask:抽象类继承自ForkJoinTask,用来被继承,重写compute方法;这里作多线程的递归
package com.itheima.juc;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
class MyTask extends RecursiveTask<Integer> {
private static final Integer ADJUST_VALUE = 10;
private int begin;
private int end;
private int result;
public MyTask(int begin, int end) {
this.begin = begin;
this.end = end;
}
@Override
protected Integer compute() {
if ((end - begin) <= ADJUST_VALUE) { //10以内太小了就没必要拆分了
for (int i = begin; i <= end; i++) {
result = result + i;
}
} else {
int middle = (begin + end) / 2;
MyTask task01 = new MyTask(begin, middle);
MyTask task02 = new MyTask(middle + 1, end);
task01.fork();
task02.fork();
result = task01.join() + task02.join();
}
return result;
}
}
public class ForkJoinDemo {
public static void main(String[] args) throws Exception {
MyTask myTask = new MyTask(0, 100); //继承了RecursiveTask,实现0~100的相加
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask<Integer> forkJoinTask = forkJoinPool.submit(myTask); //分支合并池提交Task
System.out.println(forkJoinTask.get());
forkJoinPool.shutdown();
}
}
public static void main(String[] args) throws Exception {
CompletableFuture<Void> completableFuture1 = CompletableFuture.runAsync(()->{ //Void是void包装类
System.out.println(Thread.currentThread().getName()+"没有返回值,update mysql ok");
});
completableFuture1.get();
//------------------------------------------------------------------------------------------
CompletableFuture<Integer> completableFuture2 = CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread().getName()+"有返回值,insert mysql ok");
int i = 1/0;
return 1024;
});
System.out.println(completableFuture2.whenComplete((t, u) -> { // BiConsumer super T, ? super Throwable> action 要有两个输入参数,且没有返回值
System.out.println("t=" + t); //t:正确完成
System.out.println("u=" + u); //u:异常
}).exceptionally(f -> { //如果异常,则走这里
System.out.println("exception:" + f.getMessage());
return 404;
}).get());
}