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

JavaEE 【知识改变命运】05 多线程(4)

文章目录

  • 单例模式
    • 什么是单例模式
    • 饿汉模式
    • 懒汉模式
    • 多线程- 懒汉模式
      • 分析多线程问题
      • 第一种添加sychronized的方式
      • 第二种添加sychronized的方式
      • 改进第二种添加sychronized的方式(DCL检查锁)
  • 阻塞队列
    • 什么是阻塞队列
    • 什么是消费生产者模型
    • 标准库中的阻塞队列
    • 消息队列应用的场景
    • 自己模拟实现阻塞队列
  • 定时器
    • 标准库中的定时器
    • 实现定时器
  • 工厂模式
  • 线程池
    • 线程池的一些问题
    • 实现一个线程池
    • 创建系统自带的线程池
  • wait和sleep的区别

单例模式

什么是单例模式

  • 单例模式能保证某个类在程序中只存在唯⼀⼀份实例, ⽽不会创建出多个实例
  • 单例模式实现方式很多,最常用饿汉模式和懒汉模式实现

饿汉模式

  • 创建过程:
    – 1. 定义一个static修饰的变量,就可以包子这个变量全局唯一
    – 2.构造方法私有化,防止变量被修改
    – 3.提供一个获取变量的get静态方法,通过类名的方式去调用
public class Singleton {//懒汉模式//创建一个私有静态属性,并且把对象new出来private static Singleton instance =new Singleton();//私有化构造器private Singleton() {}//提供一个公共的静态方法,返回单例对象public static Singleton getInstance() {return instance;}public static void main(String[] args) {Singleton s1 = Singleton.getInstance();Singleton s2 = Singleton.getInstance();System.out.println(s1 == s2); // true}
}
  • 把这种类加载时候就完成对象的初始化的创建方式,就叫”饿汉模式“
  • 这种模式存在的问题是,可能对象创建了但是没有使用,从而导致资源浪费。

懒汉模式

public class SingLetonLazy {//创建一个对象不去new对象private static SingLetonLazy instance;//私有化构造器private SingLetonLazy() {}//提供一个公共的静态方法,返回单例对象public static SingLetonLazy getInstance() {if(instance==null) {instance=new SingLetonLazy();}return instance;}public static void main(String[] args) {SingLetonLazy s1 = SingLetonLazy.getInstance();SingLetonLazy s2 = SingLetonLazy.getInstance();System.out.println(s1 == s2); // true}
}
  • 懒汉模式创建对象,在要获得单例对象的时候,创建,避免了资源的浪费但是存在多线程安全问题。

多线程- 懒汉模式

public class SingLetonLazy {private static SingLetonLazy instance;//私有化构造器private SingLetonLazy() {}//提供一个公共的静态方法,返回单例对象public static SingLetonLazy getInstance() {if(instance==null) {instance=new SingLetonLazy();}return instance;}public static void main(String[] args) {for (int i = 0; i < 10; i++) {Thread t1 =new Thread(()->{try {sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}SingLetonLazy s1 = SingLetonLazy.getInstance();System.out.println(s1);});t1.start();}}
}

在这里插入图片描述
出现了多线程问题。

分析多线程问题

在这里插入图片描述

第一种添加sychronized的方式

public static SingLetonLazy getInstance() {if(instance==null) {synchronized (SingLetonLazy.class){instance=new SingLetonLazy();}}return instance;}

在这里插入图片描述

  • 这种写法不能保证多线程安全

第二种添加sychronized的方式

public static SingLetonLazy getInstance() {synchronized (SingLetonLazy.class){if(instance==null) {instance=new SingLetonLazy();}}return instance;}

在这里插入图片描述

  • 这种写法似乎可以保证多线程安全,但是还是存在一个问题,当一个线程进行这个方法,如果没有初始化,则获取锁进行初始化操作,此时单例对象被第一个线程创建完成,后面的线程以后永远不会在执行new对象的操作,synchronized就没必要添加了,第二次线程开始这个加锁解锁都是无效的操作,lock和unlock对应的锁指令是互斥锁,比较消耗系统资源。
  • 添加锁本质就会消耗很多资源

改进第二种添加sychronized的方式(DCL检查锁)

 private volatile static SingLetonLazy instance;//给共享变量加上volatile

在这里插入图片描述

