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

Java基础27,28(多线程,ThreadMethod ,线程安全问题,线程状态,线程池)

目录

一、多线程

1. 概述

2. 进程与线程

2.1 程序

2.2 进程

2.3 线程

2.4 进程与线程的区别

3. 线程基本概念

4.并发与并行

5. 线程的创建方式

方式一:继承Thread类

 方式二:实现Runable接口

方式三:实现Callable接口

方式四:线程池

小结:

二、ThreadMethod 

1. Thread类常用的构造方法

2. 线程的命名 

3. 获取当前线程

4. 线程的休眠(暂停)

5. 线程的优先级

6. 守护线程(Daemon Thread)

用户线程与守护线程的区别

设置守护线程

7. 线程的让出:yield( )方法

8. 线程的插队:join( )方法 

9. 小结

三、线程安全问题

多线程售票问题

解决方案1

解决方案2

解决方案3

四、线程状态

1. New:新建状态

2. Runnable:运行状态

3. Terminated:终止状态

4. Blocked:阻塞状态

5. Timed Waiting:计时等待状态

6.  Waiting:等待状态

五、线程池

1. 什么是线程池?

2. 线程池常用类和接口

3. 线程池常见方法

执行线程任务

4. 线程池分类总结

FixedThreadPool

CachedThreadPool

SingleThreadExecutor

ScheduledThreadPool

5. 线程池的配置参数

6. 线程池的执行流程

7. 线程池的状态


一、多线程

1. 概述

现代操作系统(Windows,macOS,Linux)都可以执行多任务。多任务就是同时运行多个任务。例如:播放音乐的同时,浏览器可以进行文件下载,同时可以进行QQ消息的收发。
CPU执行代码都是一条一条顺序执行的,但是,即使是单核CPU,也可以同时运行多个任务。因为操作系统执行多任务实际上就是让CPU对多个任务轮流交替执行。
操作系统轮流让多个任务交替执行,例如,让浏览器执行0.001秒,让QQ执行0.001秒,再让音乐播放器执行0.001秒。在用户使用的体验看来,CPU就是在同时执行多个任务。

2. 进程与线程

2.1 程序

程序是含有指令和数据的文件,被存储在磁盘或其他的数据存储设备中,可以理解为程序是包含静态代码的文件。例如:浏览器软件、音乐播放器软件等软件的安装目录和文件。

2.2 进程

进程是程序的一次执行过程,是系统运行程序的基本单位。在Windows系统中,每一个正在执行的exe文件或后台服务,都是一个进程,由操作系统统一管理并分配资源,因此进程是动态的。 例如:正在运行中的浏览器就是一个进程,正在运行中的音乐播放器是另一个进程,同理,正在运行中的QQ和WPS等都是进程。
操作系统运行一个程序,即是一个进程从创建,运行到消亡的过程。简单来说,一个进程就是一个执行中的程序,它在计算机中一个指令接着一个指令地执行着, 同时,每个进程还占有某些系统资源如 CPU时间,内存空间,文件,输入输 出设备的使用权等。

2.3 线程

某些进程内部还需要同时执行多个子任务。例如,我们在使用WPS时,WPS可以让我们一边打字,一边进行拼写检查,同时还可以在后台进行自动保存和上传云文档,我们把子任务称为线程。线程是进程划分成的更小的运行单位。
进程和线程的关系就是:一个进程可以包含一个或多个线程,但至少会有一个主线程。

2.4 进程与线程的区别

  • 根本区别:进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位;
  • 资源开销:每个进程都有独立的代码副本和数据空间,进程之间的切换,资源开销较大;线程可以看做轻量级的进程,每个线程都有自己独立的运行栈和程序计数器,线程之间切换,资源开销小;
  • 包含关系:一个进程内包含有多个线程,在执行过程,线程的执行不是线性串行的,而是多条线程并行共同完成;
  • 内存分配:同一进程内的所有线程共享本进程的内存空间和资源;进程之间的内存空间和资源相互独立;
  • 影响关系:一个进程崩溃后,在保护模式下不会对其他进程产生影响;一个线程崩溃,会导致整个进程退出。所以多进程要比多线程健壮;
  • 执行过程:每个独立的进程有程序运行的入口和程序出口。但是线程不能独立执行,必须依存在应用程序(进程)中,由应用程序提供多个线程执行控制;

3. 线程基本概念

单线程:单线程就是进程中只有一个线程。单线程在程序执行时,所走的程序路径按照连续顺序排下来,前面的必须处理好,后面的才会执行。

public static void main(String[] args) {int a =10;int b =20;int c=a+b;System.out.println(c);}

多线程:由一个以上的线程组成的程序称为多线程程序。Java中,一定是从主线程开始执行(main方法),然后在主线程的某个位置创建并启动新的线程。

作用:最主要是为了提高程序的运行效率
多线程应用场景:软件中耗时的操作  拷贝,迁移文件,加载大量的时候


为什么要有多线程?
线程:操作系统能够进行调度的最小单元,他被包含在进程中,是进程中实际运行的单位
进程:进程是程序的基本执行实体

4.并发与并行

并发:在同一时刻,有多个指令在单个Cpu上交替执行
并行:在同一时刻,有多个指令在单个Cpu上同时执行

5. 线程的创建方式

方式一:继承Thread类

继承 java.lang.Thread 类(线程子类)

创建多线程的方法1:

  1. 自定义一个类继承Thread
  2. 重写run方法
  3. 创建子类的对象,并启动线程

 MyThread类:

public class MyThread extends Thread {public MyThread() {super();}@Overridepublic void run() {//写线程要执行的代码for (int i = 0; i < 100; i++) {System.out.println(getName()+"_hello");}}
}

Demo01类:

public class Demo01 {public static void main(String[] args) {/***创建多线程的方法1:*1.自定义一个类继承Thread*2.重写run方法*3.创建子类的对象,并启动线程*/MyThread t1 = new MyThread();t1.setName("线程1");//给线程设置名称t1.start();//此处需要注意,不能直接调用run方法,否则就是对象的创建和方法的调用MyThread t2 = new MyThread();t2.setName("线程2");t2.start();for (int i = 0; i < 15; i++) {System.out.println(Thread.currentThread().getName()+"_world");}}
}

 方式二:实现Runable接口

实现 java.lang.Runnable 接口(线程执行类)

创建多线程的方法2:

  1. 自己定义一个类实现Runable接口
  2. 重写里面的run方法
  3. 创建自己的类的对象
  4. 创建Thread类的对象,并开启线程

MyRun类:

public class MyRun implements Runnable {@Overridepublic void run() {// 写线程要执行的代码for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName() + "_hello");}}
}

Demo02类:

public class Demo02 {public static void main(String[] args) {/*** 1.自己定义一个类实现Runable接口 2.重写里面的run方法 3.创建自己的类的对象 4.创建Thread类的对象,并开启线程*/// 创建MyRun多线程,标识多线程要执行的任务MyRun run = new MyRun();// 创建线程Thread t1 = new Thread(run);Thread t2 = new Thread(run);t1.setName("线程1");t2.setName("线程2");t1.start();t2.start();}
}

方式三:实现Callable接口

实现 java.util.concurrent.Callable 接口,允许子线程返回结果、抛出异常

创建多线程的方法3:

