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();
	}
	@Override
	public 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 {
	@Override
	public 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之间的和
	@Override
	public 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);
	}

	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			try {
				sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.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) {
    		//创建一个匿名内部类对象run
    		Runnable run = new Runnable() {
    
    			@Override
    			public 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");
    
    		//获取线程的优先级:默认为5
    		System.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)
		//细节:当其他的非守护的线程执行完毕后,守护线程会陆续结束
		//即(当女神线程结束,那么备胎线程也没有存在的必要)
		
		//创建线程1
		Thread t1 = new Thread() {
			@Override
			public void run() {
				for (int i = 1; i <=10; i++) {
					System.out.println(getName()+"@"+i);
				}
			};
		};
		//创建线程2
		Thread t2 = new Thread() {
			@Override
			public 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 {
	//当前线程让出执行权后,再次进行抢夺也可能会抢夺到相关资源
	@Override
	public 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 {
	@Override
	public 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;

	@Override
	public void run() {
		// 卖票
		while (true) {
			if (ticket < 10) {
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.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();

	@Override
	public void run() {
		// 卖票
		while (true) {
			synchronized (obj) {
				if (ticket < 1000) {
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					ticket++;
					System.out.println(getName() + "正在卖出第" + ticket + "张票");
				} else {
					break;
				}
			}
		}
	}
}

解决方案2

解放方案2:锁方法:

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

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

public class MyThread extends Thread {
	// 表示当前的变量提升到了类级别,所有的类共享此成员变量
	static int ticket = 0;

	@Override
	public 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 block
				e.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;

	@Override
	public 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 block
				e.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();

	@Override
	public 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 block
				e.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() {
			@Override
			public 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() {
			@Override
			public 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() {
			@Override
			public 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() {

			@Override
			public 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() {

			@Override
			public void run() {
				synchronized (this) {
					System.out.println(Thread.currentThread().getName());
					try {
//						Thread.sleep(5000);
						this.wait(5000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.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() {

			@Override
			public void run() {
				Thread t1 = new Thread() {
					@Override
					public void run() {
						System.out.println("t1线程");

					};
				};
				t1.start();
				//join方法会让当前的线程处于计时等待
				try {
					t1.join(500 * 1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.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 block
						e.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() {
			@Override
			public 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) {

	}

	@Override
	public 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;
	}
	@Override
	public 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());//55
        System.out.println(futureTask2.get());//5050
        System.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() {
			@Override
			public 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;
	}

	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName()+ name + "======>" + "正在执行");

		// 让任务稍微慢点,便于观察结果
		try {
			Thread.sleep(Integer.MAX_VALUE);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}
//线程池的创建方式2:
//    new ThreadPoolExecutor

public 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 ThreadPoolExecutor

public 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;
	}

	@Override
	public 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: 终止状态,该状态表示线程池彻底关闭。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/682177.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【操作系统】(详细理解进程的状态)执行状态、就绪状态、阻塞状态、挂起状态

下面是进程的几种状态的概念&#xff1a; 执行状态&#xff1a;当一个进程已获得必要资源&#xff0c;并占有CPU进行执行。 就绪状体&#xff1a;进程已分配到除CPU外的所有必要资源&#xff0c;只要获取CPU允许就可立即执行。 阻塞状态&#xff1a;正在执行的进程&#xff0c;…

C++之虚函数与多态

1、多态 前面三种称为静态绑定&#xff08;静态多态&#xff09;&#xff0c;最后面的虚函数&#xff0c;则称为动态绑定&#xff08;动态多态&#xff09;。 2、静态绑定与动态绑定 要实现动态绑定&#xff0c;就必须使用虚函数。 3、虚函数 只有当你在&#xff1a;基类的指…

计算机组成原理·考点知识点整理

根据往年考试题&#xff0c;对考点和知识点的一个整理。 校验编码 码距 一种编码的最小码距&#xff0c;其实就是指这种编码的码距。码距有两种定义&#xff1a; 码距所描述的对象含义 2 2 2 个特定的码其二进制表示中不同位的个数一种编码这种编码中任意 2 2 2 个合法编码的…

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

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

JVMの垃圾回收

在上一篇中&#xff0c;介绍了JVM组件中的运行时数据区域&#xff0c;这一篇主要介绍垃圾回收器 JVM架构图&#xff1a; 1、垃圾回收概述 在第一篇中介绍JVM特点时&#xff0c;有提到过内存管理&#xff0c;即Java语言相对于C&#xff0c;C进行的优化&#xff0c;可以在适当的…

技术回眸一笑

回忆一下一年前的出差日记吧&#xff0c;那个时候真的是一点经验没有&#xff0c;干硬件又干软件&#xff0c;只能一步一步慢慢摸索&#xff0c;努力过后慢慢成长起来的吧。那个时候甚至开学了都没有正常报道&#xff0c;但是也收获了不少东西&#xff0c;并且也将作为我后来继…

Vue——组件数据传递与props校验

文章目录 前言组件数据传递的几种类型简单字符串类型数据专递其他类型数据(数字、数组、对象)传递注意事项 数据传递值校验限定数据类型 type给定默认值 default指定必选项 required 前言 组件与组件之间并不是完全独立的&#xff0c;他们之间可以进行一些数据的传递操作。传递…

10个高清视频素材库分享,高清高质量的分享给你

今天&#xff0c;我将为各位介绍几个极具价值的高清视频素材库。无论您是短视频创作者、自媒体运营者还是影视后期制作专家&#xff0c;这些素材库将大大提升您作品的质量。现在&#xff0c;让我们直接深入主题&#xff0c;探索这些优秀的资源平台&#xff01; 蛙学府视频素材…

使用cesiumLab使shp转为3dtlies

过程不做赘述&#xff0c;网上大把&#xff0c;说下注意事项。 1. 存储3DTiles 选项 若是打开则输出的文件为glb格式文件,因为glb文件好储存易传输跨平台。cesium可以使用但无法处理&#xff0c;例如改变颜色&#xff0c;改着色器等。若是不打开则输出的文件为bm3d格式文件,此…

ShowDoc item_id 未授权SQL注入漏洞复现

0x01 产品简介 ShowDoc 是一个开源的在线文档协作平台,它支持Markdown、图片等多种格式,方便团队成员共同编辑和分享文档。企业常见使用场景是使用其进行接口文档、内部知识库管理。 0x02 漏洞概述 2024年6月,ShowDoc官方发布新版本修复了一个SQL注入漏洞。鉴于该漏洞无前…

k8s怎么监听自定义资源的变更?(2)

接上一篇当生成下面代码之后怎么去使用呢&#xff1f; 1.生成crd文件 这里我们通过kubebuilder的一个子项目 controller-gen 来生成crd文件 https://github.com/kubernetes-sigs/controller-tools curl -L -o https://github.com/kubernetes-sigs/controller-tools; go ins…

【前端】响应式布局笔记——flex

二、Flex Flex(FlexiableBox:弹性盒子&#xff0c;用于弹性布局&#xff0c;配合rem处理尺寸的适配问题)。 1、flex-direction:子元素在父元素盒子中的排列方式。 父级元素添加&#xff1a;flex-direction: row; 父级元素添加&#xff1a;flex-direction: row-reverse; 父…

基于百度接口的实时流式语音识别系统

目录 基于百度接口的实时流式语音识别系统 1. 简介 2. 需求分析 3. 系统架构 4. 模块设计 4.1 音频输入模块 4.2 WebSocket通信模块 4.3 音频处理模块 4.4 结果处理模块 5. 接口设计 5.1 WebSocket接口 5.2 音频输入接口 6. 流程图 程序说明文档 1. 安装依赖 2.…

RPA-UiBot6.0数据采集机器人(海量信息一网打尽)内附RPA师资培训课程

前言 友友们是否曾为海量的数据信息而头疼,不知道如何从中精准抓取你所需的数据?小北的这篇博客将为你揭晓答案,让我们一起学习如何运用RPA数据采集机器人,轻松实现海量信息的快速抓取与整理,助力你的工作效率翻倍! 诚邀各位友友参与小北博客的评论,共同开启自动…

C++ Primer 总结索引 | 第十五章:面向对象程序设计

继承和动态绑定 对程序的编写 有两方面的影响&#xff1a;一是 我们可以更容易地定义与其他类相似 但不完全相同的新类&#xff1b;二是 在使用这些彼此相似的类编写程序时&#xff0c;我们可以在一定程度上 忽略掉它们的区别 在很多程序中都存在着一些相互关联 但是有细微差别…

【机器学习300问】107、自然语言处理(NLP)领域有哪些子任务?

自然语言处理&#xff08;NLP&#xff09;是计算机科学、人工智能和语言学领域的一个交叉学科&#xff0c;致力于让计算机能够理解、解析、生成和与人类的自然语言进行互动。自然语言指的是人们日常交流使用的语言&#xff0c;如英语、汉语等&#xff0c;与计算机编程语言相对。…

IO流----字节流

字节流 字节流&#xff1a;操作&#xff1a;文件字节输入输出流 &#xff1a;写入数据&#xff1a;读取数据&#xff1a;文件拷贝&#xff1a; 带缓冲区的字节输入输出流&#xff1a;拷贝文件&#xff1a;写入数据&#xff1a;读取数据: 深入 带缓冲区的字节输出流 &#xff1a…

C语言基础学习之位运算

枚举类型 enum 枚举名 { 枚举常量 //名字 }; 注意: 1.c语言中 对于枚举类型 实际上是 当作整型处理的 2.提高代码可读性&#xff0c; 写成枚举&#xff0c;可以做语法检查 3.枚举常量&#xff0c;之间用逗号隔开 4.枚举常量&#xff0c;可以给初值&#xff0c;给了初值之后&…

React(五)useEffect、useRef、useImperativeHandle、useLayoutEffect

(一)useEffect useEffect – React 中文文档 useEffect hook用于模拟以前的class组件的生命周期&#xff0c;但比原本的生命周期有着更强大的功能 1.类组件的生命周期 在类组件编程时&#xff0c;网络请求&#xff0c;订阅等操作都是在生命周期中完成 import React, { Com…

二叉树练习题(2024/6/5)

1翻转二叉树 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 示例 1&#xff1a; 输入&#xff1a;root [4,2,7,1,3,6,9] 输出&#xff1a;[4,7,2,9,6,3,1]示例 2&#xff1a; 输入&#xff1a;root [2,1,3] 输出&#xff1a;[2,3,1]…