public static SingLetonLazy getInstance() {//第一次判断是否加锁if(instance==null) {synchronized (SingLetonLazy.class) {判断是否创建了一个对象if (instance == null) {instance = new SingLetonLazy();}}}return instance;}

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

阻塞队列

什么是阻塞队列

  1. 阻塞队列本质还是队列,遵循”先进先出“的原则
  2. 阻塞队列是一种线程安全的数据结构,有以下特征
    – 当队列满的时候,继续入队就会发生阻塞等待,直到队列中有其他线程取出元素后,队列有空位才会再次入队
    – 当队列空的时候,继续出队就会放生阻塞等待,知道队列中有其他线程插入元素时候,队列有元素才会再次出队
  3. 阻塞队列适用于一种典型场景‘消费生产者模型’

什么是消费生产者模型

  1. 生产者消费者模式就是通过一个容器解决消费者和生产者的强耦合问题。
  2. 生产者和消费者不会直接影响,生产者生产的资源直接放入容器(阻塞队列)中,消费者消费的资源,直接从容器(阻塞队列)中拿。从而保证生产者不会生产资源等待消费者消费,消费者也不会等待生产者生产资源。
  3. 阻塞队列相当于一个缓冲区,平衡生产者和消费者的处理能力
    – 比如双11时候,会涌入大量的支付订单,这时候如果服务器直接处理这些订单,可能就会把服务器挤爆,这时候中间设置一个阻塞队列,把产生的大小支付订单扔进阻塞队列里面,然后服务器根据自己的处理能力,从队列里面取出要处理的订单,从而达到削峰的效果,防止服务器被挤爆。
  4. 阻塞队列也能使生产者和消费者之间 解耦
    – 过年期间大家都会包饺子,擀饺子皮相当于生产者,包饺子相当于消费者,中间放个案板,所有的饺子皮都放在案板上,包饺子皮的人直接从案板上取,擀饺子皮的可能是妈妈可能是爸爸可能是我,无论是谁擀饺子皮消费者都不关心,因为都是从案板上取的饺子皮。

标准库中的阻塞队列

  1. 在 Java 标准库中内置了阻塞队列. 如果我们需要在一些程序中使用阻塞队列, 直接使用标准库中的即可.
    – BlockingQueue 是一个接口. 真正实现的类是 LinkedBlockingQueue.
    – put 方法用于阻塞式的入队列, take 用于阻塞式的出队列.
    – BlockingQueue 也有 offer, poll, peek 等方法, 但是这些方法不带有阻塞特性.
  2. 创建一个BlockingQueue
    在这里插入图片描述
    在这里插入图片描述
    – 其中capacity是这个队列的大小。

在这里插入图片描述
– 这里设置一个三个大小的阻塞队列,当第四个元素入队时候就会发生阻塞等待
在这里插入图片描述
– 这里取出三个元素后,队列为空,队列阻塞等待
在这里插入图片描述
在这里插入图片描述
– put和take都会抛出一个InterrupteException异常

  1. 其他补充常问的方法
    在这里插入图片描述
    – add()
    在这里插入图片描述
    – offer()
    在这里插入图片描述
    – remove()
    在这里插入图片描述
    – poll
    在这里插入图片描述

消息队列应用的场景

  1. 解耦
    – 高内聚,低耦合:业务强相关的代码组织在一起,不相关的单独定义便于以后的维护,以为要把重复的代码尽量抽象出来,封装成一个公共方法,在需要的地方直接调用这个方法即可
    – 生产消息的应用程序把消息写进消息队列(生产者),使用消息的应用程序从消息队列里面取出消息(消费者)
    在这里插入图片描述
    在这个模型中,服务器A要时刻感应到服务器B,在调用的过程中双方都要知道对方需要调用的参数和调用方式
    ,在ABC整个调用的链路中秒如果其中一个出现了问题,就会影响整个业务执行
    在这里插入图片描述

  2. 削峰填谷(流量)
    – 针对流量暴增的时候使用消息队列来进行缓冲
    在这里插入图片描述
    在这里插入图片描述
    – 实例:
    在这里插入图片描述

  3. 异步操作
    周末:我和我女朋友取买包子

  • 同步操作:她一直等我买包子回来,开始,中间这个过程啥也不干,同步发出请求后,必须要等待响应才能- 进行下一步操作
  • 异步操作:她让我去之后,在家做点别的事情,比如,做点小菜,熬点稀饭,异步操作,发出请求之后,不需要等待响应,而做其他的事情,等待响应主动通知自己

自己模拟实现阻塞队列

public class MyBlockingDeque {int [] arr;volatile int head=0;volatile int tail=0;volatile int size=0;MyBlockingDeque(int capacity){if(capacity<=0) {throw new RuntimeException("capacity must be positive");}arr = new int[capacity];}public void put(int val) throws InterruptedException {while(size>=arr.length) {synchronized (this){this.wait();}}arr[tail]=val;tail++;if(tail>=arr.length) {tail=0;}size++;synchronized (this){this.notifyAll();}}public synchronized int take() throws InterruptedException {while(size==0) {this.wait();}int val =arr[head];head++;if(head>=arr.length) {head=0;}size--;this.notifyAll();return val;}
}
class Main{public static void main(String[] args) throws InterruptedException {MyBlockingDeque myBlockingDeque = new MyBlockingDeque(10);int i=0;new Thread(()->{while (true){try {sleep(1000);int val = myBlockingDeque.take();System.out.println(Thread.currentThread().getName()+"取出成功"+val);} catch (InterruptedException e) {e.printStackTrace();}}}).start();while (true){myBlockingDeque.put(i);System.out.println(Thread.currentThread().getName()+"添加成功"+i);i++;}}
}

在这里插入图片描述

  • put时候
    在这里插入图片描述
  • take时候
    在这里插入图片描述
  • 我们上锁可以锁代码块也可以方法
    在这里插入图片描述
  • if改为while的原因是防止大量现场
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

定时器

标准库中的定时器

  • 标准库中定义一个TImer类。Timer类的核心方法为schedule
  • schedule包含两个参数,第一个参数指定要执行的代码任务,第二个参数指定多场实际之后执行。
import java.util.Timer;
import java.util.TimerTask;public class Demo_801 {public static void main(String[] args) {// 使用jdk中提供的类,创建一个定器Timer timer = new Timer();//向定时器添加任务timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("Hello World!");}},1000);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("任务1");}},1500);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("任务2");}},2000);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("任务3");}},2500);}
}

ctrl+p查看方法的参数列表
在这里插入图片描述
定义自己的任务
在这里插入图片描述
延迟多久执行的任务
在这里插入图片描述
任务具体执行的时间
在这里插入图片描述

实现定时器

  1. 设计思路
  • 用一个类描述任务和执行任务的时间
    – 具体任务逻辑用Runable表示,执行时间可以用一个long型delay表示
  • 组织任务和时间对应的对象
    – 可以考虑用一个阻塞队列,我们选择用PriorityBlockingQueue(),保证扫描任务时候,延时最少的任务先执行

- 提供一个方法,
在这里插入图片描述

  • 提供一个方法,提交任务
    在这里插入图片描述

  • 要有一个线程执行任务
    – 在哪里定义扫描线程?
    –在构造方法里面直接定义线程
    – 1.取出队首元素,2.判断一下任务到执行的时间没有,3,如果到了就执行,4.没有就放回队列
    在这里插入图片描述

  1. 代码实现
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;public class MyTimer {//用一个阻塞队列来组织任务private BlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();private Object lock = new Object();public MyTimer() {//创建线程Thread thread =new Thread(()->{while(true){try {//从队列中取出任务MyTask task=this.queue.take();//判断有没有到执行的时间long currentTime=System.currentTimeMillis();if(currentTime>=task.getTime()){//时间到了执行task.getRunnable().run();} else{//时间没到,将任务放回队列中this.queue.put(task);}} catch (InterruptedException e) {throw new RuntimeException(e);}}});thread.start();}/*** 添加定时任务* @param runnable 任务* @param delay 延时* @throws InterruptedException*/public void schedule(Runnable runnable,long delay) throws InterruptedException {//根据传的参数构造一个MyTask对象MyTask task=new MyTask(runnable,delay);//将这个MyTask对象阻塞放入队列中queue.put(task);}
}//MyTask类,用于封装任务和执行时间
class MyTask implements Comparable<MyTask>{//任务private Runnable runnable;//执行时间private long time;public MyTask(Runnable runnable, long delay) {//增强代码健壮性if(runnable==null){throw new IllegalArgumentException("任务不能为空");}if(delay<0) {throw new IllegalArgumentException("延迟时间不能为负数");}this.runnable = runnable;//计算出任务的执行时间this.time = delay+System.currentTimeMillis();}public Runnable getRunnable() {return runnable;}public long getTime() {return time;}@Overridepublic int compareTo(MyTask o) {if(this.getTime()<o.getTime()){return -1;} else if(this.getTime()==o.getTime()){return 0;}else {return 1;}//万一时间超过了long的范围溢出,怎么办?用上面的比较比较好//return (int)(this.getTime()-o.getTime());}
}
public class Test {public static void main(String[] args) throws InterruptedException {MyTimer timer = new MyTimer();timer.schedule(new Runnable(){@Overridepublic void run() {System.out.println("任务1");}},1000);timer.schedule(new Runnable(){@Overridepublic void run() {System.out.println("任务2");}},500);timer.schedule(new Runnable(){@Overridepublic void run() {System.out.println("任务3");}},2000);//timer.schedule(null,-100);//任务加强健壮性}
}
  • 注意事项:
    – 注意我们要实现Conparable接口指定排序规则
    在这里插入图片描述
    在这里插入图片描述

– 我们要添加校验,防止非法的输入
在这里插入图片描述

– 解决数据可能会溢出的问题,比如设置的时间
在这里插入图片描述

  1. 再次深度优化我们代码
    在这里插入图片描述
  • 以上代码我们存在“忙等”的情况
  • 优化后的代码
    在这里插入图片描述
    – 这里注意一下这个lambda表达式中的this引用的是他所在对象的实例。
  • 新的问题:当任务1在等待时候,这时候如果又put进来一个新的任务,这个等待的时间就有问题。再次优化
    在这里插入图片描述
    每添加新的任务都进行一次唤醒,保证执行的永远是最少延时的任务。
  • 从CPU调度的过程中可以会产生的执行顺序的问题,或当一个线程执行到一半的时间被掉调度走的现象。
    在这里插入图片描述
    这个线程造成的原因就是没有保证原子性。
  • 优化代码
    在这里插入图片描述

在这里插入图片描述

  • 再次观察一种极端情况
    在这里插入图片描述
    – 我们发现当我们把三个任务的延时时间设置为0的时候,结果只执行了任务1,我们进行调试
    在这里插入图片描述
    – 调试之后我们又发现是正常情况,但是运行时候不符合我们的预期结果,这时候我们不要慌,我们用jconsole工具去查看下扫描情况
    在这里插入图片描述
  • 我们发现在MyTimer。java22行出现了问题
  • 在这里插入图片描述

1.创建一个定时器
2.向定时器添加任务1
3.第一个任务被添加到阻塞队列中
4.扫描线程启动,处理第一个任务
5.扫描线程1循环,获得第二个任务时候,队列为空,开始等待,同时扫描线程获得锁
6.主线程向阻塞队列添加任务时候,等待扫描对象的对象,由于扫描线程无法释放锁对象,主线程也就获取不到锁对象,造成相互等待,造成死锁

  • 我们再次优化代码创造一个后台扫描线程,只做定时唤醒操作,定时1秒或者10ms,唤醒一次

在这里插入图片描述
-最终的代码

public class MyTimer {//用一个阻塞队列来组织任务private BlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();private Object lock = new Object();public MyTimer() {//创建线程Thread thread =new Thread(()->{while(true) {try {synchronized (this) {//从队列中取出任务MyTask task = this.queue.take();//判断有没有到执行的时间long currentTime = System.currentTimeMillis();if (currentTime >= task.getTime()) {//时间到了执行task.getRunnable().run();} else {//时间没到,将任务放回队列中long waitTime = task.getTime() - currentTime;this.queue.put(task);this.wait(waitTime);}}} catch (InterruptedException e) {throw new RuntimeException(e);}}});thread.start();//创建守护线程,定时唤醒一次Thread deamonThread=new Thread(()->{synchronized (this) {//唤醒一次this.notifyAll();//每隔100ms唤醒一次try {TimeUnit.MILLISECONDS.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}}});//设置为守护线程deamonThread.setDaemon(true);deamonThread.start();}/*** 添加定时任务* @param runnable 任务* @param delay 延时* @throws InterruptedException*/public void schedule(Runnable runnable,long delay) throws InterruptedException {//根据传的参数构造一个MyTask对象MyTask task=new MyTask(runnable,delay);//将这个MyTask对象阻塞放入队列中queue.put(task);System.out.println("任务添加成功");}
}//MyTask类,用于封装任务和执行时间
class MyTask implements Comparable<MyTask>{//任务private Runnable runnable;//执行时间private long time;public MyTask(Runnable runnable, long delay) {//增强代码健壮性if(runnable==null){throw new IllegalArgumentException("任务不能为空");}if(delay<0) {throw new IllegalArgumentException("延迟时间不能为负数");}this.runnable = runnable;//计算出任务的执行时间this.time = delay+System.currentTimeMillis();}public Runnable getRunnable() {return runnable;}public long getTime() {return time;}@Overridepublic int compareTo(MyTask o) {if(this.getTime()<o.getTime()){return -1;} else if(this.getTime()==o.getTime()){return 0;}else {return 1;}//万一时间超过了long的范围溢出,怎么办?用上面的比较比较好//return (int)(this.getTime()-o.getTime());}
public class Test {public static void main(String[] args) throws InterruptedException {MyTimer timer = new MyTimer();timer.schedule(new Runnable(){@Overridepublic void run() {System.out.println("任务1");}},0);timer.schedule(new Runnable(){@Overridepublic void run() {System.out.println("任务2");}},0);timer.schedule(new Runnable(){@Overridepublic void run() {System.out.println("任务3");}},0);//timer.schedule(null,-100);//任务加强健壮性}
}

工厂模式

  • 先看出现的问题在这里插入图片描述
    我们这里造成了重载参数的相同,但是我们就是要这样的构造方法我们怎么解决呢?

在这里插入图片描述
工厂方法模式。根据不同的业务需求定义不同的方法来获取对象。

线程池

线程池的一些问题

  • 什么是线程池
    1.线程池就是一次创建多个线程,把这些线程放进一个池中,用的时候从池中取出,用完就还回去
  • 为什么要用线程池
    我们首先要明白,线程的创建和销毁都会消耗大量的资源,线程池中的线程当有任务的时候,就会执行任务,没有任务的时候就阻塞等待,并不销毁线程,线程池最⼤的好处就是减少每次启动、销毁线程的损耗。
  • 为什么使用线程池可以提升效率
    少量创建,少量销毁,创建一个线程要分为内核态和用户态,用户态相当于jvm层面,内核太相当于操作系统层面,当我们在jvm层面创建一个线程,就要在操作系统层面创建对应指令,就会消耗大量资源,消耗线程也如此,所以线程池减少了频繁的销毁和创建,用的时候就直接在线程池里面用已经创建多的,从而提升效率。
  • 怎么用?
    – jdk给我们提供了一组针对不同场景的线程池实例
public static void main(String[] args) {//1.用来处理大量短时间的任务的线程池,如果池没有可用的线程将创建线程,如果线程空闲60秒将收回并移除缓存ExecutorService cachedThreadpool= Executors.newCachedThreadPool();//2.创建一个操作无界队列,线程池大小固定的线程池ExecutorService fixedThreadpool= Executors.newFixedThreadPool(5);//可以指定线程数量//3.创建一个操作无界队列,只有一个线程的线程池ExecutorService singleThreadExecutor= Executors.newSingleThreadExecutor();//4.创建一个单线程执行器,可以加时间给定时间后执行或者定期执行ScheduledExecutorService singleThreadScheduledExecutor= Executors.newSingleThreadScheduledExecutor();//5.创建一个指定大小的线程池,可以加时间给定时间后执行或者定期执行ScheduledExecutorService scheduledThreadpool= Executors.newScheduledThreadPool(5);//6.创建一个指定大小(不传参,为当前机器的cpu核数)的线程池,并行处理任务,不保证处理顺序Executors.newWorkStealingPool();
}
Runtime.getRuntime().availableProcessors()
获取系统的cpu核数

实现一个线程池

  • 先构思思路(先描述,再组织)
  1. 用Runable描述任务
  2. 组织管理任务可以使用一个队列,可以使用阻塞队列取实现
  3. 提供一个向队列的添加任务的方法
  4. 创建多个线程,扫描队列里面的任务,有任务时候执行,没有任务时候等待
public class MyExectorService {//定义阻塞队列阻止任务private BlockingQueue<Runnable> queue=new LinkedBlockingQueue(100);private  static Object lock=new Object();public MyExectorService(int threadNum){for (int i = 0; i < threadNum; i++){Thread thread=new Thread(()->{//不停扫描队列while (true) {try {synchronized (lock){Runnable runable=  queue.take();runable.run();}TimeUnit.MILLISECONDS.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}//take()方法会阻塞,直到队列中有任务});//启动线程thread.start();}}/*** 提交任务到线程池中* @param runnable 具体的任务* @throws InterruptedException*/public void sumbit(Runnable runnable) throws InterruptedException {if(runnable==null){throw new IllegalArgumentException("任务不能为空");}//把任务加入队列中queue.put(runnable);}}
class Test1{public static void main(String[] args) throws InterruptedException {MyExectorService myExectorService=new MyExectorService(3);AtomicInteger j= new AtomicInteger();for (int i = 0; i < 10; i++) {myExectorService.sumbit(() -> {System.out.println(Thread.currentThread().getName() + " " + j);j.getAndIncrement();});if(i%3==0){TimeUnit.SECONDS.sleep(1);}}}
}

创建系统自带的线程池

  • 前面jdk提供的线程池比较固定,也就是说我们不能自己定制,但是我们看底层代码时发现,这些线程池都是对ThreadPoolExecutor的封装
    在这里插入图片描述
  • 那我们可以根据ThreadPoolEecutor创建一个自定义线程池

在这里插入图片描述
用现实的两个例子去模拟线程工作的原理
周末去吃饭
在这里插入图片描述
银行办理业务
在这里插入图片描述

  • 线程池的拒绝策略详解
    在这里插入图片描述
    在这里插入图片描述
  • 我们注意一下,3和4是不会抛出异常的,1和2是会抛出异常的,放弃的任务永远都找不回来,所以指定拒绝策略的时候,要关注任务是不是必须要执行,如果必须要执行,就指定“返回调用者”,否则选1,3,4一个即可
public static void main(String[] args) {ThreadPoolExecutor threadPool=new ThreadPoolExecutor(2,5,10, TimeUnit.SECONDS,new LinkedBlockingQueue<>(7),new ThreadPoolExecutor.AbortPolicy());for (int i = 0; i < 100; i++) {int takeI=i;threadPool.submit(new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+" 执行任务 "+takeI);}});}}
  • 直接拒绝
    实际只执行几个后面的都没执行
    在这里插入图片描述
    在这里插入图片描述
  • 返回给调用者
    有一部分代码返回给调用者main执行了
  public static void main(String[] args) {ThreadPoolExecutor threadPool=new ThreadPoolExecutor(2,5,10, TimeUnit.SECONDS,new LinkedBlockingQueue<>(7),new ThreadPoolExecutor.CallerRunsPolicy());for (int i = 0; i < 100; i++) {int takeI=i;threadPool.submit(new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+" 执行任务 "+takeI);}});}}