  1. 创建Callable是实现类
  2. 重写ca11方法 
  3. 创建Mycal1实现类的对象 
  4. 创建FutureTask的对象(管理多线程运行的结果)
  5. 创建Thread对象,并启动

MyCall类:

public class MyCall implements Callable<Integer>{//求1-100之间的和@Overridepublic Integer call() throws Exception {int sum = 0;for (int i = 1; i <= 100; i++) {sum+=i;}return sum;}}

Demo03类:

public class Demo03 {public static void main(String[] args) throws InterruptedException, ExecutionException {/*** 1.创建Callable是实现类* 2.重写ca11方法 * 3.创建Mycal1实现类的对象 * 4.创建FutureTask的对象(管理多线程运行的结果)* 5.创建Thread对象,并启动*/MyCall call1 = new MyCall();FutureTask<Integer> ft = new FutureTask<Integer>(call1);Thread t1 = new Thread(ft);t1.start();//获取多线程运行后的结果Integer result = ft.get();System.out.println(result);

方式四:线程池

线程池(见五、线程池)

小结:

继承Thread类          

优点:编程比较简单,可以直接使用Thread类中的方法,      

缺点:可扩展性比较差,不能再继承其他的类
实现Runable接口    

优点:扩展性强,实现该接口后还可以同时继承其他类,实现其他的接口    

缺点:编程相对比较复杂,不能直接使用Thread类中的方法
实现Callable接口    

优点:有返回值,可以利用FutureTask进行结果的保存          

缺点:编程相对比较复杂,不能直接使用Thread类中的方法    

  

注意:直接调用Thread实例的run()方法是无效的,因为直接调用run()方法,相当于调用了一个普通的Java方法,当前线程并没有任何改变,也不会启动新线程。                         

二、ThreadMethod 

1. Thread类常用的构造方法

public class MyThread extends Thread {public MyThread() {super();}public MyThread(String name) {super(name);}@Overridepublic void run() {for (int i = 0; i < 20; i++) {try {sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(getName() + "@" + i);}}
}
Thread类常用的构造方法:
Thread()
Thread(String name)
Thread(Runnable run,String name,
Thread(Runnable run)
MyThread t1 = new MyThread();
// 通过构造方法创建线程MyThread t2 = new MyThread("线程2");

2. 线程的命名 

生产环境中,为了排查问题方便,建议在创建线程的时候指定一个合理的线程名字

  • 调用父类的setName()方法或在构造方法中给线程名字赋值;
// getName():获取线程名称// 细节:如果给线程没有设置名称,线程也有默认的名称 Thread-x(从0开始)// setName()设置线程的名称构造方法也可以设置线程的名称MyThread t1 = new MyThread();t1.setName("线程1");
  • 如果没有为线程命名,系统会默认指定线程名,命名规则是Thread-N的形式

3. 获取当前线程

Thread.currentThread()

// Thread.currentThread()获取当前线程对象Thread tmain = Thread.currentThread();tmain.setName("核心线程");for (int i = 0; i < 20; i++) {System.out.println(tmain.getName() + "@" + i);}

4. 线程的休眠(暂停)

在线程中,可以通过调用Thread.sleep(long millis),强迫当前线程按照指定毫秒值休眠。

public class Main {public static void main(String[] args) {System.out.println("main start...");Thread t = new Thread() {public void run() {System.out.println("thread run...");try {Thread.sleep(10); // 子线程休眠10毫秒} catch (InterruptedException e) {}System.out.println("thread end.");}};t.start();try {Thread.sleep(20); // 主线程休眠20毫秒} catch (InterruptedException e) {}System.out.println("main end...");}
}

5. 线程的优先级

  • 在线程中,通过setPriority(int n)设置线程优先级,范围是1-10,默认为 5
  • 优先级高的线程被操作系统调度的优先级较高(操作系统对高优先级线程,调度更频繁)
  • 注意:并不能代表,通过设置优先级来确保高优先级的线程一定会先执行
  • 案例:
    public static void main(String[] args) {//创建一个匿名内部类对象runRunnable run = new Runnable() {@Overridepublic void run() {for (int i = 0; i < 20; i++) {System.out.println(Thread.currentThread().getName() + "@" + i);}}};Thread t1 = new Thread(run, "线程1");Thread t2 = new Thread(run, "线程2");//获取线程的优先级:默认为5System.out.println(t1.getPriority());System.out.println("线程t2的优先级为" + t2.getPriority());System.out.println("主的优先级为:" + Thread.currentThread().getPriority());//设置优先级,优先级的取值范围1-10之间,优先级越高,抢到的线程概率越大,但并不是绝对的t1.setPriority(10);t2.setPriority(1);t1.start();t2.start();}

6. 守护线程(Daemon Thread)

用户线程与守护线程的区别

  • 用户线程:我们平常创建的普通线程;
  • 守护线程:用来服务于用户线程的线程,在JVM中,所有非守护线程都执行完毕后,无论有没有守护线程,虚拟机都会自动退出;而守护线程执行结束后,虚拟机不会自动退出。

设置守护线程

守护线程:备胎线程--setDaemon(true)
        细节:当其他的非守护的线程执行完毕后,守护线程会陆续结束
        即(当女神线程结束,那么备胎线程也没有存在的必要)

在调用start()方法前,调用setDaemon(true)把该线程标记为守护线程

public static void main(String[] args) {//守护线程:备胎线程--setDaemon(true)//细节:当其他的非守护的线程执行完毕后,守护线程会陆续结束//即(当女神线程结束,那么备胎线程也没有存在的必要)//创建线程1Thread t1 = new Thread() {@Overridepublic void run() {for (int i = 1; i <=10; i++) {System.out.println(getName()+"@"+i);}};};//创建线程2Thread t2 = new Thread() {@Overridepublic void run() {for (int i = 1; i <=100; i++) {System.out.println(getName()+"@"+i);}};};//给线程设置名称t1.setName("女神");t2.setName("备胎");//将第二个线程设置为守护线程t2.setDaemon(true);t1.start();t2.start();}

7. 线程的让出:yield( )方法

线程通过调用yield()方法告诉JVM的线程调度,当前线程愿意让出CPU给其他线程使用。
○至于系统是否采纳,取决于JVM的线程调度模型:分时调度模型和抢占式调度模型

  • 分时调度模型:所有的线程轮流获得 cpu的使用权,并且平均分配每个线程占用的 CPU 时间片;
  • 抢占式调度模型:优先让可运行池中优先级高的线程占用 CPU,如果可运行池中的线程优先级相同,那么就随机选择一个线程,使其占用CPU。(JVM虚拟机采用的是抢占式调度模型 )
public class MyThread1 extends Thread {//当前线程让出执行权后,再次进行抢夺也可能会抢夺到相关资源@Overridepublic void run() {for (int i = 1; i <= 100; i++) {System.out.println(getName() + "@" + i);//表示让出当前的cpu的执行权,会让结果更加均匀一点,但是并非绝对Thread.yield();//出让线程,礼让线程}}
}
public class Demo04 {public static void main(String[] args) {//创建线程1和线程2//出让线程,礼让线程Thread.yield();MyThread1 t1 = new MyThread1();MyThread1 t2 = new MyThread1();t1.setName("线程1");t2.setName("线程2");t1.start();t2.start();}
}

8. 线程的插队:join( )方法 

t.join()方法会使当前线程( 主线程 或者调用t.join()的线程 )进入等待池,并等待 线程t 执行完毕后才会被唤醒。此时,并不影响同一时刻处在运行状态的其他线程。

public class Demo05 {public static void main(String[] args) throws InterruptedException {//线程对象.join();插队线程//此时土豆线程会和main线程抢占cpu的资源//如果土豆先执行,后执行main就用到join方法MyThread2 t1 = new MyThread2();t1.setName("土豆");t1.start();//表示把t1线程插入到当前的线程前面t1.join();for (int i = 0; i <=100; i++) {System.out.println(Thread.currentThread().getName()+"@"+i);}}
}class MyThread2 extends Thread {@Overridepublic void run() {for (int i = 1; i <= 100; i++) {System.out.println(getName() + "@" + i);}}
}

9. 小结

Thread线程的方法

  • getName()--有默认的线程名
  • setName()--给线程设置名称,构造方法也可以设置线程名
  • Thread.currentThread(),获取当前线程
  • getPriority() 获取线程的优先级1-10,默认的优先级为5
  • setPriority(int) 数字越大优先级越大
  • sleep(long mill)   睡眠
  • join()  插队线程
  • yield() 礼让线程
  • setDaemon(boolean) 守护线程

三、线程安全问题

多线程售票问题

public class MyThread extends Thread {//表示当前的变量提升到了类级别,所有的类共享此成员变量static int ticket = 0;@Overridepublic void run() {// 卖票while (true) {if (ticket < 10) {try {Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}ticket++;System.out.println(getName() + "正在卖出第" + ticket + "张票");} else {break;}}}
}
public class Demo01 {public static void main(String[] args) {//线程在进行执行的过程中,cpu的执行权随时都可能被其他线程给抢走MyThread t1 = new MyThread();MyThread t2 = new MyThread();MyThread t3 = new MyThread();t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");//线程启动开始卖票t1.start();t2.start();t3.start();}
}

当多个线程同时运行时,线程的调度由操作系统决定,程序本身无法决定。因此,任何一个线程都有可能在任何指令处被操作系统暂停,然后在某个时间段后继续执行。
这个时候,一个在单线程模型下不存在的问题就会发生:如果多个线程同时读写共享变量,会出现数据不一致的问题。

解决方案1

解放方案1:锁代码块:

synchronized(锁对象){//锁对象可以是任意类型对象,但是必须要保证多个线程使用同一个锁操作的共享的数据资源}

默认下,锁是打开装填,有一个线程进来,锁自动关闭
里面的代码执行完毕,线程出来,锁自动打开

public class MyThread extends Thread {// 表示当前的变量提升到了类级别,所有的类共享此成员变量static int ticket = 0;//注意事项:锁对象要唯一static Object obj = new Object();@Overridepublic void run() {// 卖票while (true) {synchronized (obj) {if (ticket < 1000) {try {Thread.sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}ticket++;System.out.println(getName() + "正在卖出第" + ticket + "张票");} else {break;}}}}
}

解决方案2

解放方案2:锁方法:

修饰符 synchronized 返回值类型 方法名(){括号内写的是共享的资源
}

1.特点:同步方法锁锁住的方法中的代码
2.特点:方法上面的锁不能自己指定,
            实例方法:通过当前对象,作为“锁”
            静态方法:通过当前类class字节码对象,作为“锁

public class MyThread extends Thread {// 表示当前的变量提升到了类级别,所有的类共享此成员变量static int ticket = 0;@Overridepublic void run() {// 卖票while (true) {if (Method()) {break;}}}public static synchronized boolean Method() {if (ticket < 100) {try {Thread.sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}ticket++;System.out.println(Thread.currentThread().getName() + "正在卖出第" + ticket + "张票");} else {return true;}return false;}
}

public class Demo02 {public static void main(String[] args) {//MyRun作为Thread线程的参数进行传值MyRun run =new MyRun();Thread t1 = new Thread(run,"线程1");Thread t2 = new Thread(run,"线程2");Thread t3 = new Thread(run,"线程3");t1.start();t2.start();t3.start();}
}
class MyRun implements Runnable{static int ticket =0;@Overridepublic void run() {while (true) {if (Method()) {break;}}}//此处锁对象为当前的对象public static synchronized boolean Method() {if (ticket < 10000) {try {Thread.sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}ticket++;System.out.println(Thread.currentThread().getName() + "正在卖出第" + ticket + "张票");} else {return true;}return false;}
}

解决方案3

解放方案3:Lock锁  1.5:

可以手动开锁关锁
lock();加锁
unlock();解锁

//Lock锁  1.5
//可以手动开锁关锁
//lock();加锁
//unlock();解锁public class MyThread extends Thread {// 表示当前的变量提升到了类级别,所有的类共享此成员变量static int ticket = 0;static Lock lock = new ReentrantLock();@Overridepublic void run() {while (true) {lock.lock();try {if (ticket < 100) {ticket++;System.out.println(getName() + "正在卖出第" + ticket + "张票");} else {break;}} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}finally {lock.unlock();}}}}

四、线程状态

在Java程序中,一个线程对象通过调用start()方法启动线程,并且在线程获取CPU时,自动执行run()方法。run()方法执行完毕,代表线程的生命周期结束。
●在整个线程的生命周期中,线程的状态有以下6种:

  • New:新建状态,新创建的线程,此时尚未调用start()方法;
  • Runnable:运行状态,运行中的线程,已经调用start()方法,线程正在或即将执行run()方法;
  • Blocked:阻塞状态,运行中的线程,在等待竞争锁时,被阻塞,暂不执行;
  • Waiting:等待状态,运行中的线程,因为join()等方法调用,进入等待;
  • Timed Waiting:计时等待状态,运行中的线程,因为执行sleep(等待毫秒值)join(等待毫秒值)等方法,进入计时等待;
  • Terminated:终止状态,线程已终止,因为run()方法执行完毕。

●当线程启动后,它可以在Runnable、Blocked、Waiting和Timed Waiting这几个状态之间切换,直到最后变成Terminated状态,线程终止

线程终止的原因有:
○线程正常终止:run()方法执行到return语句返回;
○线程意外终止:run()方法因为未捕获的异常导致线程终止;
○对某个线程的Thread实例调用stop()方法强制终止(宇宙超级无敌强烈不推荐);

1. New:新建状态

新建状态:线程对象创建,还没有调用start方法的时候

//新建状态:线程对象创建,还没有调用start方法的时候
public class NewState {public static void main(String[] args) {Thread thread = new Thread() {@Overridepublic void run() {System.out.println("正在执行某件任务");};};System.out.println(thread.getState());//NEW}
}

2. Runnable:运行状态

线程对象调用start()方法,处于等待CPU分配资源,running ready

//线程对象调用start()方法,处于等待CPU分配资源,running ready
public class RunState {public static void main(String[] args) {Thread thread = new Thread() {@Overridepublic void run() {System.out.println("正在执行某件任务");};};thread.start();System.out.println(thread.getState());// RUNNABLE}
}

3. Terminated:终止状态

TERMINATED操作系统注销此

//TERMINATED操作系统注销此
public class TerminatedState {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread() {@Overridepublic void run() {System.out.println("正在执行某件任务");};};thread.start();Thread.sleep(1000);System.out.println(thread.getState());//TERMINATED}
}

4. Blocked:阻塞状态

运行中的线程,在等待竞争锁时,被阻塞,暂不执行

public class BlockedState {public static void main(String[] args) throws InterruptedException {Runnable run = new Runnable() {@Overridepublic void run() {synchronized (this) {//死循环while (true);}}};//创建线程t1和t2,并启动线程Thread t1 = new Thread(run, "t1");Thread t2 = new Thread(run, "t2");t1.start();t2.start();//让主线程睡眠1秒,目的想让t1和t2同时竞争一个锁Thread.sleep(1000);System.out.println("t1 state" + t1.getState());System.out.println("t2 state" + t2.getState());}
}

5. Timed Waiting:计时等待状态

运行中的线程,因为执行sleep(等待毫秒值)join(等待毫秒值)等方法,进入计时等待;

//	wait()
//	notify()
//	notifyA11()
//注意事项:
//1.要和synchronized写在一起
//2.锁对象要和调用的对象保持一致//TIMED WAITING计时等待
//执行sleep(1ong millis)等方法	不会释放锁资源
//执行wait(long millis)等方法	会释放所资源
// join(等待时间)
public class TimeWaitingState {public static void main(String[] args) throws InterruptedException {Runnable run = new Runnable() {@Overridepublic void run() {synchronized (this) {System.out.println(Thread.currentThread().getName());try {
//						Thread.sleep(5000);this.wait(5000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}};Thread t1 = new Thread(run, "t1");Thread t2 = new Thread(run, "t2");// 期待线程,观察sleep方法和wait方法对比t1.start();t2.start();// 让当前线程睡眠1秒Thread.sleep(1000);// 获取线程状态System.out.println(t1.getState());System.out.println(t2.getState());}
}

//Object 类
//	wait(long)
//	wait()//TIMED WAITING计时等待
//执行sleep(long millis)等方法	不会释放锁资源
//执行wait(long millis)等方法	会释放所资源
//执行 join(等待时间)
public class TimeWaitingState02 {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread() {@Overridepublic void run() {Thread t1 = new Thread() {@Overridepublic void run() {System.out.println("t1线程");};};t1.start();//join方法会让当前的线程处于计时等待try {t1.join(500 * 1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}};};thread.start();Thread.sleep(1000);//查看thread线程的状态System.out.println(thread.getState());}
}

6.  Waiting:等待状态

运行中的线程,因为join()等方法调用,进入等待;

//WAITING 等待状态 wait()
//                join()//Oject中的wait() wait(long time)
//1.synchronized写在一起
//2.锁对象要和调用的对象保持一致
public class WaitingState01 {public static void main(String[] args) throws InterruptedException {Object obj = new Object();Thread t1 = new Thread() {public void run() {synchronized (obj) {try {obj.wait();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}};};t1.start();Thread.sleep(1000);System.out.println(t1.getState());//WAITING}
}

//Oject中的wait() wait(long time)
// 1.synchronized写在一起
// 2.锁对象要和调用的对象保持一致
public class WaitingState02 {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread() {@Overridepublic void run() {while (true);}};t1.start();t1.join();//t1加到当前的主线程的前面,主线程一直处于等待状态//获取不到,因为当前的主线程被t1线程插队,t1线程任务是个死循环,无法结束System.out.println(Thread.currentThread().getState());}
}

五、线程池

1. 什么是线程池?

线程池内部维护了若干个线程,没有任务的时候,这些线程都处于等待空闲状态。如果有新的线程任务,就分配一个空闲线程执行。如果所有线程都处于忙碌状态,线程池会创建一个新线程进行处理或者放入队列(工作队列)中等待。

线程池:线程池是一个可以复用线程的技术

2. 线程池常用类和接口

在Java标准库提供了如下几个类或接口,来操作并使用线程池:

  1. ExecutorService接口:进行线程池的操作访问;
  2. Executors类:创建线程池的工具类;
  3. ThreadPoolExecutor及其子类:封装线程池的核心参数和运行机制;

MyRunable类:

public class MyRunable implements Runnable {public MyRunable() {}public MyRunable(String name) {}@Overridepublic void run() {// TODO Auto-generated method stub}}

线程池的基本使用方式:

//线程池:线程池是一个可以复用线程的技术
//1. ExecutorService接口:进行线程池的操作访问;
//    submit()
//    execute()
//2. Executors类:创建线程池的工具类;
//		Executors.newCachedThreadPool()  创建一个线程没有上线的线程池
//3. ThreadPoolExecutor及其子类:封装线程池的核心参数和运行机制;
public class Demo01 {public static void main(String[] args) throws InterruptedException {// 创建一个线程没有上限的线程池ExecutorService pool1 = Executors.newCachedThreadPool();// 线程池提交任务pool1.execute(new MyRunable());
//		Thread.sleep(1000);pool1.execute(new MyRunable());
//		Thread.sleep(1000);pool1.execute(new MyRunable());
//		Thread.sleep(1000);pool1.execute(new MyRunable());// 销毁线程--用完后线程池给砸了pool1.shutdown();}}

3. 线程池常见方法

  • 执行无返回值的线程任务:void execute(Runnable command);
  • 提交有返回值的线程任务:Future<T> submit(Callable<T> task);
  • 关闭线程池:void shutdown(); 或 shutdownNow();
  • 等待线程池关闭:boolean awaitTermination(long timeout, TimeUnit unit);

执行线程任务

        execute()只能提交Runnable类型的任务,没有返回值,而submit()既能提交Runnable类型任务也能提交Callable类型任务,可以返回Future类型结果,用于获取线程任务执行结果。
        execute()方法提交的任务异常是直接抛出的,而submit()方法是捕获异常,当调用Future的get()方法获取返回值时,才会抛出异常。

//线程池:线程池是一个可以复用线程的技术
//1. ExecutorService接口:进行线程池的操作访问;
//  submit()--提交任务 Runable Callable
//  execute()--提交任务Runable
//2. Executors类:创建线程池的工具类;
//		Executors.newCachedThreadPool()  创建一个线程没有上限的线程池
//		Executors.newFixedThreadPool(int nThreads) 创建一个有上限的线程池
//3. ThreadPoolExecutor及其子类:封装线程池的核心参数和运行机制;public class Demo02 {public static void main(String[] args) {
//		Executors.newFixedThreadPool(int nThreads) 创建一个有上限的线程池ExecutorService pool = Executors.newFixedThreadPool(5);pool.submit(new MyRunable("任务1"));pool.submit(new MyRunable("任务2"));pool.submit(new MyRunable("任务3"));pool.submit(new MyRunable("任务4"));}}

MyCallable类:

public class MyCallable implements Callable<Integer>{private int number;public MyCallable(int number) {this.number = number;}@Overridepublic Integer call() throws Exception {int sum = 0;for (int i = 0; i <= number ; i++) {sum+=i;}return sum;}}
//线程池:线程池是一个可以复用线程的技术
//1. ExecutorService接口:进行线程池的操作访问;
//    submit()--提交任务 Runable Callable
//    execute()--提交任务Runable
//2. Executors类:创建线程池的工具类;
//		Executors.newCachedThreadPool()  创建一个线程没有上线的线程池
//		Executors.newFixedThreadPool(int nThreads) 创建一个有上线的线程池
//      核心线程数量到底配置多少:计算密集型任务: =CPU的核数+1;    IO密集型任务: CPU的核数*2
//3. ThreadPoolExecutor及其子类:封装线程池的核心参数和运行机制;public class Demo03 {public static void main(String[] args) throws InterruptedException, ExecutionException {// 创建一个有上线的线程的线程池ExecutorService pool = Executors.newFixedThreadPool(5);//测试submit的方法--传callable值Future<Integer> futureTask1 = pool.submit(new MyCallable(10));Future<Integer> futureTask2 = pool.submit(new MyCallable(100));Future<Integer> futureTask3 = pool.submit(new MyCallable(1000));System.out.println(futureTask1.get());//55System.out.println(futureTask2.get());//5050System.out.println(futureTask3.get());//500500}}

4. 线程池分类总结

FixedThreadPool

线程数固定的线程池

线程池参数:

  • 核心线程数和最大线程数一致
  • 非核心线程线程空闲存活时间,即keepAliveTime为0
  • 阻塞队列为无界队列LinkedBlockingQueue

工作机制:

  1. 提交线程任务
  2. 如果线程数少于核心线程,创建核心线程执行任务
  3. 如果线程数等于核心线程,把任务添加到LinkedBlockingQueue阻塞队列
  4. 如果线程执行完任务,去阻塞队列取任务,继续执行

使用场景: 适用于处理CPU密集型的任务,确保CPU在长期被工作线程使用的情况下,尽可能的少的分配线程,即适用执行长期的任务。

CachedThreadPool

可缓存线程池,线程数根据任务动态调整的线程池
线程池参数:

  • 核心线程数为0
  • 最大线程数为Integer.MAX_VALUE
  • 工作队列是SynchronousQueue同步队列
  • 非核心线程空闲存活时间为60秒

●工作机制:

  1. 提交线程任务
  2. 因为核心线程数为0,所以任务直接加到SynchronousQueue工作队列
  3. 判断是否有空闲线程,如果有,就去取出任务执行
  4. 如果没有空闲线程,就新建一个线程执行
  5. 执行完任务的线程,还可以存活60秒,如果在这期间,接到任务,可以继续存活下去;否则,被销毁。

使用场景: 用于并发执行大量短期的小任务。

SingleThreadExecutor

单线程化的线程池
线程池参数:

  • 核心线程数为1
  • 最大线程数也为1
  • 阻塞队列是LinkedBlockingQueue
  • 非核心线程空闲存活时间为0秒

使用场景: 适用于串行执行任务的场景,将任务按顺序执行。

ScheduledThreadPool

能实现定时、周期性任务的线程池
线程池参数:

  • 最大线程数为Integer.MAX_VALUE
  • 阻塞队列是DelayedWorkQueue
  • keepAliveTime为0

使用场景: 周期性执行任务,并且需要限制线程数量的需求场景。

//线程池:线程池是一个可以复用线程的技术
//1. ExecutorService接口:进行线程池的操作访问;
//    submit()
//    execute()
//2. Executors类:创建线程池的工具类;
//		Executors.newCachedThreadPool()  创建一个线程没有上线的线程池,如果任务执行完线程空闲60秒会被回收
//		Executors.newFixedThreadPool(int nThreads) 创建一个有上线的线程池
//		Executors.newSingleThreadExecutor()创建一个只有一个线程的线程池对象
//		Executors.newScheduledThreadPool() 创建一个线程池,可以实现在给时间延后执行或者定期执行任务
//3. ThreadPoolExecutor及其子类:封装线程池的核心参数和运行机制;public class Demo04 {public static void main(String[] args) {
//		创建只有一个线程的线程池
//		ExecutorService pool=Executors.newSingleThreadExecutor();
//		pool.submit(new MyRunable("任务1"));
//		pool.submit(new MyRunable("任务2"));//1.创建任务调度线程池对象ScheduledExecutorService pool1=Executors.newScheduledThreadPool(2);Runnable run = new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName());}};//提交任务,延时5秒执行
//		pool1.schedule(run,5,TimeUnit.SECONDS);//提交任务,延时5秒,以后每1秒执行1次pool1.scheduleAtFixedRate(run, 5, 1,TimeUnit.SECONDS);}}

5. 线程池的配置参数

●corePoolSize线程池核心线程数:也可以理解为线程池维护的最小线程数量,核心线程创建后不会被回收。大于核心线程数的线程,在空闲时间超过keepAliveTime后会被回收;

  • 在创建了线程池后,默认情况下,线程池中并没有任何线程,当调用 execute() 方法添加一个任务时,如果正在运行的线程数量小于corePoolSize,则马上创建新线程并运行这个任务。
  • IO密集计算:由于 I/O 设备的速度相对于 CPU来说都很慢,所以大部分情况下,I/O 操作执行的时间相对于 CPU 计算来说都非常长,这种场景我们一般都称为 I/O 密集型计算。最佳线程数 =CPU 核数 * [ 1 +(I/O 耗时 / CPU 耗时)]。
  • CPU密集型:CPU 密集型计算大部分场景下都是纯 CPU 计算,多线程主要目的是提升CPU利用率,最佳线程数 =“CPU 核心数 +1”。这样的话,当线程因为偶尔的内存页失效或其他原因导致阻塞时,这个额外的线程可以临时替补,从而保证 CPU 的利用率。

●maximumPoolSize线程池最大线程数:线程池允许创建的最大线程数量;(包含核心线程池数量)
●keepAliveTime非核心线程线程存活时间:当一个可被回收的线程的空闲时间大于keepAliveTime,就会被回收。

  • 当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会被回收,直到线程池中的线程数不超过corePoolSize。
  • 如果设置allowCoreThreadTimeOut = true,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;

●TimeUnit时间单位:参数keepAliveTime的时间单位;
●BlockingQueue阻塞工作队列:用来存储等待执行的任务;
●ThreadFactory线程工厂 : 用于创建线程,以及自定义线程名称,需要实现ThreadFactory接口;
●RejectedExecutionHandler拒绝策略:当线程池线程内的线程耗尽,并且工作队列达到已满时,新提交的任务,将使用拒绝策略进行处理;

  • ThreadPoolExecutor.AbortPolicy:默认策略,丢弃任务并抛出RejectedExecutionException异常;
  • ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常;
  • ThreadPoolExecutor.DiscardOldestPolicy:丢弃工作队列中的队头任务(即最旧的任务,也就是最早进入队列的任务)后,继续将当前任务提交给线程池;
  • ThreadPoolExecutor.CallerRunsPolicy:由原调用线程处理该任务 (谁调用,谁处理);

MyRun 类:

public class MyRun implements Runnable {private String name;public MyRun() {}public MyRun(String name) {this.name = name;}@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+ name + "======>" + "正在执行");// 让任务稍微慢点,便于观察结果try {Thread.sleep(Integer.MAX_VALUE);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
//线程池的创建方式2:
//    new ThreadPoolExecutorpublic class Demo01 {public static void main(String[] args) {
//		corePoolSize:线程核心数量
//		maximumPoolSize:线程池中最大的线程数
//		keepAliveTime:临时线程的存活时间
//		unit,时间单位(时,分,秒)
//		workQueue, 任务队列
//		threadFactory, 为线程池创建线程的线程工厂
//		handler:指定线程池的拒绝策略
//		    1.AbortPolicy--丢弃任务并抛出异常,默认拒绝策略
//		    2.CallerRunsPolicy--由主线程负责调用run方法,绕过线程池
//		    3.DiscardOldestPolicy--抛弃队列中等待最久的任务,把当前的任务加入到队列
//		    4.DiscardPolicy--丢弃任务,不抛出异常,不推荐ExecutorService pool = new ThreadPoolExecutor(3, 6, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3),Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardPolicy());//		MyRun run = new MyRun();pool.execute(new MyRun("任务1"));//提交第一个任务,线程池会自动创建一个新的线程,自动处理任务pool.execute(new MyRun("任务2"));pool.execute(new MyRun("任务3"));pool.execute(new MyRun("任务4"));//提交第4个任务时候,进入等待队列中pool.execute(new MyRun("任务5"));pool.execute(new MyRun("任务6"));pool.execute(new MyRun("任务7"));//提交第7个任务的时候,核心线程数已满,队列已满,再来新任务,看临时工pool.execute(new MyRun("任务8"));//pool.execute(new MyRun("任务9"));pool.execute(new MyRun("任务10"));//核心线程满,队列满,临时满pool.execute(new MyRun("任务11"));}}

//线程池的创建方式2:
//    new ThreadPoolExecutorpublic class Demo02 {public static void main(String[] args) throws InterruptedException, ExecutionException {
//		corePoolSize:线程核心数量
//		maximumPoolSize:线程池中最大的线程数
//		keepAliveTime:临时线程的存活时间
//		unit,时间单位(时,分,秒)
//		workQueue, 任务队列
//		threadFactory, 为线程池创建线程的线程工厂
//		handler:指定线程池的拒绝策略
//		    1.AbortPolicy--丢弃任务并抛出异常,默认拒绝策略
//		    2.CallerRunsPolicy--由主线程负责调用run方法,绕过线程池
//		    3.DiscardOldestPolicy--抛弃队列中等待最久的任务,把当前的任务加入到队列
//		    4.DiscardPolicy--丢弃任务,不抛出异常,不推荐ExecutorService pool = new ThreadPoolExecutor(3, 6, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3),Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardPolicy());Future<Integer> f1=pool.submit(new MyCall(10));Future<Integer> f2=pool.submit(new MyCall(20));Future<Integer> f3=pool.submit(new MyCall(30));System.out.println(f1.get());System.out.println(f2.get());System.out.println(f3.get());}
}class MyCall implements Callable<Integer> {private int number;public MyCall(int number) {this.number = number;}@Overridepublic Integer call() throws Exception {int sum = 0;for (int i = 0; i <= number; i++) {sum += i;}return sum;}}

public class Demo03 {public static void main(String[] args) {
//		corePoolSize:线程核心数量
//		maximumPoolSize:线程池中最大的线程数
//		keepAliveTime:临时线程的存活时间
//		unit,时间单位(时,分,秒)
//		workQueue, 任务队列
//		threadFactory, 为线程池创建线程的线程工厂
//		handler:指定线程池的拒绝策略
//		    1.AbortPolicy--丢弃任务并抛出异常,默认拒绝策略
//		    2.CallerRunsPolicy--由主线程负责调用run方法,绕过线程池
//		    3.DiscardOldestPolicy--抛弃队列中等待最久的任务,把当前的任务加入到队列
//		    4.DiscardPolicy--丢弃任务,不抛出异常,不推荐ThreadPoolExecutor pool = new ThreadPoolExecutor(3, 6, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3),new MyThreadFactory(), new ThreadPoolExecutor.DiscardPolicy());//		MyRun run = new MyRun();pool.execute(new MyRun("任务1"));//提交第一个任务,线程池会自动创建一个新的线程,自动处理任务pool.execute(new MyRun("任务2"));pool.execute(new MyRun("任务3"));ThreadPoolExecutor pool1 = new ThreadPoolExecutor(3, 6, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3),new MyThreadFactory(), new ThreadPoolExecutor.DiscardPolicy());//		MyRun run = new MyRun();pool1.execute(new MyRun("任务1"));//提交第一个任务,线程池会自动创建一个新的线程,自动处理任务pool1.execute(new MyRun("任务2"));pool1.execute(new MyRun("任务3"));}}class MyThreadFactory implements ThreadFactory {private static final AtomicInteger poolNumber = new AtomicInteger(1);private final ThreadGroup group;private final AtomicInteger threadNumber = new AtomicInteger(1);private final String namePrefix;MyThreadFactory() {SecurityManager s = System.getSecurityManager();group = (s != null) ? s.getThreadGroup() :Thread.currentThread().getThreadGroup();namePrefix = "池子-" +poolNumber.getAndIncrement() +"-线程-";}public Thread newThread(Runnable r) {Thread t = new Thread(group, r,namePrefix + threadNumber.getAndIncrement(),0);if (t.isDaemon())t.setDaemon(false);if (t.getPriority() != Thread.NORM_PRIORITY)t.setPriority(Thread.NORM_PRIORITY);return t;}
}

6. 线程池的执行流程

1. 提交一个新线程任务,线程池会在线程池中分配一个空闲线程,用于执行线程任务;
2. 如果线程池中不存在空闲线程,则线程池会判断当前“存活的线程数”是否小于核心线程数corePoolSize。

        ○如果小于核心线程数corePoolSize,线程池会创建一个新线程(核心线程)去处理新线程任务;
        ○如果大于核心线程数corePoolSize,线程池会检查工作队列;


                ■如果工作队列未满,则将该线程任务放入工作队列进行等待。线程池中如果出现空闲线程,将从工作队列中按照FIFO的规则取出1个线程任务并分配执行;
                ■如果工作队列已满,则判断线程数是否达到最大线程数maximumPoolSize;

                        ●如果当前“存活线程数”没有达到最大线程数maximumPoolSize,则创建一个新线程(非核心线程)执行新线程任务;
                ●如果当前“存活线程数”已经达到最大线程数maximumPoolSize,直接采用拒绝策略处理新线程任务;

综上所述,执行顺序为:核心线程、工作队列、非核心线程、拒绝策略

7. 线程池的状态

线程池的状态分为:RUNNING,SHUTDOWN,STOP,TIDYING,TERMINATED


RUNNING 运行状态,线程池被一旦被创建,就处于RUNNING状态,并且线程池中的任务数为0。该状态的线程池会接收新任务,并处理工作队列中的任务。
  • 调用线程池的shutdown()方法,可以切换到SHUTDOWN关闭状态;
  • 调用线程池的shutdownNow()方法,可以切换到STOP停止状态;

SHUTDOWN关闭状态,该状态的线程池不会接收新任务,但会处理工作队列中的任务;

  • 当工作队列为空时,并且线程池中执行的任务也为空时,线程池进入TIDYING状态;

STOP停止状态,该状态的线程不会接收新任务,也不会处理阻塞队列中的任务,而且会中断正在运行 的任务;

  • 线程池中执行的任务为空,进入TIDYING状态;

TIDYING整理状态,该状态表明所有的任务已经运行终止,记录的任务数量为0;

  • terminated()执行完毕,进入TERMINATED状态;

TERMINATED: 终止状态,该状态表示线程池彻底关闭。

相关文章:

Java基础27,28(多线程,ThreadMethod ,线程安全问题,线程状态,线程池)

目录 一、多线程 1. 概述 2. 进程与线程 2.1 程序 2.2 进程 2.3 线程 2.4 进程与线程的区别 3. 线程基本概念 4.并发与并行 5. 线程的创建方式 方式一&#xff1a;继承Thread类 方式二&#xff1a;实现Runable接口 方式三&#xff1a;实现Callable接口 方式四&…...

C#WPF数字大屏项目实战04--设备运行状态

1、引入Livecharts包 项目中&#xff0c;设备运行状态是用饼状图展示的&#xff0c;因此需要使用livechart控件&#xff0c;该控件提供丰富多彩的图形控件显示效果 窗体使用控件 2、设置饼状图的显示图例 通过<lvc:PieChart.Series>设置环状区域 3、设置饼状图资源样…...

IntelliJ IDEA安装

IntelliJ IDEA 的安装、配置与使用-简化版 一、IntelliJ IDEA 介绍 1.JetBrains 公司介绍 IDEA(https://www.jetbrains.com/idea/)是 JetBrains 公司的产品&#xff0c;公司旗下还有其 它产品&#xff0c;比如&#xff1a; ➢ WebStorm&#xff1a;用于开发 JavaScript、HT…...

铸铁机械5G智能工厂工业物联数字孪生平台,推进制造业数字化转型

铸铁机械5G智能工厂工业物联数字孪生平台&#xff0c;推进制造业数字化转型。工业物联数字孪生平台以5G技术为基础&#xff0c;通过工业物联网连接铸铁机械生产过程中的各个环节&#xff0c;运用数字孪生技术构建虚拟工厂&#xff0c;实现生产过程的实时监测、模拟与优化&#…...

rocketmq No route info of this topic 问题排查

Broker配置项 autoCreateTopicEnable true 如果是单节点(master),注释掉这里的配置 #有三个值&#xff1a;SYNC_MASTER&#xff0c;ASYNC_MASTER&#xff0c;SLAVE&#xff1b;同步和异步表示Master和Slave之间同步数据的机制&#xff1b; #brokerRole SYNC_MASTER Pytho…...

STEEL ——首个利用 LLM 检测假新闻的框架算法解析

1.概述 近年来&#xff0c;假新闻的泛滥确实对政治、经济和整个社会产生了深远的负面影响。为了解决这一问题&#xff0c;人们开发了各种假新闻检测方法&#xff0c;这些方法试图通过分析新闻内容、来源和传播方式来识别虚假信息。 然而&#xff0c;正如你所提到的&#xff0…...

【AREngine BUG 解决方法】无法获取有效的相机图像尺寸

近期拿了一台 华为mate20 Pro的手机&#xff0c;在运行AR示例的过程中出现了黑屏。 问题排查 SDK版本&#xff1a;com.huawei.hms:arenginesdk:3.7.0.3 定位 经排查&#xff0c;发现(ARCamera对象的相机内参) getImageDimensions()返回的图像尺寸的width和height都为0。 这…...

植物大战僵尸杂交版2.0.88最新版+防闪退工具V2+修改工具+高清工具

植物大战僵尸杂交版&#xff0c;不仅继承原作的经典玩法&#xff0c;而且引入了全新的植物融合玩法&#xff0c;将各式各样的植物进行巧妙的杂交&#xff0c;孕育出前所未有、功能各异的全新植物。 创新的杂交合成系统 游戏引入了创新的杂交合成系统&#xff0c;让玩家可以将不…...

面试题:说说你对 JS 中 this 指向的了解

面试题&#xff1a;说说你对 JS 中 this 指向的了解 JS 的代码执行环境分为严格模式和非严格模式&#xff0c;可以通过 use strict 打开严格模式&#xff0c;此时 JS 在语法检查上会更加严格。要讨论 JS 中的 this 指向问题&#xff0c;也要分为严格模式和非严格模式进行讨论。…...

分享一个实用的MySQL一键巡检脚本

今日分享一个实用的MySQL一键巡检脚本&#xff0c;脚本内容还不是很完善&#xff0c;后续会继续进行优化。大家可以先在测试环境执行&#xff0c;确认执行没问题后可以在生产环境进行操作&#xff0c;问题的可以私信我。 MySQL一键巡检脚本的作用主要是帮助数据库管理员快速且…...

【动手学深度学习】卷积神经网络CNN的研究详情

目录 &#x1f30a;1. 研究目的 &#x1f30a;2. 研究准备 &#x1f30a;3. 研究内容 &#x1f30d;3.1 卷积神经网络 &#x1f30d;3.2 练习 &#x1f30a;4. 研究体会 &#x1f30a;1. 研究目的 特征提取和模式识别&#xff1a;CNN 在计算机视觉领域被广泛用于提取图像…...

2024年数字化经济与智慧金融国际会议(ICDESF 2024)

2024 International Conference on Digital Economy and Smart Finance 【1】大会信息 大会时间&#xff1a;2024-07-22 大会地点&#xff1a;中国成都 截稿时间&#xff1a;2024-07-10(以官网为准&#xff09; 审稿通知&#xff1a;投稿后2-3日内通知 会议官网&#xff1a;h…...

kafka-消费者服务搭建配置简单消费(SpringBoot整合Kafka)

文章目录 1、使用efak 创建 主题 my_topic1 并建立6个分区并给每个分区建立3个副本2、创建生产者发送消息3、application.yml配置4、创建消费者监听器5、创建SpringBoot启动类6、屏蔽 kafka debug 日志 logback.xml7、引入spring-kafka依赖 1、使用efak 创建 主题 my_topic1 并…...

C++STL---list常见用法

C STL中的list list是C标准模板库&#xff08;STL&#xff09;中的一个序列容器&#xff0c;它实现了一个双向链表。与vector和deque相比&#xff0c;list支持快速的任意位置插入和删除操作&#xff0c;但不支持快速随机访问。 基本操作 创建和初始化 #include <list> …...

MQTT.FX的使用

背景 在如今物联网的时代下&#xff0c;诞生了许多的物联网产品&#xff0c;这些产品通过BLE、WIFI、4G等各种各样的通信方式讲数据传输到各种各样的平台。 除了各个公司私有的云平台外&#xff0c;更多的初学者会接触到腾讯云、阿里云之类的平台。设备接入方式也有着多种多样…...

SRS、ZLMediakit音视频流媒体服务器

SRS、ZLMediakit都是做为webrtc的SFU&#xff08;selective forward unit&#xff09; WebRTC 开发实践&#xff1a;为什么你需要 SFU 服务器 https://mp.weixin.qq.com/s?__bizMzAxNTc1MjM0Mw&mid2652213442&idx1&sn33f0393a2dbc2b6a39c613bb238ec145&chksm…...

大模型Prompt-Tuning技术进阶

LLM的Prompt-Tuning主流方法 面向超大规模模型的Prompt-Tuning 近两年来&#xff0c;随之Prompt-Tuning技术的发展&#xff0c;有诸多工作发现&#xff0c;对于超过10亿参数量的模型来说&#xff0c;Prompt-Tuning所带来的增益远远高于标准的Fine-tuning&#xff0c;小样本甚至…...

统一响应,自定义校验器,自定义异常,统一异常处理器

文章目录 1.基本准备&#xff08;构建一个SpringBoot模块&#xff09;1.在A_universal_solution模块下创建新的子模块unified-processing2.pom.xml引入基本依赖3.编写springboot启动类4.启动测试 2.统一响应处理1.首先定义一个响应枚举类 RespBeanEnum.java 每个枚举对象都有co…...

完整状态码面试背

{"100": "继续","101": "切换协议","102": "处理中","103": "早期提示","200": "成功","201": "已创建","202": "已接受",&qu…...

QT+FFmpeg+Windows开发环境搭建(加薪点)

01、Windows 环境搭建 FFMPEG官网:http://ffmpeg.org/ 02、下载4.2.1版本源码 源码:https://ffmpeg.org/releases/ffmpeg-4.2.1.tar.bz2 03、下载4.2.1编译好的文件 下载已经编译好的FFMPEG)(迅雷下载很快) 网址:https://ffmpeg.zeranoe.com/builds/ 32位下载地址:(迅雷…...

Linux 主机一键安全整改策略

为防止linux主机被恶意攻击&#xff0c;和受到攻击后能更快定位到源头&#xff0c;需要对linux主机做一些参数配置。 比如禁用root的远程登录、用户多次密码验证失败后被锁、禁止系统账号交互式登录等等。 下面是linux主机安全整改的一些简单介绍&#xff0c;最后会通过脚本一…...

Hot100——二叉树

树的定义&#xff1a; public static class TreeNode{int val;TreeNode left;TreeNode right;TreeNode(){};TreeNode(int val){ this.val val; };TreeNode(int val, TreeNode left, TreeNode right){this.val val;this.left left;this.right right;}} 深度优先遍历&#x…...

C++ static_cast、dynamic_cast、const_cast 和 reinterpret_cast 用处和区别

在 C 中&#xff0c;static_cast、dynamic_cast、const_cast 和 reinterpret_cast 是四种类型转换运算符&#xff0c;它们各自有不同的用途和行为&#xff1a; static_cast 用于编译时已知类型的转换&#xff0c;如基本数据类型转换、派生类到基类的转换、指针和引用的转换等…...

三十七、openlayers官网示例Earthquakes Heatmap解析——在地图上加载热力图

官网demo地址&#xff1a; Earthquakes Heatmap 这篇主要介绍了热力图HeatmapLayer HeatmapLayer 是一个用于在地图上显示热力图的图层类型&#xff0c;通常用于表示地理数据中的密度或强度。例如&#xff0c;它可以用来显示地震、人口密度或其他空间数据的热点区域。在这个示…...

curl 92 HTTP/2 stream 5 was not closed cleanly: CANCEL

source ~/.bash_profile flutter clean Command exited with code 128: git fetch --tags Standard error: 错误&#xff1a;RPC 失败。curl 92 HTTP/2 stream 5 was not closed cleanly: CANCEL (err 8) 错误&#xff1a;预期仍然需要 2737 个字节的正文 fetch-pack: unexpec…...

Spring Security 注册过滤器关键点与最佳实践

在 Spring Security 框架中&#xff0c;注册过滤器是实现身份验证和授权的关键组件。正确配置和使用注册过滤器对于确保应用程序的安全性至关重要。以下是一些关于 Spring Security 注册过滤器的注意事项和最佳实践。 过滤器链顺序&#xff1a; 注册过滤器通常位于过滤器链的末…...

力扣2024.考试的最大困扰度

力扣2024.考试的最大困扰度 注意同时>k才处理 class Solution {public:int maxConsecutiveAnswers(string answerKey, int k) {int n answerKey.size(),res0;unordered_map<int,int> cnt;for(int i0,j0;i<n;i){cnt[answerKey[i] - a] ;while(cnt[T - a] > k …...

java配置文件解析yml/xml/properties文件

XML 以mybatis.xml:获取所有Environment中的数据库并连接session为例 import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException;import javax.xml.parsers.DocumentBuilder; impo…...

grpc接口调用

grpc接口调用 准备依赖包clientserver 参考博客&#xff1a; Grpc项目集成到java方式调用实践 gRpc入门和springboot整合 java 中使用grpc java调用grpc服务 准备 因为需要生成代码&#xff0c;所以必备插件 安装后重启 依赖包 <?xml version"1.0" encoding&…...

通信技术振幅键控(ASK)调制与解调硬件实验

一、实验目的 1. 掌握用键控法产生ASK信号的方法&#xff1b; 2. 掌握ASK非相干解调的原理。 二、实验内容 1. 观察ASK已调信号的波形&#xff1b; 2. 观察ASK解调信号的波形。 三、实验器材 1. 双踪示波器&#xff1b; 2. 通信原理实验箱信号源模块、③、④、⑦号模块。…...

福建自适应网站建设/成都最好的seo外包

原文&#xff1a;https://my.oschina.net/u/877759/blog/501733 写的一个scala多线程的小demo&#xff0c;以备后用 Runnable/Callable 区别&#xff1a;Runnable无返回值&#xff0c;Callable线程执行完有返回值 Runnable示例 import java.util.concurrent.{Executors, Exe…...

dede做电影网站/合肥网站快速排名提升

在为程序加背景时&#xff0c;发现在拖动List或Grid列表时一片漆黑&#xff0c;很是难看android提供了一种方法&#xff0c;在用户拖动列表时不总是一片漆黑或者说透明。其实只是View的属性而已&#xff0c;ListView、GridView都有这个属性&#xff0c;两种设置如 下&#xff1…...

怎样建立网站视频教程/十大新媒体平台有哪些

本文实例分析了Android开发之TimePicker控件用法。分享给大家供大家参考&#xff0c;具体如下&#xff1a;新建项目&#xff1a;New Android Project->Project name:HelloSpinnerBuild Target:Android 2.2Application name:HelloSpinnerPackage name:com.b510Create Activit…...

网站的专区怎么建设/曹操论坛seo

文章目录1.运行时数据区⭐运行时常量池《深入理解Java虚拟机》第六章中关于常量池的解释&#xff1a;知乎&#xff1a;JVM详解之:运行时常量池&#xff1a;[https://zhuanlan.zhihu.com/p/160770086](https://zhuanlan.zhihu.com/p/160770086)字符串常量池2.3 对象的创建3.对象…...

网页设计作业在线网站首页/广告网站建设网站排名优化

完整系列教程详见&#xff1a;http://golang.iswbm.com在 Golang 中用于执行命令的库是 os/exec&#xff0c;exec.Command 函数返回一个 Cmd 对象&#xff0c;根据不同的需求&#xff0c;可以将命令的执行分为三种情况只执行命令&#xff0c;不获取结果执行命令&#xff0c;并获…...

wordpress读取图片loading/网络优化工具app手机版

重启网卡 root用户 systemctl restart network非root用户 sudo systemctl restart network...