java多线程
1、线程简介
普通方法和多线程:
进程:在操作系统中运行的程序就是进程,比如你的QQ,播放器,游戏,IDE等等。
程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。
进程则是执行程序的一次执行过程,它是一个动态的概念。是系统资源分配的单位通常在一个进程中可以包含若干个线程,当然一
个进程中至少有一个线程,不然没有存在的意义。
线程是CPU调度和执行的的单位。
tips:
1、线程就是独立的执行路径;
2、在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程;
3、main()称之为主线程,为系统的入口,用于执行整个程序;
4、在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为的
千预的。
5、对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;线程会带来额外的开销,如cpu调度时间,并发控制开销。
6、每个线程在自己的工作内存交互,内存控制不当会造成数据不一致。
2、线程实现(三种)
继承Thread类(重点)
步骤:
1、自定义线程类继承Thread类重写run()方法
2、编写线程执行体创建线程对象
3、调用start()方法启动线程
示例代码:
package Multithread;
public class TestThread1 extends Thread {
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println("线程1");
}
}
public static void main(String[] args) {
// 两个线程同时执行
new TestThread1().start();
for (int i = 0; i < 200; i++) {
System.out.println("主线程");
}
}
}
多线程下载图片:
package Multithread;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
public class TestThread2 extends Thread {
private String url;
private String name;
public TestThread2(String url, String name) {
this.url = url;
this.name = name;
}
@Override
public void run() {
new Downloader().downloader(url,name);
System.out.println("下载了图片:"+name);
}
public static void main(String[] args) {
TestThread2 t1=new TestThread2("https://img-home.csdnimg.cn/images/20220928102134.jpg","1.jpg");
TestThread2 t2=new TestThread2("https://img-operation.csdnimg.cn/csdn/silkroad/img/1664246005001.jpg","2.jpg");
TestThread2 t3=new TestThread2("https://img-operation.csdnimg.cn/csdn/silkroad/img/1663729624980.jpg","3.jpg");
t1.start();
t2.start();
t3.start();
}
}
class Downloader{
public void downloader(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("Downloader捕获到异常");
}
}
}
实现Runnable接口(重点)
步骤:
1、定义MyRunnable类实现Runnable接口
2、实现run()方法,编写线程执行体
3、创建线程对象,调用start()方法启动线程
基础代码:
package Multithread;
public class TestThread3 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("线程1");
}
}
public static void main(String[] args) {
Thread t=new Thread(new TestThread3());
t.start();
for (int i = 0; i < 20; i++) {
System.out.println("主线程");
}
}
}
体现Runnable的多继承性:
package Multithread;
public class TestThread4 implements Runnable{
int ticket=100;
@Override
public void run() {
while (ticket>0){
System.out.println(Thread.currentThread().getName()+"抢到票:"+ticket--);
}
}
public static void main(String[] args) {
TestThread4 r=new TestThread4();
// 多继承使用
new Thread(r,"小明").start();//小明作为线程名
new Thread(r,"李华").start();
new Thread(r,"黄牛").start();
}
}
龟兔赛跑:
package Multithread;
public class TestRace implements Runnable{
private static String winner;
@Override
public void run() {
for (int i = 0; i <=100; i++) {
// 判断比赛是否结束
if (gameover(i)) {
break;
}
// 模拟兔子睡觉
if (Thread.currentThread().getName().equals("兔子")&&i%10==0){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");
}
}
// 判断获胜者
public Boolean gameover(int step){
if (winner!=null)return true;
else {
if (step>=100){
winner=Thread.currentThread().getName();
System.out.println("获胜者是"+winner);
return true;
}
}
return false;
}
public static void main(String[] args) {
TestRace r=new TestRace();
new Thread(r,"乌龟").start();
new Thread(r,"兔子").start();
}
}
实现callable接口(了解)
步骤:
1、实现Callable接口,需要返回值类型
2、重写call方法,需要抛出异常
3、创建目标对象
4、创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);
5、提交执行:Future result1 = ser.submit(t1);
6、获取结果:boolean r1 = result1.get()
7、关闭服务:ser.shutdownNow();
示例代码:
package Multithread;
import java.util.concurrent.*;
public class TestThread5 implements Callable<Boolean> {
private String url;
private String name;
public TestThread5(String url, String name) {
this.url = url;
this.name = name;
}
@Override
public Boolean call() {
new Downloader().downloader(url, name);
System.out.println("下载了图片:" + name);
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
TestThread5 t1 = new TestThread5("https://img-home.csdnimg.cn/images/20220928102134.jpg", "1.jpg");
TestThread5 t2 = new TestThread5("https://img-operation.csdnimg.cn/csdn/silkroad/img/1664246005001.jpg", "2.jpg");
TestThread5 t3 = new TestThread5("https://img-operation.csdnimg.cn/csdn/silkroad/img/1663729624980.jpg", "3.jpg");
//创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);
ExecutorService ser = Executors.newFixedThreadPool(3);
//提交执行:Future<Boolean> result1 = ser.submit(t1);
Future<Boolean> r1 = ser.submit(t1);
Future<Boolean> r2 = ser.submit(t2);
Future<Boolean> r3 = ser.submit(t3);
//获取结果:boolean r1 = result1.get()
boolean rs1 = r1.get();
boolean rs2 = r2.get();
boolean rs3 = r3.get();
//关闭服务:ser.shutdownNow();
ser.shutdownNow();
}
}
补充:静态代理模式
真实对象专注做自己的事情,代理对象做真实对象的附加业务。
代码:
package Multithread;
/*
* 静态代理模式
* */
//实际类和代理类需实现同一接口
public class StaticP {
public static void main(String[] args) {
You y=new You();
wedCompany wd=new wedCompany(y);
wd.happymarry();
}
}
interface Marry{
void happymarry();
}
//需求者
class You implements Marry{
@Override
public void happymarry() {
System.out.println("你结婚了");
}
}
//代理
class wedCompany implements Marry{
private Marry target;
public wedCompany(Marry target) {
this.target = target;
}
@Override
public void happymarry() {
before();
target.happymarry();
after();
}
public void before(){
System.out.println("结婚前");
}
public void after(){
System.out.println("结婚后");
}
}
lambda表达式
package Multithread;
public class TestT {
public static void main(String[] args) {
new Thread(()-> System.out.println("哈哈哈")).start();
}
}
3、线程状态
(线程五大状态)图解:
1、精简:
2、详细:
线程方法:
停止线程:
1、不推荐使用JDK提供的stop()、destroy()方法。【已废弃】
2、推荐线程自己停止下来
3、建议使用一个标志位进行终止变量当flag=false,则终止线程运行。
停止线程推荐方式代码:
package Multithread;
/*
* 推荐使用的线程停止方式,使用一个外部标志位
* */
public class StateT implements Runnable{
private Boolean flag=true;
@Override
public void run() {
int i=0;
while (flag){
System.out.println("线程运行:"+i);
}
}
public void stop(){
this.flag=false;
}
public static void main(String[] args) {
StateT stateT = new StateT();
Thread thread = new Thread(stateT);
thread.start();
for (int i = 0; i < 100; i++) {
System.out.println("main");
if (i==90){
stateT.stop();
System.out.println("线程停止");
}
}
}
}
线程休眠:(Thread.sleep(1000))
sleep(时间)指定当前线程阻塞的毫秒数;
sleep存在异常InterruptedException;
sleep时间达到后线程进入就绪状态;
sleep可以模拟网络延时,倒计时等。
每一个对象都有一个锁,sleep不会释放锁;
使用案例(倒计时、打印系统当前时间)
package Multithread;
import java.text.SimpleDateFormat;
import java.util.Date;
public class SleepT {
public static void Down() throws InterruptedException {
int times=10;
while (times>=0){
Thread.sleep(1000);
System.out.println("倒计时:"+times--);
}
}
public static void main(String[] args) throws InterruptedException {
// 倒计时
Down();
// 打印系统当前时间
Date time=new Date(System.currentTimeMillis());//获取系统当前时间
while (true){
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(time));
time=new Date(System.currentTimeMillis());//更新系统当前时间
}
}
}
线程礼让:(Thread.yield())
礼让线程,让当前正在执行的线程暂停,但不阻塞
将线程从运行状态转为就绪状态
让cpu重新调度,礼让不一定成功!看CPU心情
代码:
package Multithread;
public class ThreadYield {
public static void main(String[] args) {
mythraed mythraed = new mythraed();
new Thread(mythraed,"a").start();
new Thread(mythraed,"b").start();
}
}
class mythraed implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"开始");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"结束");
}
}
线程强制执行:(thread.join())
Join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞
可以想象成插队
测试代码:
package Multithread;
public class Threadjoin implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("vip"+i);
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Threadjoin());
thread.start();
for (int i = 0; i < 200; i++) {
if (i==50){
thread.join();
}
System.out.println("主线程"+i);
}
}
}
线程状态:Thread.getState()
线程优先级:
Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行。
线程的优先级用数字表示,范围从1~10.
Thread.MIN_PRIORITY = 1;
Thread.MAX_PRIORITY = 10;
Thread.NORM_PRIORITY = 5;(默认优先级)
使用以下方式改变或获取优先级
getPriority()
setPriority(int xxx)
测试代码:
package Multithread;
public class ThreadPri implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" "+Thread.currentThread().getPriority());
}
public static void main(String[] args) {
ThreadPri tp=new ThreadPri();
Thread t1 = new Thread(tp,"t1");
Thread t2 = new Thread(tp,"t2");
Thread t3 = new Thread(tp,"t3");
Thread t4 = new Thread(tp,"t4");
t1.setPriority(10);
t2.setPriority(5);
t3.setPriority(3);
t4.setPriority(1);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
守护线程
线程分为用户线程和守护线程
虚拟机必须确保用户线程执行完毕
虚拟机不用等待守护线程执行完毕
如后台记录操作日志,监控内存,垃圾回收等待…
thread.setDaemon(true)//设置线程为守护线程
4、线程同步
简介:
形成条件:队列加锁
并发:同一个对象被多个线程操作。
线程不安全案例:
package Multithread;
import java.util.ArrayList;
import java.util.List;
public class Unsafe {
public static void main(String[] args) {
List<String > list=new ArrayList<String >();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
System.out.println(list.size());
}
}
同步方法:
由于我们可以通过private 关键字来保证数据对象只能被方法访问﹐所以我们只需要针对方法提出一套机制,这套机制就是
synchronized关键字,它包括两种用法:synchronized方法和synchronized块.
synchronized方法控制对“对象”的访问,每个对象对应一把锁
每个synchronized方法都必须获得调用该方法的对象的锁才能执行﹐否则线程会阻塞,方法一旦执行就独占该锁,直到该方
法返回才释放锁﹐后面被阻塞的线程才能获得这个锁﹐继续执行
代码:
package Multithread;
public class TestThread4 implements Runnable{
private int ticket=100;
private Boolean flag=true;
@Override
public void run() {
while (flag){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
flag = buy();
}
}
public synchronized Boolean buy(){
if (ticket<1){
return false;
}
System.out.println(Thread.currentThread().getName()+"抢到票:"+ticket);
ticket--;
return true;
}
public static void main(String[] args) {
TestThread4 r=new TestThread4();
// 多继承使用
new Thread(r,"小明").start();
new Thread(r,"李华").start();
new Thread(r,"黄牛").start();
}
}
同步块
同步块:synchronized (Obj ){ }
Obj称之为同步监视器:
Obj可以是任何对象﹐但是推荐使用共享资源作为同步监视器。
同步方法中无需指定同步监视器﹐因为同步方法的同步监视器就是this ,就是这个对象本身,或者是class。
同步监视器的执行过程:
- 第一个线程访问﹐锁定同步监视器﹐执行其中代码。
2.第二个线程访问﹐发现同步监视器被锁定,无法访问。
3.第一个线程访问完毕,解锁同步监视器。
4.第二个线程访问,发现同步监视器没有锁﹐然后锁定并访问。
代码示例:
package Multithread;
public class TestThread4 implements Runnable{
private int ticket=100;
private Boolean flag=true;
@Override
public void run() {
while (flag){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (this){
flag = buy();
}
}
}
public Boolean buy(){
if (ticket<1){
return false;
}
System.out.println(Thread.currentThread().getName()+"抢到票:"+ticket);
ticket--;
return true;
}
public static void main(String[] args) {
TestThread4 r=new TestThread4();
// 多继承使用
new Thread(r,"小明").start();
new Thread(r,"李华").start();
new Thread(r,"黄牛").start();
}
}
死锁问题:
多个线程各自占有一些共享资源﹐并且互相等待其他线程占有的资源才能运行﹐而导致两个或者多个线程都在等待对方释放资
源﹐都停止执行的情形.某一个同步块同时拥有“两个以上对象的锁”时,就可能会发生“死锁”的问题。
产生死锁的四个必要条件:
1.互斥条件:一个资源每次只能被一个进程使用。
2.请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
3、不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
4.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
Lock
从JDK 5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。
同步锁使用Lock对象充当java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。
锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象
ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁。
基本格式:
代码示例:
package Multithread;
import java.util.concurrent.locks.ReentrantLock;
public class Lock1 {
public static void main(String[] args) {
TestLock testLock = new TestLock();
new Thread(testLock ).start();
new Thread(testLock ).start();
new Thread(testLock ).start();
}
}
class TestLock implements Runnable{
int ticketNums=10;
// 定义Lock锁
private final ReentrantLock lock=new ReentrantLock();
@Override
public void run() {
while (true){
try{
lock.lock();//加锁
if (ticketNums>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(ticketNums--);
}else {
break;
}
}finally {
lock.unlock();//解锁
}
}
}
}
5、线程协作(生产消费模型)
应用场景∶生产者和消费者问题
假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费.
如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止.
如果仓库中放有产品﹐则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止.
1、管程法
参考代码:(引用)
package Multithread;
public class ProAndCon1 {
private static int count = 0;
private static final int buffCount = 10;
private static String lock = "lock";
class Producer implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock) {
while (count == buffCount) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count++;
System.out.println(Thread.currentThread().getName() + "-生产者生产,数量为:" + count);
lock.notifyAll();
}
}
}
}
class Consumer implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock) {
while (count == 0) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count--;
System.out.println(Thread.currentThread().getName() + "-消费者消费,数量为:"+ count);
lock.notifyAll();
}
}
}
}
public static void main(String[] args) {
ProAndCon1 waitTest = new ProAndCon1();
new Thread(waitTest.new Producer()).start();
new Thread(waitTest.new Consumer()).start();
new Thread(waitTest.new Producer()).start();
new Thread(waitTest.new Consumer()).start();
new Thread(waitTest.new Producer()).start();
new Thread(waitTest.new Consumer()).start();
}
}
运行结果:
Thread-0-生产者生产,数量为:1
Thread-3-消费者消费,数量为:0
Thread-2-生产者生产,数量为:1
Thread-4-生产者生产,数量为:2
Thread-5-消费者消费,数量为:1
Thread-1-消费者消费,数量为:0
Thread-4-生产者生产,数量为:1
Thread-5-消费者消费,数量为:0
Thread-2-生产者生产,数量为:1
Thread-1-消费者消费,数量为:0
Thread-0-生产者生产,数量为:1
Thread-3-消费者消费,数量为:0
Thread-2-生产者生产,数量为:1
Thread-1-消费者消费,数量为:0
Thread-4-生产者生产,数量为:1
Thread-0-生产者生产,数量为:2
Thread-5-消费者消费,数量为:1
Thread-3-消费者消费,数量为:0
Thread-4-生产者生产,数量为:1
Thread-0-生产者生产,数量为:2
Thread-5-消费者消费,数量为:1
Thread-3-消费者消费,数量为:0
Thread-2-生产者生产,数量为:1
Thread-1-消费者消费,数量为:0
Thread-2-生产者生产,数量为:1
Thread-4-生产者生产,数量为:2
Thread-1-消费者消费,数量为:1
Thread-5-消费者消费,数量为:0
Thread-0-生产者生产,数量为:1
Thread-3-消费者消费,数量为:0
Thread-4-生产者生产,数量为:1
Thread-1-消费者消费,数量为:0
Thread-2-生产者生产,数量为:1
Thread-3-消费者消费,数量为:0
Thread-0-生产者生产,数量为:1
Thread-5-消费者消费,数量为:0
Thread-2-生产者生产,数量为:1
Thread-5-消费者消费,数量为:0
Thread-4-生产者生产,数量为:1
Thread-0-生产者生产,数量为:2
Thread-1-消费者消费,数量为:1
Thread-3-消费者消费,数量为:0
2、信号灯法
实质是缓冲区为1的线程池
示例代码:
package Multithread;
public class PlayAndW {
public static void main(String[] args) {
TV tv=new TV();
new Thread(new Player(tv)).start();
new Thread(new Watcher(tv)).start();
}
}
//演员
class Player implements Runnable{
TV tv;
public Player(TV tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 30; i++) {
if (i%2==0){
this.tv.play("B站LOL直播中");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
this.tv.play("抖音视频");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
//观众
class Watcher implements Runnable{
TV tv;
public Watcher(TV tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 30; i++) {
this.tv.watch();
}
}
}
class TV{
String voice;
Boolean flag=true;
// 表演
public synchronized void play(String voice){
if (!flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("演员表演了:"+voice);
this.voice=voice;
this.flag=!this.flag;
this.notifyAll();//通知观众观看
}
// 观看
public synchronized void watch(){
if (flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观看了节目:"+this.voice);
// 通知演员表演
this.flag=!this.flag;
this.notifyAll();
}
}
运行结果:
演员表演了:B站LOL直播中
观看了节目:B站LOL直播中
演员表演了:抖音视频
观看了节目:抖音视频
演员表演了:B站LOL直播中
观看了节目:B站LOL直播中
演员表演了:抖音视频
观看了节目:抖音视频
演员表演了:B站LOL直播中
观看了节目:B站LOL直播中
演员表演了:抖音视频
观看了节目:抖音视频
演员表演了:B站LOL直播中
观看了节目:B站LOL直播中
演员表演了:抖音视频
观看了节目:抖音视频
演员表演了:B站LOL直播中
观看了节目:B站LOL直播中
演员表演了:抖音视频
观看了节目:抖音视频
演员表演了:B站LOL直播中
观看了节目:B站LOL直播中
演员表演了:抖音视频
观看了节目:抖音视频
演员表演了:B站LOL直播中
观看了节目:B站LOL直播中
3、线程池(浅析,重点见下一章)
背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。
好处:
提高响应速度(减少了创建新线程的时间)
降低资源消耗(重复利用线程池中线程,不需要每次都创建)
便于线程管理(…)
corePoolSize:核心池的大小
maximumPoolSize:最大线程数
keepAliveTime:线程没有任务时最多保持多长时间后会终止
JDK 5.0起提供了线程池相关API: ExecutorService和Executors
ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
void execute(Runnable command)∶执行任务/命令,没有返回值,一般用来执行Runnable
Future submit(Callable task):执行任务,有返回值,一般又来执行Callable
void shutdown()∶关闭连接池
Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
分类:
FixedThreadPool:固定长度的线程池,可以控制线程的最大并发数,超出的线程会放到队列中。
SingleThreadExecutor:单线程线程池,它只会用唯一的线程来执行任务。
CachedThreadPool:可缓存的线程池,如果数据请求过多,它会不断创建新的线程,并且它还可以灵活回收空闲的线程。
ScheduledThreadPool:用于定时执行任务的线程池。
示例代码:
package Multithread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestPool {
public static void main(String[] args) {
// 1、创建服务
// 2、创建线程池
ExecutorService service= Executors.newFixedThreadPool(10);//10线程池大小
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
:执行任务,有返回值,一般又来执行Callable
void shutdown()∶关闭连接池
**Executors**:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
#### 分类:
FixedThreadPool:固定长度的线程池,可以控制线程的最大并发数,超出的线程会放到队列中。
SingleThreadExecutor:单线程线程池,它只会用唯一的线程来执行任务。
CachedThreadPool:可缓存的线程池,如果数据请求过多,它会不断创建新的线程,并且它还可以灵活回收空闲的线程。
ScheduledThreadPool:用于定时执行任务的线程池。
<img src="https://i-blog.csdnimg.cn/blog_migrate/f78e1fd157c6957a366bea885fc08289.png" alt="image-20221004164756853" style="zoom: 50%;" />
#### 示例代码:
```java
package Multithread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestPool {
public static void main(String[] args) {
// 1、创建服务
// 2、创建线程池
ExecutorService service= Executors.newFixedThreadPool(10);//10线程池大小
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}