在这里插入图片描述

  • 放弃最早的任务
public static void main(String[] args) {ThreadPoolExecutor threadPool=new ThreadPoolExecutor(2,5,10, TimeUnit.SECONDS,new LinkedBlockingQueue<>(7),new ThreadPoolExecutor.DiscardOldestPolicy());for (int i = 0; i < 100; i++) {int takeI=i;threadPool.submit(new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+" 执行任务 "+takeI);}});}}

在这里插入图片描述

  • 放弃最新的任务
public static void main(String[] args) {ThreadPoolExecutor threadPool=new ThreadPoolExecutor(2,5,10, TimeUnit.SECONDS,new LinkedBlockingQueue<>(7),new ThreadPoolExecutor.DiscardPolicy());for (int i = 0; i < 100; i++) {int takeI=i;threadPool.submit(new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+" 执行任务 "+takeI);}});}}

在这里插入图片描述

wait和sleep的区别

1.共同点,让线程休眠一会
2.从实现使用上来说是两种不同的方法
wait是Object类的方法,与锁相关,配合sychronized一起使用,调用wait之后会释放锁
sleep是Thread类的方法,与锁无关
wait可以通过notify和指定直线的方法唤醒,唤醒之后重新竞争锁资源
sleep只能通过超时时间唤醒

  • 补充
    在这里插入图片描述

