1.基础知识
并发编程的目的是为了让程序变得更快,但是,并不是启动更多的线程就能让程序最大限度的并发执行。在进行并发编程中,如果希望通过多线程让程序运行的更快,会面临非常多的挑战。比如上下文切换、死锁问题,以及受限于硬件和软件的资源问题。
多线程最主要的作用是压榨cpu.
1.1死锁
死锁就是两个进程(线程)同时占用两个资源,但又在彼此等待对方释放锁,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁
代码演示:
java
public class DeadLock {
private static String Resource_A = "A";
private static String Resource_B = "B";
public static void main(String[] args) {
new DeadLock().deadLock();
}
private void deadLock(){
Thread t1 = new Thread(new Runnable() {
public void run() {
synchronized (Resource_A){
try {
Thread.sleep(3000);//睡眠3s的意思 让B线程启动起来 去使用Resource_B资源
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (Resource_B){
System.out.println("1");
}
}
}
});
Thread t2 = new Thread(new Runnable() {
public void run() {
synchronized (Resource_B){
synchronized (Resource_A){
System.out.println("2");
}
}
}
});
try {
t1.start();
Thread.sleep(2000);//睡眠2s的意思 让线程1先启动
t2.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
1.2并行和并发
并发:一个处理器同一时间段处理多个任务,强调一个时间段。(不是真正的同时,而是看起来同时,逻辑上的同时发生)
并行:多个处理器同一时刻处理多个任务。真正的同时。(物理意义上的同时发生)

2.线程的创建
2.1继承Thread类
java
public class MyThread extends Thread {
@Override
public void run() {
for(int i=0; i<100; i++) {
System.out.println(i);
}
}
}
public class MyThreadDemo {
public static void main(String[] args) {
MyThread my1 = new MyThread();
MyThread my2 = new MyThread();
my1.start();
my2.start();
}
}
run()是用来封装被线程执行的代码。start()启动线程;然后由JVM调用此线程的run()方法
2.2 实现Runnable接口
代码实现1:
java
public class MyRunnable implements Runnable {
@Override
public void run() {
for(int i=0; i<100; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
public class MyRunnableDemo {
public static void main(String[] args) {
//创建MyRunnable类的对象
MyRunnable my = new MyRunnable();
//创建Thread类的对象,把MyRunnable对象作为构造方法的参数
Thread t1 = new Thread(my,"坦克");
Thread t2 = new Thread(my,"飞机");
//启动线程
t1.start();
t2.start();
}
}
匿名内部类实现:
java
new Thread(new Runnable() {
@Override
public void run() {
// 调用资源方法,完成业务逻辑
}
}, "your thread name").start();
2.3实现Callable接口
java
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
for (int i = 0; i < 100; i++) {
System.out.println("跟女孩表白" + i);
}
//返回值就表示线程运行完毕之后的结果
return "答应";
}
}
public class Demo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//线程开启之后需要执行里面的call方法
MyCallable mc = new MyCallable();
//Thread t1 = new Thread(mc);
//可以获取线程执行完毕之后的结果.也可以作为参数传递给Thread对象
FutureTask<String> ft = new FutureTask<>(mc);
//创建线程对象
Thread t1 = new Thread(ft);
String s = ft.get();
//开启线程
t1.start();
//String s = ft.get();
System.out.println(s);
}
}

2.4线程睡眠

java
public class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "---" + i);
}
}
}
public class Demo {
public static void main(String[] args) throws InterruptedException {
/*System.out.println("睡觉前");
Thread.sleep(3000);
System.out.println("睡醒了");*/
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(mr);
Thread t2 = new Thread(mr);
t1.start();
t2.start();
}
}
3.线程周期


