当前位置: 首页 > news >正文

JUC多线程个人笔记

1.基础知识

并发编程的目的是为了让程序变得更快,但是,并不是启动更多的线程就能让程序最大限度的并发执行。在进行并发编程中,如果希望通过多线程让程序运行的更快,会面临非常多的挑战。比如上下文切换、死锁问题,以及受限于硬件和软件的资源问题。

多线程最主要的作用是压榨cpu.

1.1死锁

死锁就是两个进程(线程)同时占用两个资源,但又在彼此等待对方释放锁,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁
请添加图片描述
代码演示:

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类

public class MyThread extends Thread {@Overridepublic 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:

public class MyRunnable implements Runnable {@Overridepublic 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();}
}

匿名内部类实现:

new Thread(new Runnable() {@Overridepublic void run() {// 调用资源方法,完成业务逻辑}
}, "your thread name").start();

2.3实现Callable接口

public class MyCallable implements Callable<String> {@Overridepublic 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线程睡眠

请添加图片描述

public class MyRunnable implements Runnable {@Overridepublic 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.线程周期

请添加图片描述

在这里插入图片描述

public class Thread{public enum State{NEW,RUNNABLE,BLOCKED,WAITING,TIME_WATING,TERMINATED }
}

4.线程同步

4.1Synchronized

synchronized 同步块是 Java 提供的一种原子性内置锁,Java 中的每个对象都可以把它当作一个同步锁来使用,这些 Java 内置的使用者看不到的锁被称为内置锁,也叫作监视器锁。

每个对象Object都内置了一个监视器锁,当某个线程获得了这个监视器锁后,其它线程在想获得这个对象的监视器锁,就必须要排队。也就是说:synchronzied关键字的底层,相当于一个排它锁。
请添加图片描述

public class SellTicket implements Runnable {private int tickets = 100;private Object obj = new Object();@Overridepublic 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
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都是可重入锁,可重入锁的一个优点是可一定程度避免死锁

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区别

独占锁:在任意一时刻,有且只有一个线程能够获取锁

  1. lock是一个接口,而synchronized是java的一个关键字
  2. Lock位于juc.locks包中,synchronizedjdk内置的锁。
  3. synchronized是独占锁,加锁和解锁的过程自动进行,易于操作,但不够灵活。ReentrantLock也是独占锁,加锁和解锁的过程需要手动进行,不易操作,但非常灵活。
  4. synchronized 可重入,因为加锁和解锁自动进行,不必担心最后是否释放锁;ReentrantLock也可重入,但加锁和解锁需要手动进行,且次数需一样,否则其他线程无法获得锁。可以避免死锁。
  5. synchronized在发生异常时会自动释放占有的锁,因此不会出现死锁,而ReentrantLock发生异常时,不会主动释放占有的锁,必须手动来释放锁,可能引起死锁的发生。
  6. Synchronized不可响应中断:第一个线程获得某把锁后,第二个线程也想要获得该锁,则它必须处于阻塞或等待状态。如果第一个线程不释放锁,那第二个线程就会一直阻塞或等待,不可被中断。

5.线程通信

请添加图片描述
案例生产者消费者
所谓生产者消费者问题,实际上主要是包含了两类线程:

​ 一类是生产者线程用于生产数据

​ 一类是消费者线程用于消费数据

为了解耦生产者和消费者的关系,通常会采用共享的数据区域,就像是一个仓库

生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者的行为

消费者只需要从共享数据区中去获取数据,并不需要关心生产者的行为

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;}@Overridepublic 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,叫醒等待的消费者开吃。@Overridepublic 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;}@Overridepublic 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中的线程安全

这段是线程不安全的

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保证线程安全

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也是线程安全的,效率较高。

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- 50for (int i = 0; i < 51; i++) {System.out.println(hm.get(i + ""));}//0 1 2 3 .... 50}
}

请添加图片描述

6.3CountDownLatch

new CountDownLatch(int count) //实例化一个倒计数器,count指定初始计数
countDown() // 每调用一次,计数减一
await() //等待,当计数减到0时,阻塞线程(可以是一个,也可以是多个)并行执行

案例:

class Player implements Runnable {private String playerName;private CountDownLatch countDownLatch;public Player(String playerName, CountDownLatch countDownLatch) {this.playerName = playerName;this.countDownLatch = countDownLatch;}@Overridepublic 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也能实现

class Player1 implements Runnable {private String playerName;public Player1(String playerName) {this.playerName = playerName;}@Overridepublic 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

这个类的中文意思是“循环栅栏”。大概的意思就是一个可循环利用的屏障。该命令只在每个屏障点运行一次。若在所有参与线程之前更新共享状态,此屏障操作很有用
常用方法:

  1. CyclicBarrier(int parties, Runnable barrierAction) 创建一个CyclicBarrier实例,parties指定参与相互等待的线程数,barrierAction一个可选的Runnable命令,该命令只在每个屏障点运行一次,可以在执行后续业务之前共享状态。该操作由最后一个进入屏障点的线程执行。
  2. CyclicBarrier(int parties) 创建一个CyclicBarrier实例,parties指定参与相互等待的线程数。
  3. await() 该方法被调用时表示当前线程已经到达屏障点,当前线程阻塞进入休眠状态,直到所有线程都到达屏障点,当前线程才会被唤醒。

案例:

class CyclicBarrierResource implements Runnable {String studentName;CyclicBarrier cyclicBarrier;public CyclicBarrierResource(String studentName, CyclicBarrier cyclicBarrier) {this.studentName = studentName;this.cyclicBarrier = cyclicBarrier;}@Overridepublic 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.阻塞队列

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
方法:
在这里插入图片描述
代码演示:


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工具类

在这里插入图片描述
代码:


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;@Overridepublic 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;@Overridepublic 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;@Overridepublic 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使用
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() {@Overridepublic 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拒绝策略

在这里插入图片描述

9其他

9.1 java内存模型(JMM)

9.1 volatile

9.2 cas

http://www.dtcms.com/a/354103.html

相关文章:

  • 【DC工具GUI入门】
  • APP测试全流程以及测试点
  • 【开题答辩全过程】以 基于SpringBoot的流浪动物领养系统的设计与实现 为例,包含答辩的问题和答案
  • 从Java到Go:初遇Go语言的震撼体验
  • 力扣 30 天 JavaScript 挑战 第41天 (第十二题)对异步操作,promise,async/await有了更深理解
  • 【Linux实时内核机制】ww_rt_mutex 的contending_lock异常问题
  • android/java中主线程和子线程的详解
  • Nano Banana揭秘:Google Gemini 2.5 Flash Image正式发布 | AI图像编辑新时代
  • 内网应用如何实现外网访问?外地通过公网地址访问内网服务器的设置方法
  • 动态规划:青蛙跳台阶实践
  • H20 性能表现之 Kimi-K2
  • 【git】:gitee项目管理vs2019
  • 装饰器进阶与设计模式
  • Linux入门教程 第十五章 Linux 系统调优工具
  • 【工具篇】github/huggingface 镜像源总结
  • 嵌入式系统学习Day24(线程)
  • Custom SRP - Shadow Masks
  • Axure:如何将SVG转换为形状
  • leetcode 155 官方golang标准答案错误
  • Java Lambda 处理日期时间 根据区间找出区间内集合
  • Linux程序与进程:核心概念与管理全解析
  • Class45循环神经网络RNN
  • “互联网 +”时代下开源 AI 大模型 AI 智能名片 S2B2C 商城小程序:行业变革与未来展望
  • 基于 Ultralytics YOLO11与 TrackZone 的驱动的高效区域目标跟踪方案实践
  • Python Imaging Library (PIL) 全面指南:PIL基础入门-Python图像处理实战
  • 多版本兼容的golang客服系统
  • 稀土:从“稀有”到“命脉”的科技核心
  • 通过概率正 - 未标记网络从医学图像的特定感兴趣区域中学习|文献速递-深度学习人工智能医疗图像
  • 【底层机制】thread_local 变量的初始化时机和生命周期
  • Spring Retry Spring 生态系统优雅的重试组件