相关文章:

JavaEE 【知识改变命运】05 多线程(4)

文章目录 单例模式什么是单例模式饿汉模式懒汉模式多线程- 懒汉模式分析多线程问题第一种添加sychronized的方式第二种添加sychronized的方式改进第二种添加sychronized的方式&#xff08;DCL检查锁&#xff09; 阻塞队列什么是阻塞队列什么是消费生产者模型标准库中的阻塞队列…...

【CSS in Depth 2 精译_076】12.4 @font-face 的工作原理

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第四部分 视觉增强技术 ✔️【第 12 章 CSS 排版与间距】 ✔️ 12.1 间距设置 12.1.1 使用 em 还是 px12.1.2 对行高的深入思考12.1.3 行内元素的间距设置 12.2 Web 字体12.3 谷歌字体12.4 font-fac…...

SQL Having用法

拿个业务场景说这个案例&#xff0c;比如我们有个表里面可能有批改过的数据&#xff0c;批改过得数据不会随着新批改的数据覆盖&#xff0c;而是逐条插入表中&#xff0c;如果想找出包含最早批改的数据和最新批改数据的话&#xff0c;那么我们就需要用到了havinng 用法,假设最开…...

@JsonNaming实现入参接口参数下划线驼峰自动转换

JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) 是用于 Jackson 库中的一个注解&#xff0c;作用是改变 Java 对象的字段命名策略&#xff0c;特别是在序列化和反序列化时。这可以帮助 Java 对象中的字段名从驼峰命名法&#xff08;CamelCase&#xff09;转换为蛇…...