java
public class Thread{
public enum State{
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIME_WATING,
TERMINATED
}
}
4.线程同步
4.1Synchronized
synchronized 同步块是 Java 提供的一种原子性内置锁,Java 中的每个对象都可以把它当作一个同步锁来使用,这些 Java 内置的使用者看不到的锁被称为内置锁,也叫作监视器锁。
每个对象Object都内置了一个监视器锁,当某个线程获得了这个监视器锁后,其它线程在想获得这个对象的监视器锁,就必须要排队。也就是说:synchronzied关键字的底层,相当于一个排它锁。
java
public class SellTicket implements Runnable {
private int tickets = 100;
private Object obj = new Object();
@Override
public void run() {
while (true) {
synchronized (obj) { // 对可能有安全问题的代码加锁,多个线程必须使用同一把锁
//t1进来后,就会把这段代码给锁起来
if (tickets > 0) {
try {
Thread.sleep(100);
//t1休息100毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
//窗口1正在出售第100张票
System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
tickets--; //tickets = 99;
}
}
//t1出来了,这段代码的锁就被释放了
}
}
}
public class SellTicketDemo {
public static void main(String[] args) {
SellTicket st = new SellTicket();
Thread t1 = new Thread(st, "窗口1");
Thread t2 = new Thread(st, "窗口2");
Thread t3 = new Thread(st, "窗口3");
t1.start();
t2.start();
t3.start();
}
}
4.2Lock
在功能上等同于synchronized.
能用synchronized都能用Lock,能用Lockd的不一定都能用synchronized.
不管是synchronized还是Lock,这俩都属于本地锁。
本地锁:锁对象都是jvm虚拟机中的对象。【只要一个对象自己能new 出现,这个对象都叫本地对象】
分布式锁:锁对象不是jvm虚拟机中的对象(属于自己组件中的对象)这个对象从来没new 过,组件给你new 的 你直接使用。
Lock是一个接口,这里主要有三个实现:ReentrantLock、ReentrantReadWriteLock.ReadLock、ReentrantReadWriteLock.WriteLock
4.2.1ReentrantLock
java
class Tickets_A {
private int number = 10;
//创建lock实现类
final Lock lock = new ReentrantLock();
public void saleTickets() {
System.out.println(Thread.currentThread().getName() +"线程进来了,现有票" + number + "张:并且开始卖票,");
lock.lock();
try {
number--;
System.out.println(Thread.currentThread().getName() + "线程抢到了锁,并卖了一张票,还剩下" + number + "张票");
Thread.sleep(1000);
if (number <= 0) {
System.out.println("票已售罄!");
return;
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public class MultipleSaleTicketsByLock {
public static void main(String[] args) {
//资源对象
Tickets_A tickets_a = new Tickets_A();
for (int i = 1; i <= 2; i++) {
new Thread(() -> {
for (int j = 0; j <5 ; j++) {
tickets_a.saleTickets();
}
}, i + "").start();
}
}
}
可重入锁
可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动拥有该锁(不用抢了)。Java中ReentrantLock和synchronized都是可重入锁,可重入锁的一个优点是可一定程度避免死锁
java
class A{
public synchronized void aa(){
......
this.bb();
......
}
public synchronized void bb(){
......
}
}
A a = new A();
a.aa();
A类中有两个普通同步方法,都需要对象a的锁。如果是不可重入锁的话,aa方法首先获取到锁,aa方法在执行的过程中需要调用bb方法,此时锁被aa方法占有,bb方法无法获取到锁,这样就会导致bb方法无法执行,aa方法也无法执行完,出现了死锁情况。可重入锁可避免这种死锁的发生。
ReentrantLock还可以实现公平锁。所谓公平锁,也就是在锁上等待时间最长的线程将获得锁的使用权。通俗的理解就是谁排队时间最长谁先执行获取锁。
4.2.2ReentrantLock和synchronized区别
独占锁:在任意一时刻,有且只有一个线程能够获取锁
- lock是一个接口,而synchronized是java的一个关键字
- Lock位于juc.locks包中,synchronizedjdk内置的锁。
- synchronized是独占锁,加锁和解锁的过程自动进行,易于操作,但不够灵活。ReentrantLock也是独占锁,加锁和解锁的过程需要手动进行,不易操作,但非常灵活。
- synchronized 可重入,因为加锁和解锁自动进行,不必担心最后是否释放锁;ReentrantLock也可重入,但加锁和解锁需要手动进行,且次数需一样,否则其他线程无法获得锁。可以避免死锁。
- synchronized在发生异常时会自动释放占有的锁,因此不会出现死锁,而ReentrantLock发生异常时,不会主动释放占有的锁,必须手动来释放锁,可能引起死锁的发生。
- Synchronized不可响应中断:第一个线程获得某把锁后,第二个线程也想要获得该锁,则它必须处于阻塞或等待状态。如果第一个线程不释放锁,那第二个线程就会一直阻塞或等待,不可被中断。
5.线程通信
案例生产者消费者
所谓生产者消费者问题,实际上主要是包含了两类线程:
一类是生产者线程用于生产数据
一类是消费者线程用于消费数据
为了解耦生产者和消费者的关系,通常会采用共享的数据区域,就像是一个仓库
生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者的行为
消费者只需要从共享数据区中去获取数据,并不需要关心生产者的行为
java
public class Desk {
//定义一个标记
//true 就表示桌子上有汉堡包的,此时允许吃货执行
//false 就表示桌子上没有汉堡包的,此时允许厨师执行
//public static boolean flag = false;
private boolean flag;
//汉堡包的总数量
//public static int count = 10;
//以后我们在使用这种必须有默认值的变量
// private int count = 10;
private int count;
//锁对象
//public static final Object lock = new Object();
private final Object lock = new Object();
public Desk() {
this(false,10); // 在空参内部调用带参,对成员变量进行赋值,之后就可以直接使用成员变量了
}
public Desk(boolean flag, int count) {
this.flag = flag;
this.count = count;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public Object getLock() {
return lock;
}
@Override
public String toString() {
return "Desk{" +
"flag=" + flag +
", count=" + count +
", lock=" + lock +
'}';
}
}
public class Cooker extends Thread {
private Desk desk;
public Cooker(Desk desk) {
this.desk = desk;
}
// 生产者步骤:
// 1,判断桌子上是否有汉堡包
// 如果有就等待,如果没有才生产。
// 2,把汉堡包放在桌子上。
// 3,叫醒等待的消费者开吃。
@Override
public void run() {
while(true){
synchronized (desk.getLock()){
if(desk.getCount() == 0){
break;
}else{
//System.out.println("验证一下是否执行了");
if(!desk.isFlag()){
//生产
System.out.println("厨师正在生产汉堡包");
desk.setFlag(true);
desk.getLock().notifyAll();
}else{
try {
desk.getLock().wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}
public class Foodie extends Thread {
private Desk desk;
public Foodie(Desk desk) {
this.desk = desk;
}
@Override
public void run() {
// 1,判断桌子上是否有汉堡包。
// 2,如果没有就等待。
// 3,如果有就开吃
// 4,吃完之后,桌子上的汉堡包就没有了
// 叫醒等待的生产者继续生产
// 汉堡包的总数量减一
//套路:
//1. while(true)死循环
//2. synchronized 锁,锁对象要唯一
//3. 判断,共享数据是否结束. 结束
//4. 判断,共享数据是否结束. 没有结束
while(true){
synchronized (desk.getLock()){
if(desk.getCount() == 0){
break;
}else{
//System.out.println("验证一下是否执行了");
if(desk.isFlag()){
//有
System.out.println("吃货在吃汉堡包");
desk.setFlag(false);
desk.getLock().notifyAll();
desk.setCount(desk.getCount() - 1);
}else{
//没有就等待
//使用什么对象当做锁,那么就必须用这个对象去调用等待和唤醒的方法.
try {
desk.getLock().wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}
public class Demo {
public static void main(String[] args) {
/*消费者步骤:
1,判断桌子上是否有汉堡包。
2,如果没有就等待。
3,如果有就开吃
4,吃完之后,桌子上的汉堡包就没有了
叫醒等待的生产者继续生产
汉堡包的总数量减一*/
/*生产者步骤:
1,判断桌子上是否有汉堡包
如果有就等待,如果没有才生产。
2,把汉堡包放在桌子上。
3,叫醒等待的消费者开吃。*/
Desk desk = new Desk();
Foodie f = new Foodie(desk);
Cooker c = new Cooker(desk);
f.start();
c.start();
}
}
6.并发工具类
6.1 List中的线程安全
这段是线程不安全的
java
public class NoSafeList {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i <1000; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0, 8));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
可以使用Vector和SynchronizedList保证线程安全
java
public class NoSafeList {
public static void main(String[] args) {
// ArrayList<String> list = new ArrayList<>();
List<Object> list = Collections.synchronizedList(new ArrayList<>());
// Vector list = new Vector<Integer>();
for (int i = 0; i < 20; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0,2));
System.out.println(list);
}).start();
}
}
}
6.2 ConcurrentHashMap
在集合类中HashMap是比较常用的集合对象,但是HashMap是线程不安全的(多线程环境下可能会存在问题)。为了保证数据的安全性我们可以使用Hashtable,但是Hashtable的效率低下。
1 ,HashMap是线程不安全的。多线程环境下会有数据安全问题
2 ,Hashtable是线程安全的,但是会将整张表锁起来,效率低下
3,ConcurrentHashMap也是线程安全的,效率较高。
java
import java.util.Hashtable;
import java.util.concurrent.ConcurrentHashMap;
public class MyConcurrentHashMapDemo {
public static void main(String[] args) throws InterruptedException {
ConcurrentHashMap<String, String> hm = new ConcurrentHashMap<>(100);
Thread t1 = new Thread(() -> {
for (int i = 0; i < 25; i++) {
hm.put(i + "", i + "");
}
});
Thread t2 = new Thread(() -> {
for (int i = 25; i < 51; i++) {
hm.put(i + "", i + "");
}
});
t1.start();
t2.start();
System.out.println("----------------------------");
//为了t1和t2能把数据全部添加完毕
Thread.sleep(1000);
//0-0 1-1 ..... 50- 50
for (int i = 0; i < 51; i++) {
System.out.println(hm.get(i + ""));
}//0 1 2 3 .... 50
}
}

6.3CountDownLatch
java
new CountDownLatch(int count) //实例化一个倒计数器,count指定初始计数
countDown() // 每调用一次,计数减一
await() //等待,当计数减到0时,阻塞线程(可以是一个,也可以是多个)并行执行
案例:
java
class Player implements Runnable {
private String playerName;
private CountDownLatch countDownLatch;
public Player(String playerName, CountDownLatch countDownLatch) {
this.playerName = playerName;
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
System.out.println(playerName + "开始准备");
try {
Thread.sleep(2000);
System.out.println(playerName + "准备完成");
countDownLatch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(2);//指定一个初始计数
new Thread(new Player("Player-1", countDownLatch)).start();
new Thread(new Player("Player-2", countDownLatch)).start();
//
countDownLatch.await();
System.out.println("游戏开始");
}
}
上述代码join也能实现
java
class Player1 implements Runnable {
private String playerName;
public Player1(String playerName) {
this.playerName = playerName;
}
@Override
public void run() {
try {
System.out.println(playerName + "开始准备");
Thread.sleep(2000);
System.out.println(playerName+"准备完成");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class JoinDemo {
public static void main(String[] args) throws InterruptedException {
Thread player1 = new Thread(new Player1("player-1"));
Thread player2 = new Thread(new Player1("player-2"));
player1.start();
player2.start();
player1.join();
player2.join();
System.out.println("游戏开始");
}
}
调用一个子线程的 join()方法后,子线程程会一直被阻塞直到该线程运行完毕。而 CountDownLatch 则使用计数器允许子线程运行完毕或者运行中时候递减计数,也就是 CountDownLatch 可以在子线程运行任何时候让 await 方法返回而不一定必须等到线程结束,countDownLatch 相比 Join 方法让我们对线程同步有更灵活的控制。
6.4CyclicBarrier
这个类的中文意思是"循环栅栏"。大概的意思就是一个可循环利用的屏障。该命令只在每个屏障点运行一次。若在所有参与线程之前更新共享状态,此屏障操作很有用
常用方法:
- CyclicBarrier(int parties, Runnable barrierAction) 创建一个CyclicBarrier实例,parties指定参与相互等待的线程数,barrierAction一个可选的Runnable命令,该命令只在每个屏障点运行一次,可以在执行后续业务之前共享状态。该操作由最后一个进入屏障点的线程执行。
- CyclicBarrier(int parties) 创建一个CyclicBarrier实例,parties指定参与相互等待的线程数。
- await() 该方法被调用时表示当前线程已经到达屏障点,当前线程阻塞进入休眠状态,直到所有线程都到达屏障点,当前线程才会被唤醒。
案例:
java
class CyclicBarrierResource implements Runnable {
String studentName;
CyclicBarrier cyclicBarrier;
public CyclicBarrierResource(String studentName, CyclicBarrier cyclicBarrier) {
this.studentName = studentName;
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
//模拟每个人到达教室的时间
try {
TimeUnit.SECONDS.sleep((long) Math.random() * 5000);
System.out.println(studentName + "到达教室门口");
cyclicBarrier.await();//等待其它人到达教室门口
System.out.println(studentName+"进入教室");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class CyclicBarrierDemo {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(3, () -> {
System.out.println("最后一个达到教室,罚款200元");
});
new Thread(new CyclicBarrierResource("first student",cyclicBarrier)).start();
new Thread(new CyclicBarrierResource("tow student",cyclicBarrier)).start();
new Thread(new CyclicBarrierResource("three student",cyclicBarrier)).start();
}
}
6.5Semaphore
emaphore可以控制同时访问的线程个数。非常适合需求量大,而资源又很紧张的情况。比如给定一个资源数目有限的资源池,假设资源数目为N,每一个线程均可获取一个资源,但是当资源分配完毕时,后来线程需要阻塞等待,直到前面已持有资源的线程释放资源之后才能继续。
Semaphore:主要的场景就是限流:但是只能对单个微服务限流,如果是某个微服务的集群,那个Juc下的Semaphore就做不到。就需要找个分布式的限流工具。
Redisson----也有一个信号量Semaphore。----对整个集群来完成限流。
Juc-----信号量Semaphore----对单个实例完成限流,不能够对整个集群起到限流。
Sentinel:组件---给程序员用。
public Semaphore(int permits) // 构造方法,permits指资源数目(信号量)
public void acquire() throws InterruptedException // 占用资源,当一个线程调用acquire操作时,它要么通过成功获取信号量(信号量减1),要么一直等下去,直到有线程释放信号量,或超时。
public void release() // (释放)实际上会将信号量的值加1,然后唤醒等待的线程。
信号量主要用于两个目的:
- 多个共享资源的互斥使用。
- 用于并发线程数的控制。保护一个关键部分不要一次输入超过N个线程。
7.阻塞队列

方法:
代码演示:
java
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
public class BlockQueueDemo {
public static void main(String[] args) {
ArrayBlockingQueue<String> blockingQueue = new ArrayBlockingQueue<String>(5);
// 一组add remove
//addRemove(blockingQueue);
// 二组:offer poll
// offerPoll(blockingQueue);
// 三组:put take
// putTake(blockingQueue);
// 四组:带时间
// offerPollWithTime(blockingQueue);
}
private static void offerPollWithTime(ArrayBlockingQueue<String> blockingQueue) {
try {
System.out.println(blockingQueue.offer("a"));
System.out.println(blockingQueue.offer("b"));
System.out.println(blockingQueue.offer("c"));
System.out.println(blockingQueue.offer("d"));
System.out.println(blockingQueue.offer("e"));
try {
System.out.println(blockingQueue.offer("f",5, TimeUnit.SECONDS));
} catch (InterruptedException e) {
e.printStackTrace();
}
String poll = blockingQueue.poll(3, TimeUnit.SECONDS);
System.out.println(poll);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static void putTake(ArrayBlockingQueue<String> blockingQueue) {
for (int i = 1; i <=5; i++) {
try {
blockingQueue.put(""+i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
new Thread(()->{
try {
Thread.sleep(5000);
blockingQueue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
try {
System.out.println(Thread.currentThread().getName()+"开始放入6");
blockingQueue.put("6");
System.out.println(Thread.currentThread().getName()+"放入6成功");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static void offerPoll(ArrayBlockingQueue<String> blockingQueue) {
for (int i = 1; i <=5; i++) {
boolean flag = blockingQueue.offer("" + i);
System.out.println(flag);
// System.out.println(blockingQueue.poll());
}
boolean offer = blockingQueue.offer("6");
System.out.println(offer);
String poll = blockingQueue.poll();
System.out.println(poll);
}
private static void addRemove(ArrayBlockingQueue<String> blockingQueue) {
for (int i = 1; i <=5; i++) {
boolean flag = blockingQueue.add("" + i);
System.out.println(flag);
// System.out.println(blockingQueue.remove());
}
boolean add = blockingQueue.add("6");
System.out.println(add);
System.out.println(blockingQueue.remove());
}
}
8.线程池
线程池是一种池化的技术,类似的还有数据库连接池、HTTP 连接池等等。
池化的思想主要是为了减少每次获取和结束资源的消耗,提高对资源的利用率。
线程池的优势:线程复用;控制最大并发数;管理线程。
8.1Executors工具类

代码:
java
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.*;
public class ThreadPoolDemo {
public static void main(String[] args) throws InterruptedException {
// 一池一线程
// singleThreadPool();
// 一池多线程
// FixedThreadPool();
// 创建缓存的线程
// CacheThreadPool();
// 创建带延时任务或者定时任务的线程池
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
// SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
// String format = simpleDateFormat.format(new Date());
// System.out.println("开始准备执行任务时间为:"+format);
// scheduledExecutorService.schedule(()->{
// String format1 = simpleDateFormat.format(new Date());
// System.out.println();
// System.out.println("执行任务---do some thing--时间为"+format1);
// },5, TimeUnit.SECONDS);
// scheduledExecutorService.shutdown();
// ScheduledFuture<String> schedule = scheduledExecutorService.schedule(new Callable<String>() {
// @Override
// public String call() throws Exception {
// return Thread.currentThread().getName();
// }
// }, 5, TimeUnit.SECONDS);
//
// String s = null;
// try {
// s = schedule.get();
// } catch (InterruptedException e) {
// e.printStackTrace();
// } catch (ExecutionException e) {
// e.printStackTrace();
// }
// System.out.println("结果---" + s);
// SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yy-MM-dd Hh:mm:ss");
// System.out.println("当前时间"+simpleDateFormat.format(new Date()));
// scheduledExecutorService.scheduleAtFixedRate(()->{
// String format = simpleDateFormat.format(new Date());
// System.out.println(format+"开始执行任务do some thing");
// try {
// Thread.sleep(6000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// },3,5,TimeUnit.SECONDS);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yy-MM-dd Hh:mm:ss");
System.out.println("当前时间"+simpleDateFormat.format(new Date()));
scheduledExecutorService.scheduleWithFixedDelay(()->{
String format = simpleDateFormat.format(new Date());
System.out.println(format+"开始执行任务do some thing");
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
e.printStackTrace();
}
},3,5,TimeUnit.SECONDS);
}
private static void CacheThreadPool() {
ExecutorService executorService = Executors.newCachedThreadPool(new ThreadFactory() {
int i = 0;
@Override
public Thread newThread(Runnable r) {
i++;
Thread thread = new Thread(r);
thread.setName("myPool-" + i + "-thread-" + i);
return thread;
}
});
for (int i = 1; i <= 10; i++) {
int finalI = i;
executorService.execute(() -> {
System.out.println("任务" + finalI + "被线程池中的线程" + Thread.currentThread().getName() + "执行");
});
}
}
private static void FixedThreadPool() {
ExecutorService executorService = Executors.newFixedThreadPool(5, new ThreadFactory() {
int i = 0;
@Override
public Thread newThread(Runnable r) {
i++;
Thread thread = new Thread(r);
thread.setName("myPool-" + i + "-thread-" + i);
return thread;
}
});
for (int i = 1; i <= 8; i++) {
int finalI = i;
executorService.execute(() -> {
System.out.println("执行的任务" + finalI + "被线程池中线程" + Thread.currentThread().getName() + "执行");
});
}
executorService.shutdown();
}
private static void singleThreadPool() {
//1。一池一线程
ExecutorService executorService = Executors.newSingleThreadExecutor(new ThreadFactory() {
int i = 0;
@Override
public Thread newThread(Runnable r) {
i++;
Thread thread = new Thread(r);
thread.setName("myPool-" + i + "thread-" + i);
return thread;
}
});
executorService.execute(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("执行任务所用的线程为:" + Thread.currentThread().getName());
}
});
// executorService.shutdown();
}
}

8.2ThreadPoolExecutor(常用)
8.2.1使用
java
public class ThreadPoolDemo {
public static void main(String[] args) {
ExecutorService threadPool = new ThreadPoolExecutor(2, 5,
2, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println("自定义拒绝策略");
}
}
);
try {
for (int i = 0; i < 2; i++) {
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + "执行了业务逻辑");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown();
}
}
}
8.2.2 参数

8.2.3 底层工作原理
重要:
1.在创建了线程池后,线程池中的线程数为零。
2.当调用execute()方法添加一个请求任务时,线程池会做出如下判断:
如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行这个任务;
如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列;
如果这个时候队列满了且正在运行的线程数量还小于maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;
如果队列满了且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会启动饱和拒绝策略来执行。
3.当一个线程完成任务时,它会从队列中取下一个任务来执行。
4.当一个线程无事可做超过一定的时间(keepAliveTime)时,线程会判断:
如果当前运行的线程数大于corePoolSize,那么这个线程就被停掉。
所以线程池的所有任务完成后,它最终会收缩到corePoolSize的大小
8.2.3.1拒绝策略
