1. 线程的状态
线程的状态个数众说纷纭,5,6,7都有,但实际上都是一样的,拆分与合并而已。
new 初始状态,刚new出来。
runnable 可运行状态,分ready和running,正在运行和准备运行,虽然是可运行,但不一定必须要运行,所以分成两个状态。
timedWaiting 有sleep,wait(time),join(time)之类的操作时该线程的状态,会随着时间的流逝而恢复为runnable。
waiting 调用waiting,join时会发生的状态,无限等待制,直到被notify或notifyAll唤醒。
blocked 阻塞了,synchronize的块和方法,lock,均会导致该状态,获取锁对象以变为runnable。
teminated 终止态。run代码执行完毕就变为终止态,再start要报错。
2. synchronized是可重入锁
对于多个被synchronized控制的方法,是可以互相调用的,如果不是可重入锁,将会在synchronized方法调用另一个synchronized方法时发生死锁。
3. synchronized异常跟锁
当synchronized中的代码发生异常时,锁将被释放,此时如果其他线程进入,可能会拿到此时发生异常前线程的中间数据,造成脏数据。应该在可能会出问题的地方使用catch捕获异常。
4. synchronized升级
synchronized在1.5以前是直接拿操作系统的锁,属重量级,很慢。而在1.5以后,synchronized改变策略变为可升级的锁,分四个级别。
- 偏向锁,默认没有其他线程争夺资源,记录第一个线程的markword,当有其他线程来到时,markword不匹配,升级为自旋锁。当markword匹配的线程处于未活动的状态时,降级为无锁。偏向锁是唯一能降级的级别。
- 无锁,还没有任何的线程来获取资源,等待来到的第一个线程获取资源,当有线程来获取资源,此时升级为偏向锁。
- 轻量级锁,也叫自旋锁,当多个线程来争夺资源时,采用CAS自旋得方式保证线程安全,但由于不断地循环请求会消耗性能,所以有自旋得次数限制,当超过自旋次数限制时,请求不到资源的线程被挂起,锁升级为重量级锁。
- 重量级锁,当一个锁为重量级锁时,其他来到的线程直接被挂起阻塞。重量级锁为操作系统控制的,也就是说需要从用户态切换到内核态,而频繁的切换状态,会消耗大量系统资源与时间。
5. ReentrantLock
ReentrantLock是JDK带的一种线程工具类,可以有trylock(time)方法进行有时限的加锁,time时间到后对象的锁仍然在其他线程时,返回false,并解锁。
ReentrantLock也带有lockInterruputible方法,在线程t.interrupt时可以把锁也打断,平常的lock方法是打断不了的。
ReentrantLock(true),构造方法中传入true会触发公平锁机制,在后来的线程到来时,不会马上去争夺资源,而是等待前面排队的线程先拿到锁后再争取锁。
6. CountDownLatch
CountDownLatch是类似于门栓一样的东西,当countDown方法被调用到一定次数时,调用await的线程才会苏醒。同一个CountDownLatch对象用于多个线程,想要拴住的线程调用await,其他解锁线程调用countDown,多用于强一致性的计数。
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class CountDownTest {
class Work implements Runnable{
private String name;
private CountDownLatch latch;
private CountDownLatch latch2;
public Work(String name,CountDownLatch latch,CountDownLatch latch2){
this.name=name;
this.latch=latch;
this.latch2=latch2;
}
@Override
public void run() {
System.out.println("员工"+this.name+" 正在搬砖。。。");
try {
//System.out.println(latch2.getCount());
latch2.countDown();
latch2.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
TimeUnit.SECONDS.sleep(new Random().nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("员工"+this.name+"搬完了。。。");
latch.countDown();
}
}
class Leader implements Runnable{
private String name;
private CountDownLatch latch;
public Leader(String name,CountDownLatch latch){
this.name=name;
this.latch=latch;
}
@Override
public void run() {
System.out.println("老板"+this.name+" 正在等工具人们搬砖。。。");
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("老板"+this.name+"开始检查。。!!!");
}
}
public static void main(String[] args) {
CountDownLatch latch =new CountDownLatch(5);
CountDownLatch latch2 =new CountDownLatch(5);
CountDownTest testtt = new CountDownTest();
Work 陈某 = testtt.new Work("陈某", latch,latch2);
Work 商人陈某 = testtt.new Work("商人陈某", latch,latch2);
Work 叛逆之人 = testtt.new Work("叛逆之人", latch,latch2);
Work 唯我之人 = testtt.new Work("唯我之人", latch,latch2);
Work 沉沦羔羊 = testtt.new Work("沉沦羔羊", latch,latch2);
Leader 带资本家 = testtt.new Leader("带资本家", latch);
new Thread(陈某).start();
new Thread(商人陈某).start();
new Thread(带资本家).start();
new Thread(叛逆之人).start();
new Thread(唯我之人).start();
new Thread(沉沦羔羊).start();
}
}
7. CyclicBarrier
CyclicBarrier和CountDownLatch都是有计数功能的,初始化时
CyclicBarrier cyclicBarrier = new CyclicBarrier(5,()->{
System.out.println("快开车啊我日你沟子!");
});
可以指定计数的数和达到计数时触发的方法。方法可以省略不写。
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class Test2 {
public static void main(String[] args) throws InterruptedException {
CyclicBarrier cyclicBarrier = new CyclicBarrier(5,()->{
System.out.println("快开车啊我日你沟子!");
});
for (int i = 0; i < 10; i++) {
System.out.println("this is "+i);
new Thread(()->{
try {
cyclicBarrier.await();
System.out.println("GO!");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
Thread.sleep(1000);
}
}
}
也是需要await,当await的线程达到指定数量时,才会苏醒所有await线程,并且触发任务方法。
8. Phaser
Phaser和CyclicBarrier类似,都是栅栏,到一定线程数后一起苏醒,但是Phaser是多阶段的CyclicBarrier,可以有多个等待阶段。
onAdvance方法是关键,继承Phaser类后重写该方法,该方法会在线程苏醒时自动调用,其中参数phaser是阶段标记,第一次苏醒事件时为0,往后每次加1,此为固定写法,欲结束事件,return true即可。
arriveAndAwaitAdvance方法用于实现栅栏效果,当等待线程数达到初始化时定义的数量时,触发苏醒。
arriveAndDeregister注销线程,不再等待,但计数会+1;
package myMultithreading.myPhaser;
import java.util.Random;
import java.util.concurrent.Phaser;
public class MyPhaser {
public static void main(String[] args) {
TestPhaser testPhaser = new TestPhaser(7);
new MarryHuman("德川家康", testPhaser).start();
new MarryHuman("阿克蒙德", testPhaser).start();
new MarryHuman("卫石碏", testPhaser).start();
new MarryHuman("腓特烈", testPhaser).start();
new MarryHuman("自爆磁怪", testPhaser).start();
new MarryHuman("Dervous", testPhaser).start();
new MarryHuman("Hashimoto Kanna", testPhaser).start();
}
}
class MarryHuman extends Thread {
private String name;
private Phaser phaser;
public MarryHuman(String name, Phaser phaser) {
this.name = name;
this.phaser = phaser;
}
public void eat() {
this.MySleep();
System.out.println(name + " 开始吃席");
phaser.arriveAndAwaitAdvance();
}
public void play() {
this.MySleep();
System.out.println(name + " 开始玩");
phaser.arriveAndAwaitAdvance();
}
public void chet() {
this.MySleep();
System.out.println(name + " 开始加入聊天");
phaser.arriveAndAwaitAdvance();
}
public void dongFang() {
if ("Dervous".equals(name) || "Hashimoto Kanna".equals(name)) {
this.MySleep();
System.out.println(name + " 准备开始董芳");
phaser.arriveAndAwaitAdvance();
} else {
phaser.arriveAndDeregister();
}
}
public static void MySleep() {
try {
Thread.sleep(new Random().nextInt(5) * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void run() {
this.eat();
this.play();
this.chet();
this.dongFang();
}
}
class TestPhaser extends Phaser {
public TestPhaser(int times) {
super(times);
}
@Override
protected boolean onAdvance(int phase, int registeredParties) {
switch (phase) {
case 0:
MarryHuman.MySleep();
System.out.println("在座的各位都恰完毕了");
System.out.println();
return false;
case 1:
MarryHuman.MySleep();
System.out.println("在座的各位都玩完了");
System.out.println();
return false;
case 2:
MarryHuman.MySleep();
System.out.println("在座的各位都恰聊的差不多了");
System.out.println();
return false;
case 3:
MarryHuman.MySleep();
System.out.println("bancibanci了");
System.out.println();
//事件尽了 returnTrue
return true;
default:
return true;
}
}
}
9. ReadWriteLock
对于多线程安全问题,如果串行势必导致速度下降,所以尽可能的并行是最好的方案,readwritelock提供了读锁和写锁,对于加读锁的线程,锁将不会控制串行,可以同时执行代码,而当持有写锁的线程来到时,其他线程将无法读取和写入。ReadWriteLock提高了读的速度。如果是普通的ReentrantLock,读与读之间也会发生锁的竞争。
package myMultithreading.readWriteLock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class MyReadWriteLock{
static ReentrantLock reentrantLock=new ReentrantLock();
static ReadWriteLock readWriteLock=new ReentrantReadWriteLock();
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(()->{
try {
//read(reentrantLock);
read(readWriteLock.readLock());
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
if (i%5==0){
new Thread(()->{
try {
//read(reentrantLock);
read(readWriteLock.writeLock());
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
}
public static void read(Lock lock) throws InterruptedException {
lock.lock();
Thread.sleep(1000);
System.out.println("read");
lock.unlock();
}
}
10. Semaphore
Semaphore 是类似于限流器的东西。一共有多少资源,获取完毕后,其他线程再来获取将被阻塞,直到有线程释放资源。类似于线程池。
package myMultithreading.mySemaphore;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
public class MySemaphore {
static Semaphore semaphore = new Semaphore(4);
static AtomicInteger count=new AtomicInteger(0);
public static void main(String[] args) {
for (int i = 0; i < 22; i++) {
new Thread(()->{
try {
semaphore.acquire();
System.out.println("this is "+count.addAndGet(1));
Thread.sleep(2000);
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
}
acquire为获取资源方法。初始化时定义资源总数为4。release为释放资源。
一旦涉及多个字符或字符串交替打印的题,基本上马上想到Semaphore,Semaphore可以保证顺序和第一次的打印字符
package test;
import java.util.concurrent.Semaphore;
public class AABBCCC3_Semaphore {
//初始化一个,保证A先打印
static Semaphore a = new Semaphore(1);
static Semaphore b = new Semaphore(0);
static Semaphore c = new Semaphore(0);
public static void main(String[] args) {
new Thread(() -> {
for (; ; ) {
try {
a.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print("AA");
b.release();
}
}).start();
new Thread(() -> {
for (; ; ) {
try {
b.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print("BB");
c.release();
}
}).start();
new Thread(() -> {
for (; ; ) {
try {
c.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print("CCC");
a.release();
}
}).start();
}
}
11. Exchanger
用于交换两个线程的私有变量,当线程一共有两个时没
问题,当线程数大于2时感觉难以控制。感觉很鸡肋实际中并无多大暖用。
package myMultithreading.myExchanger;
import java.util.concurrent.Exchanger;
public class MyExchanger {
static Exchanger<String> exchanger=new Exchanger();
public static void main(String[] args) {
new Thread(()->{
String wife="桥本环奈";
System.out.println(Thread.currentThread().getName()+":我要和"+wife+"结婚啦。。。");
try {
wife = exchanger.exchange(wife);
System.out.println(Thread.currentThread().getName()+":怎么变成了"+wife+"。。。");
} catch (InterruptedException e) {
e.printStackTrace();
}
},"Tokugawa").start();
new Thread(()->{
String wife="茅野爱衣";
System.out.println(Thread.currentThread().getName()+":我也要和"+wife+"结婚啦。。。");
try {
wife = exchanger.exchange(wife);
System.out.println(Thread.currentThread().getName()+":怎么却变成了"+wife+"。。。");
} catch (InterruptedException e) {
e.printStackTrace();
}
},"Dervous").start();
}
}
12. LockSupport
LockSupport实现的功能和wait,notify方法类似,但是更加便捷一些,可以unpark(t) 去指定线程对象,使用lock方法去使线程处于wait状态,并且可以在线程start后先unpark以防止后来的park方法执行。
package myMultithreading.myLockSupport;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
public class MyLockSupport {
public static void main(String[] args) {
Thread t = new Thread(()->{
for (int i = 0; i < 10; i++) {
if (i==5){
LockSupport.park();
}
System.out.println("搞");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
LockSupport.unpark(t);
}
}
如果不unpark,将只输出5个搞并永久wait。
13. Volatile
volatile的作用有两个,变量在多线程的情况下可见,和防止指令重排序。
package myMultithreading.myVolatile;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class MyVolatile {
volatile List list = new ArrayList<String>();
public static void main(String[] args) {
MyVolatile myVolatile = new MyVolatile();
new Thread(()->{
for (int i = 0; i < 10; i++) {
myVolatile.list.add(new Object());
System.out.println(i);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(()->{
for (;;){
if (myVolatile.list.size()==5){
break;
}
}
System.out.println("break");
}).start();
}
}
代码中,有两个线程,都用到了全局的变量myVolatile,对于第二个Thread,按理说,当myVolatile的list大小被第一个Thread add到5个后,将break并输出break,上述测试代码结果是对的。但是实际上如果list不加volatile,Thread2将永远不会输出break也永远不会停止,因为HotSpot JVM的机制问题,线程之间是不可见的。
猜想:在线程start的一瞬间,各个线程的变量都会私有化,也就是从主线程中复制一份myVolatile到私有栈上,当改变时,其他线程不知道,最后完毕时,提交回主线程,一旦有了volatile或者sout操作,就会在线程间不断地去其他线程获取myVolatile的实时属性。
如果测试代码中没有TimeUnit.SECONDS.sleep(1);睡一秒,Thread2将很难break。因为Thread1执行得太快,瞬间size就超过5了,Thread在循环getsize时第一次get也许是2,第二次getsize直接就是10了。
14. wait notify
wait和notify是配套使用的,一个等待一个唤醒,两个方法都是存在于Objcet类中,因为任何类都要可以成为锁,所以要放在Objcet中,o.notify方法会唤醒另一个调用o.wait的线程,前提是都用了同一个o才行。
17. ThreadLocal
ThreadLocal 适用于存放线程的私有变量,一个ThreadLocal对象就是一个变量。因为Thread类自带一个map,map的key为ThreadLocal对象,value为set的值。
import java.io.*;
class test
{
public static void main (String[] args) throws java.lang.Exception
{
ThreadLocal t1 = new ThreadLocal();
ThreadLocal t2 = new ThreadLocal();
ThreadLocal t3 = new ThreadLocal();
new Thread(()->{
t1.set("ttt");
t2.set("yyy");
System.out.println(t1.get());
}).start();
new Thread(()->{
t1.set("tt2t");
t2.set("yy2y");
System.out.println(t1.get());
}).start();
System.out.println(t1.get());
}
}
set方法源码为
public void set(T value) {
//(1)获取当前线程(调用者线程)
Thread t = Thread.currentThread();
//(2)以当前线程作为key值,去查找对应的线程变量,找到对应的map
ThreadLocalMap map = getMap(t);
//(3)如果map不为null,就直接添加本地变量,key为当前定义的ThreadLocal变量的this引用,值为添加的本地变量值
if (map != null)
map.set(this, value);
//(4)如果map为null,说明首次添加,需要首先创建出对应的map
else
createMap(t, value);
}
18.多线程高并发尽量选择Queue而不是List
List大多线程是不安全的,就算使用Vector,很多时候还是会出问题,此时应考虑到使用Queue,利用poll方法来得到数据,保证线程安全,实际上也是CAS实现的,但如果有输出,顺序会打乱。
19.面试题AABBCCC问题
package test;
import java.util.concurrent.locks.LockSupport;
public class AABBCCC {
static Thread t1=null;
static Thread t2=null;
static Thread t3=null;
public static void main(String[] args) throws InterruptedException {
t1 = new Thread(()->{
while (true){
System.out.print("AA");
LockSupport.unpark(t2);
LockSupport.park();
}
});
t2 = new Thread(()->{
while (true) {
LockSupport.park();
System.out.print("BB");
LockSupport.unpark(t3);
}
});
t3 = new Thread(()->{
while (true) {
LockSupport.park();
System.out.print("CCC");
LockSupport.unpark(t1);
}
});
t1.start();
t2.start();
t3.start();
}
}
自旋解决AABBCCC问题,加volatile是为了节约时间,因为线程可见
package test;
public class AABBCCC2 {
private static volatile String s = "A";
public static void main(String[] args) {
new Thread(() -> {
for (; ; ) {
for (; !"A".equals(s); ) {
}
if ("A".equals(s)) {
System.out.print("AA");
s = "B";
}
}
}).start();
new Thread(() -> {
for (; ; ) {
for (; !"B".equals(s); ) {
}
if ("B".equals(s)) {
System.out.print("BB");
s = "C";
}
}
}).start();
new Thread(() -> {
for (; ; ) {
for (; !"C".equals(s); ) {
}
System.out.print("CCC");
s = "A";
}
}).start();
}
}
20. A1B2C3D4E5F6G7面试题
package test;
public class A1B2C3D4E5F6G7 {
private static char[] cc = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
private static int[] ii = {1, 2, 3, 4, 5, 6, 7};
public static void main(String[] args) {
new Thread(() -> {
synchronized (A1B2C3D4E5F6G7.class) {
for (char c : cc) {
System.out.print(c);
try {
A1B2C3D4E5F6G7.class.notify();
A1B2C3D4E5F6G7.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
A1B2C3D4E5F6G7.class.notify();
}
}).start();
new Thread(() -> {
synchronized (A1B2C3D4E5F6G7.class) {
for (int i : ii) {
System.out.print(i);
try {
A1B2C3D4E5F6G7.class.notify();
A1B2C3D4E5F6G7.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
A1B2C3D4E5F6G7.class.notify();
}
}).start();
}
}
21. CompletableFuture
CompletableFuture的allof方法和CountDownLatch类似,都是主线程等待多个子线程结束后才能运行,但是CF的目的更加明确,是针对于方法的执行的类,有包装操作。
CompletableFuture也有anyOf方法,即多个线程只要有一个完成,主线程就能往下执行。
其实CF是在一些多线程特殊场景下的管理工具,可以实现各种奇奇怪怪的业务逻辑。
package test;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class CompletableFutureTest {
public static void main(String[] args) {
CompletableFuture<String> first = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "AAA";
});
CompletableFuture<String> second = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "BBB";
});
CompletableFuture<String> third = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "CCC";
});
System.out.println("start join");
CompletableFuture.allOf(first,second,third).join();
//CompletableFuture.anyOf(first,second,third).join();
System.out.println("end join");
try {
System.out.println(first.get());
System.out.println(second.get());
System.out.println(third.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
22.线程池7参数(ThreadPoolExecutor)
1. corePoolSize 核心线程数量,不论是否空闲,线程池中一定会存活的线程数量。
2. maximumPoolSize 最大线程数量,当核心线程都不处于空闲状态,且等待队列达到最大值时,启动新线程运行当前任务,总线程数量不能超过此参数。
3. keepAliveTime 最大空闲存活时间,当非核心线程在空闲一定时间后,将被销毁。
4. unit 存活时间单位,与keepAliveTime参数向照应,keepAliveTime填数字,unit填单位,单位为TimeUnit类的枚举类型。
5. workQueue 工作队列,即任务队列,用于存放新来的且无法立即被执行的任务。JDK自带4种。
①ArrayBlockingQueue
基于数组的有界阻塞队列,按FIFO排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。当线程池中线程数量达到corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。如果队列已经是满的,则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略。
②LinkedBlockingQuene
基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而不会去创建新线程直到maxPoolSize,因此使用该工作队列时,参数maxPoolSize其实是不起作用的。
③SynchronousQuene
一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。
④PriorityBlockingQueue
具有优先级的无界阻塞队列,优先级通过参数Comparator实现。
引用自:https://blog.csdn.net/ye17186/article/details/89467919
6. threadFactory 线程工厂,JDK提供一个DefaultThreadFactory,该工厂提供的线程必不是守护线程,且线程自带group,名字等,优先级默认为正常(NORM_PRIORITY)。
7. handler 处理器,可叫做任务队列和线程都满负荷时的处理方案,即拒绝策略,自带的有4种,也可以自己定义。
1. AbortPolicy 拒绝新来的任务,并抛出异常。
2. DiscardPolicy 拒绝新来的任务,并不做任何事。
3. DiscardOldestPolicy 接受新来的任务,并将最old的任务抛弃。
4. CallerRunsPolicy 拒绝新来的任务,并改由调用新任务的线程自己去执行新来的任务。
拒绝策略测试
import java.io.IOException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Test {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(2,
4,
60L,
TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(4),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy()
);
for (int i = 0; i < 8; i++) {
executor.execute(new Task(i));
}
System.out.println(executor.getQueue().toString());
executor.execute(new Task(100));
System.out.println(executor.getQueue().toString());
}
static class Task implements Runnable{
private int num;
public Task(int num){
this.num=num;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" "+num);
try {
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public String toString() {
return "Task{" +
"num=" + num +
'}';
}
}
}
23.SingleThreadExecutor
看似毫无暖用的SingleThreadExecutor,实际上还是没什么暖用。
因为池子里就只有一个线程。
其源码实现如下。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
就是一个包装了的ThreadPoolExecutor,最大线程数为1,比起直接new一个Thread的优势在于,拥有自己的队列,能够有序执行任务,减少启动销毁线程的消耗,仅此而已。而且还有弊端,毕竟LinkedBlockingQueue,最大任务上限为Integer的最大值,容易OOM。
24. 自定义拒绝策略类
package test;
import java.io.IOException;
import java.util.concurrent.*;
public class MyRejectHandler {
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 10L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1),
Executors.defaultThreadFactory(), new MyHandler());
threadPoolExecutor.execute(()->{
try {
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
});
threadPoolExecutor.execute(()->{
try {
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
});
threadPoolExecutor.execute(()->{
try {
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
});
}
static class MyHandler implements RejectedExecutionHandler{
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println("My name is van,I am an artist,I am hired for people to fulfill their fantasies,their deep dark fantasies...");
}
}
}