使用PaliGemma2构建多模态目标检测系统:从架构设计到性能优化的技术实践指南

目标检测技术作为计算机视觉领域的核心组件&#xff0c;在自动驾驶系统、智能监控、零售分析以及增强现实等应用中发挥着关键作用。本文将详细介绍PaliGemma2模型的微调流程&#xff0c;该模型通过整合SigLIP-So400m视觉编码器与Gemma 2系列的高级语言模型&#xff0c;专门针对…...

MinerU:PDF文档提取工具

目录 docker一键启动本地配置下载模型权重文件demo.pyGPU使用情况 wget https://github.com/opendatalab/MinerU/raw/master/Dockerfile docker build -t mineru:latest .docker一键启动 有点问题&#xff0c;晚点更新 本地配置 就是在Python环境中配置依赖和安装包 根据需求…...

spark的共享变量

因为RDD在spark中是分布式存储 1、python中定义的变量仅仅在driver中运行&#xff0c;在excutor中是获取不到值的——广播变量 2、若定义了一个变量进行累加&#xff0c;先分别在driver和excutor中进行累加&#xff0c;但是结果是不会主动返回给driver的——累加器 Broadcas…...

Scrapy与MongoDB

Scrapy可以在非常短的时间里获取大量的数据。这些数据无论是直接保存为纯文本文件还是CSV文件&#xff0c;都是不可取的。爬取一个小时就可以让这些文件大到无法打开。这个时候&#xff0c;就需要使用数据库来保存数据了。 MongoDB由于其出色的性能&#xff0c;已经成为爬虫的首…...

