我的多线程笔记

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改变策略变为可升级的锁,分四个级别。

  1. 偏向锁,默认没有其他线程争夺资源,记录第一个线程的markword,当有其他线程来到时,markword不匹配,升级为自旋锁。当markword匹配的线程处于未活动的状态时,降级为无锁。偏向锁是唯一能降级的级别。
  2. 无锁,还没有任何的线程来获取资源,等待来到的第一个线程获取资源,当有线程来获取资源,此时升级为偏向锁。
  3. 轻量级锁,也叫自旋锁,当多个线程来争夺资源时,采用CAS自旋得方式保证线程安全,但由于不断地循环请求会消耗性能,所以有自旋得次数限制,当超过自旋次数限制时,请求不到资源的线程被挂起,锁升级为重量级锁。
  4. 重量级锁,当一个锁为重量级锁时,其他来到的线程直接被挂起阻塞。重量级锁为操作系统控制的,也就是说需要从用户态切换到内核态,而频繁的切换状态,会消耗大量系统资源与时间。
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...");
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值