爬虫基础与实践

爬虫技术基础与实践 在当今数字化的时代&#xff0c;数据成为了宝贵的资源。爬虫技术作为获取数据的重要手段&#xff0c;受到了广泛的关注和应用。本文将介绍爬虫的基本概念、工作原理以及一些常用的技术和工具。 一、爬虫的基本概念 爬虫&#xff0c;也称为网络蜘蛛或网络机器…...

快速上手Serverless架构与FastAPI结合实现自动化移动应用后端

快速上手Serverless架构与FastAPI结合实现自动化移动应用后端 引言 随着云计算技术的发展&#xff0c;Serverless架构已经成为构建现代应用的一种流行选择。它允许开发者将更多精力集中在核心业务逻辑上&#xff0c;而无需管理底层基础设施。本文将以AWS Lambda和API Gateway…...

ansible自动化运维(二)playbook模式详解

一.Ansible中的playbook模式 Playbook不同于使用单个模块操作远程服务器&#xff0c;Playbook的功能更加强大。如果说单个模块执行类似于Linux系统中的命令&#xff0c;那么Playbook就类似于shell脚本&#xff0c;将多个模块组合起来实现一组的操作。 Playbook还是会用到ad-h…...

基于Springboot社团管理系统【附源码】

基于Springboot社团管理系统 效果如下&#xff1a; 系统登录页面 用户管理页面 社团信息管理页面 社团活动管理页面 经费信息管理页面 新闻信息管理页面 系统主页面 社团信息页面 研究背景 在当今高校与社区环境中&#xff0c;学生社团蓬勃发展&#xff0c;成为学生课余生活…...

CSS:html中,.png的动态图,怎么只让它显示部分,比如只显示右上部分的,或右边中间部分

目录 背景 方法 1: 使用 background-image 和 background-position 示例代码 解释 方法 2: 使用 clip-path 裁剪图像 示例代码 解释 方法 3: 使用 object-fit 和 overflow 示例代码 解释 示例 总结 背景 在HTML中,如果你有一个 .png 的动态图(例如一个 GIF 动画或…...

解读CVPR2024-论文分享|RepViT: Revisiting Mobile CNN From ViT Perspective

论文标题 RepViT: Revisiting Mobile CNN From ViT Perspective 论文链接&#xff1a; https://arxiv.org/abs/2307.09283 论文作者 Ao Wang, Hui Chen, Zijia Lin, Jungong Han, Guiguang Ding 内容简介 这篇论文探讨了在资源受限的移动设备上&#xff0c;轻量级视觉变…...

linux部署安装wordpress

一、环境准备 首先我们先介绍下环境和实验中所需要的包 环境&#xff1a; 我使用的是centos7.6的系统 建议关掉selinux和影响到80端口的防火墙策略 selinux永久有效 修改 /etc/selinux/config 文件中的 SELINUX"" 为 disabled &#xff0c;然后重启。 selinux即…...

[Java] 配置Powershell 的 Maven 环境变量

目录 前言单独为 Powershell 设置 Maven 环境变量 前言 安装使用 maven 的时候发现&#xff0c;明明已经配置好了环境变量。但是在 powershell 中还是无法识别 mvn 命令。原来这货需要另外配置。 单独为 Powershell 设置 Maven 环境变量 要在 PowerShell 中永久配置 Maven 环…...

Android -- [SelfView] 自定义弹窗式颜色选择器

Android – [SelfView] 自定义弹窗式颜色选择器 PS: 1. 弹框式显示&#xff1b; 2. 支持透明度设置&#xff1b; 3. 支持拖动控件选择颜色&#xff1b; 4. 支持 ARGB | HEX 数值填写预览颜色并返回&#xff1b; 5. 输出支持Hex 和 Int 两种格式&#xff1b;效果 使用方法&…...

vue-echarts高度缩小时autoresize失效

背景 项目中采用动态给x-vue-echarts style赋值width&#xff0c;height的方式实现echarts图表尺寸的改变 <v-chart...autoresize></v-chart>给v-chart添加autoresize后&#xff0c;在图表宽度变化&#xff0c;高度增加时无异常&#xff0c;高度减小时图表并未缩…...

rabbitMq的rabbitmqctl status报错

Error: unable to perform an operation on node rabbitASUS-PC. Please see diagnostics information and suggestions below. 遇到上图这个错大部分问题可能是由于 RabbitMQ CLI 工具的 Erlang Cookie 与服务器上的不匹配而导致连接问题。Erlang Cookie 在 RabbitMQ 节点之间…...

linux c++ uuid编译时的问题

linux c uuid编译时的问题 写在前面可能编译过和不能编译过的可以编译和link过的不能编译过的 写在前面 几次翻车与uuid相关&#xff0c;超出我认知。 所以&#xff0c;把一些遇到的相关问题写在这里。 可能编译过和不能编译过的 可以编译和link过的 cmake_minimum_require…...

【STM32】RTT-Studio中HAL库开发教程九:FLASH中的OPT

文章目录 一、概要二、内部FLASH排布三、内部FLASH主要特色四、OTP函数介绍五、测试验证 一、概要 STM32系列是一款强大而灵活的微控制器&#xff0c;它的片内Flash存储器可以用来存储有关代码和数据&#xff0c;在实际应用中&#xff0c;我们也需要对这个存储器进行读写操作。…...

[SWPUCTF 2021 新生赛]crypto9

[MoeCTF 2021]Web安全入门指北—GET 意思是GET传参&#xff0c;moeflag 就可以得到falg 输入?moeflag flag为&#xff1a; NSSCTF{ff26110b-8793-403c-990e-15c7f1820596} [SWPUCTF 2021 新生赛]crypto9 #gpt写的代码 from itertools import product letter_list ABCDEFG…...

vue中常用的指令

v - if 指令 功能详细解释 它是一种真正的条件渲染指令。在 Vue 实例初始化以及数据更新过程中&#xff0c;Vue.js 会对v - if指令中的表达式进行求值。这个表达式可以是简单的布尔变量&#xff0c;也可以是一个复杂的计算表达式&#xff0c;只要最终结果是布尔值就行。当表达式…...

Docker Compose实战三:轻松部署PHP

通过前面的文章&#xff08;Docker Compose基础语法与MySQL部署&#xff09;&#xff0c;你已经掌握了Docker Compose的基本语法和常用指令&#xff0c;并成功部署了一个MySQL数据库服务器。今天&#xff0c;我们将继续深入探索Docker Compose的强大功能&#xff0c;介绍如何使…...

数据分析实战—房价特征关系

1.实战内容 &#xff08;1&#xff09; 读取房价特征关系表&#xff08;house_price.npz&#xff09;绘制离地铁站的距离与单位面积的房价的散点图&#xff0c;并对其进行分析&#xff1b; import pandas as pd import numpy as np import warnings warnings.filterwarnings(&…...

云和恩墨 zCloud 与华为云 GaussDB 完成兼容性互认证

近日&#xff0c;云和恩墨&#xff08;北京&#xff09;信息技术有限公司&#xff08;以下简称&#xff1a;云和恩墨&#xff09;的多元数据库智能管理平台 zCloud 与华为云计算技术有限公司&#xff08;以下简称&#xff1a;华为云&#xff09;的 GaussDB 数据库完成了兼容性互…...

【大语言模型LangChain】 ModelsIO OutputParsers详解

【大语言模型LangChain】 ModelsIO OutputParsers详解 一、简介二、OutputParsers 的优势三、解析器类型四、实战示例1、String 解析器2、Json 解析器3、Pydantic 解析器4、结构化输出解析器5、OpenAI 函数输出解析器5.1、JsonOutputFunctionsParser5.2、JsonKeyOutputFunction…...

PaddleSpeech本地部署文档

windows安装paddlespeech步骤&#xff1a; 1. 安装vs c编译环境 对于 Windows 系统&#xff0c;需要安装 Visual Studio 来完成 C 编译环境的安装。 Microsoft C Build Tools - Visual Studio 2. 安装conda conda create -y -p paddlespeech python3.8 conda activate pad…...

Android 第三方框架:RxJava:源码分析:责任链模式

文章目录 责任链模式RxJava中的责任链总结 责任链模式 RxJava中的责任链 链式调用的使用过程中形成了两个单向链表 第一个单向链表是Observable链表 它的形成过程&#xff1a; 1.首先调用Observable的静态方法创建第一个Observable对象&#xff0c;作为Observable链表的表…...

网络安全 与 加密算法

计算机中的网络安全 在本篇中介绍了以下几个方面: 机密性 密码学 对称加密算法(DES, 3DES, AES) 公开秘钥算法 RSA大素数的获取 完整性 散列函数(MD5, SHA-1, 并没有提及算法实现) 报文鉴别(MAC) 数字签名 端点鉴别 应用 SSL(TCP网络安全) 运行时安全 防火墙的基本知识 …...

网站开发技术协议怎么写/seo搜索引擎优化平台

图解CSS padding、margin、border属性 W3C组织建议把所有网页上的对像都放在一个盒(box)中&#xff0c;设计师可以通过创建定义来控制这个盒的属性&#xff0c;这些对像包括段落、列表、标题、图片以及层。盒模型主要定义四个区域&#xff1a;内容(content)、内边距(padding)、…...

安平县哪里做网站/日本积分榜最新排名

最近发现有些读者通过将我的教程进行二次出售来获取暴利&#xff0c;我在此对其表示强烈的谴责 朋友们, 如需转载请标明出处&#xff1a;www.captainbed.net 总目录&#xff08;请务必点击总目录从前言看起&#xff0c;这样才能充分理解本篇文章&#xff09; 什么是人工智能&am…...

官方网站建设有限公司/新闻联播直播 今天

通过浏览器访问互联网上的页面&#xff0c;实际是把页面下载并缓存到本地电脑中。页面是有大小的&#xff0c;从互联网到本地电脑&#xff0c;就存在数据流动&#xff0c;从而产生了流量。例如&#xff0c;一个页面&#xff0c;有500kb大小&#xff0c;一次受访&#xff0c;则消…...

海安公司网站建设/百度应用市场app下载

如何智能补全&#xff0c;忽略大小写&#xff1f; 设置之后效果如图&#xff1a; 如何在py文件中设置头部信息&#xff1f; 设置后效果如图&#xff1a; 常用快捷键 ctrlf 搜索 ctrlz 撤销 ctrlq 查看文档 ctrlshiftz 反撤销 ctrl/ 注释 ctrld 复制粘贴选中(复制粘贴复制一行) …...

zblog和wordpress 评测/网站友情链接的好处

这是最终版本 def zuixiao(x,y):r[]k2while True:if (x%k)0 and (y%k0):r.append(k)xx/kyy/kelse:k1if k>x or k>y:breakcm1for i in r:cmcm*ijuggcm*x*yreturn int(jugg)...

有域名了 怎么做网站/seo建设

如果你频繁的在你的系统中安装/卸载&#xff0c;那么不时的清理一下你的系统是十分必要的。 在Ubuntu终端中执行如下命令&#xff1a;sudo apt-get autoremove屏幕输出是这个样子的&#xff1a; Reading package lists… DoneBuilding dependency treeReading state informatio…...