🍀Process 和 Thread
程序是指令和数据的有序集合,而进程是执行程序的一次执行过程,是系统资源分配的单位。
一般在一个进程中可以包括若干个线程,一个进程中至少有一个线程。
线程是CPU调度和执行的单位。
🍀线程的创建
三种创建线程方式
1.Thread class,继承Thread类
2.Runnable接口,实现Runnable接口
3.Callable接口,实现Callable接口
☘️Thread class,继承Thread类
/**
* @author ber
* @version 1.0
* @description: 创建线程方式一:继承Thread类;重写run()方法;调用start开启线程
* @date 21/12/7 11:00
*/
class CreateThreadMethod1 extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("run线程" + i);
}
}
// 线程开启不一定立即执行,由CPU调度
public static void main(String[] args) {
// 1.创建线程对象
CreateThreadMethod1 threadMethod1 = new CreateThreadMethod1();
// 2.调用start()开启线程
threadMethod1.start();
for (int i = 0; i < 1000; i++) {
System.out.println("main主线程" + i);
}
}
}
☘️Runnable接口,实现Runnable接口
/**
* @author ber
* @version 1.0
* @description: 创建线程方式二:实现Runnable接口;重写run()方法;创建run()方法的Thread对象;调用Thread类对象的start()方法开启线程
* @date 21/12/7 13:56
*/
class CreateThreadMethod2 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("run线程" + i);
}
}
public static void main(String[] args) {
// 1.创建Runnable接口实现类对象
CreateThreadMethod2 threadMethod2 = new CreateThreadMethod2();
// // 2.创建线程对象,通过线程对象来开启线程
// Thread thread = new Thread(threadMethod2);
// // 3.调用start()开启线程
// thread.start();
// 2-3合并
new Thread(threadMethod2).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main主线程" + i);
}
}
}
☘️Callable接口,实现Callable接口
- 实现Callable接口
- 重写call()方法,需要有返回值,并抛出异常
- 创建目标对象 CreateThreadMethod3 threadMethod3 = new CreateThreadMethod3();
- 创建执行服务的线程池 ExecutorService service = Executors.newFixedThreadPool(1);
- 在线程池中提交需要执行的线程 Future\<Boolean> result = service.submit(threadMethod3);
- 获取线程执行的结果 result.get();
- 关闭线程池 service.shutdown();
/**
* @author ber
* @version 1.0
* @description: 创建线程方式三:实现Callable接口;重写call方法;创建目标对象;创建执行服务的线程池;提交需要执行的线程;获取线程的执行结构;关闭线程池
* @date 21/12/7 14:56
*/
class CreateThreadMethod3 implements Callable<Boolean> {
@Override
public Boolean call() throws Exception {
for (int i = 0; i < 10; i++) {
System.out.println("run线程" + i);
}
return true;
}
public static void main(String[] args) {
// 1.创建Callable接口实现类对象
CreateThreadMethod3 threadMethod3 = new CreateThreadMethod3();
// 2.创建执行服务的线程池
ExecutorService service = Executors.newFixedThreadPool(1);
// 3.提交执行的线程
Future<Boolean> result = service.submit(threadMethod3);
// 4.获取线程的结果
try {
Boolean aBoolean = result.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
// 5.关闭服务的线程池
service.shutdown();
for (int i = 0; i < 1000; i++) {
System.out.println("main主线程" + i);
}
}
}
☘️实现方式总结
继承Thread类
- 子类继承Thread类
- 启动线程方式:子类对象.start()
- 不推荐使用,因为继承Thread类实现多线程存在单继承局限性
实现Runnable接口
- 实现Runnable接口
- 启动线程方式,构建实现Runnable的run()方法的Thread对象.start()
- 推荐使用,避免了单继承局限性,构建线程的灵活性高,方便同一个对象被多个线程使用
实现Callable接口
- 实现Callable接口,重写call()方法
- 相比于run()方法,call()方法可以有返回值;call()方法可以抛出异常,被外面的操作捕获,获取异常的信息;Callable支持泛型的返回值
🍀线程的五大状态
- 创建状态
- 就绪状态
- 阻塞状态
- 运行状态
- 死亡状态
☘️线程方法
方法 | 说明 |
---|---|
setPriority(int newPriority) | 更改线程的优先级 |
sleep(long millis) | 让当前线程休眠millis毫秒 |
join() | 等待该线程终止 |
yield() | 暂停当前正在执行的线程对象,并执行其他线程 |
interrupt() | 中断线程 |
isAline() | 测试线程是否处于活动状态 |
☘️线程停止
这里建议让线程自己正常停止。
1.设置线程停止的标志位
2.限定线程运行次数
不建议使用JDK提供的stop或destroy等过时或JDK不建议使用的方法
/**
* @author ber
* @version 1.0
* @description: 线程停止规范:1.线程正常停止,设置次数;2.使用标志位,设置标志位;3.不建议使用stop或destroy等过时或JDK不建议使用的方法
* @date 21/12/8 10:16
*/
public class ThreadStop implements Runnable {
// 1.设置线程运行标志位
private boolean flag = true;
@Override
public void run() {
int i = 0;
while (flag) {
System.out.println("Thread-->" + i++);
}
}
// 2.设置停止线程的方法,转换标志位
public void stop() {
this.flag = false;
}
public static void main(String[] args) {
ThreadStop threadStop = new ThreadStop();
new Thread(threadStop).start();
for (int i = 0; i < 1000; i++) {
System.out.println("i=" + i);
if (i == 900) {
threadStop.stop();
System.out.println("满足线程停止条件");
}
}
}
}
☘️线程休眠
sleep():让当前线程休眠millis毫秒,当休眠时间达到后进入就绪状态。
包含InterruptedException异常。
使用sleep()可以模拟网络延时、倒计时等。
/**
* @author ber
* @version 1.0
* @description: sleep() 线程休眠 模拟倒计时
* @date 21/12/13 19:34
*/
public class ThreadSleep {
private static void printTime() {
Date date = new Date(System.currentTimeMillis());
while (true) {
try {
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date));
Thread.sleep(1000);
date = new Date(System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
int num = 3;
System.out.println("3秒后开始");
while (true) {
System.out.println(num--);
Thread.sleep(1000);
if (num <= 0) {
break;
}
}
System.out.println("获取系统时间");
printTime();
}
}
☘️线程礼让
让出线程,让当前正在执行的线程暂停,但不阻塞,线程将从运行状态装换为就绪状态。
线程礼让不一定成功,主要还是得看CPU如何去调度。
/**
* @author ber
* @version 1.0
* @description: 线程礼让,
* @date 21/12/13 20:06
*/
public class ThreadYield {
public static void main(String[] args) {
YieldTest yieldTest = new YieldTest();
new Thread(yieldTest, "a").start();
new Thread(yieldTest, "b").start();
}
}
class YieldTest implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程开始");
Thread.yield();
System.out.println(Thread.currentThread().getName() + "线程停止");
}
}
☘️线程强制执行(线程插队)
join(),合并线程,其他线程会转换成阻塞状态,需要等待此线程执行完成后,其他线程才能执行。
/**
* @author ber
* @version 1.0
* @description: join
* @date 21/12/13 20:17
*/
public class ThreadJoin implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("join --> " + i);
}
}
public static void main(String[] args) throws InterruptedException {
ThreadJoin threadJoin = new ThreadJoin();
Thread thread = new Thread(threadJoin);
thread.start();
for (int i = 0; i < 1000; i++) {
if (i == 200) {
thread.join();
}
System.out.println("main --> " + i);
}
}
}
☘️线程观测
线程可以处于以下状态之一:
- NEW :尚未启动的线程处于此状态。
- RUNNABLE :在Java虚拟机中执行的线程处于此状态。
- BLOCKED :被阻塞等待监视器锁定的线程处于此状态。
- WAITING :正在等待另一个线程执行特定动作的线程处于此状态。
- TIMED_WAITING :正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。
- TERMINATED :已退出的线程处于此状态。
一个线程可以在给定时间点处于一个状态。 这些状态是不反映任何操作系统线程状态的虚拟机状态。
/**
* @author ber
* @version 1.0
* @description: 观测测试线程的状态
* @date 21/12/13 20:25
*/
public class ThreadState {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程结束");
});
// 观测状态
Thread.State state = thread.getState();
System.out.println(state);
// 观测线程运行
thread.start();
state = thread.getState();
System.out.println(state);
while (state != Thread.State.TERMINATED) {
try {
Thread.sleep(500);
state = thread.getState();
System.out.println(state);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
☘️线程优先级
Java中提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级来决定应该调度哪个线程来执行。
首先,线程的优先级由数字表示,大小从1到10
获取线程优先级方法:getPriority()
设置线程优先级方法:setPriority()
默认线程优先级为5。
/**
* @author ber
* @version 1.0
* @description: 线程的优先级
* @date 21/12/13 20:40
*/
public class ThreadPriority {
// 主线程默认优先级
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName() + "-->" + Thread.currentThread().getPriority());
PriorityTest priorityTest = new PriorityTest();
Thread a1 = new Thread(priorityTest, "a1");
Thread a2 = new Thread(priorityTest, "a2");
Thread a3 = new Thread(priorityTest, "a3");
Thread a4 = new Thread(priorityTest, "a4");
Thread a5 = new Thread(priorityTest, "a5");
Thread a6 = new Thread(priorityTest, "a6");
a1.start();
a2.setPriority(Thread.MIN_PRIORITY);
a2.start();
a3.setPriority(Thread.NORM_PRIORITY);
a3.start();
a4.setPriority(4);
a4.start();
a5.setPriority(9);
a5.start();
a6.setPriority(Thread.MAX_PRIORITY);
a6.start();
}
}
class PriorityTest implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "-->" + Thread.currentThread().getPriority());
}
}
线程优先级高的,CPU会分配资源多一点,但优先级不一定就是线程执行顺序,优先级只意味着线程获取CPU调度的概率,最终是否被优先执行还是得取决于CPU调度。
☘️守护线程
线程分为User Thread(用户线程)、Daemon Thread(守护线程)
JVM不用等待守护线程执行完毕,但需要确保用户线程执行完毕。
JVM的守护线程包括日志记录、内存监控、垃圾回收等。
设置守护线程方式setDaemon(true)
/**
* @author ber
* @version 1.0
* @description: 守护线程
* @date 21/12/19 14:28
*/
public class ThreadDaemon {
public static void main(String[] args) {
Thread userThread = new Thread(new User());
Thread daemonThread = new Thread(new Daemon());
// 设置守护线程
daemonThread.setDaemon(true);
userThread.start();
daemonThread.start();
}
}
class User implements Runnable {
@Override
public void run() {
for (int i = 1; i < 100000; i++) {
System.out.println("用户线程执行。。。");
}
System.out.println("用户线程执行结束!!");
}
}
只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。
守护线程的作用是为其他线程的运行提供便利服务,守护线程最典型的应用就是 GC (垃圾回收器)。
🍀线程同步
多个线程操作同一资源,可能会发生冲突,这时需要线程同步机制,即各线程之间要有顺序调用,不能杂乱无章随意使用同一资源。
为了保证数据在被访问时的正确性,在访问时加入锁机制synchronized,但线程获得锁后,将独占资源,其他线程需等待,待锁被释放后可继续使用该资源。
加锁会存在以下问题:
- 一个线程加锁锁会导致其他所有需要该锁的线程挂起
- 多线程下,加锁、释放锁会导致上下文切换和调度的延时,带来性能问题
- 线程优先级倒置,优先级高的线程可能会等待一个优先级低的线程释放锁,引起性能问题。
☘️同步方法、同步块
synchronized,默认锁this
synchronized(){},锁的对象是变化的量,需要增删改的对象。
/**
* @author ber
* @version 1.0
* @description: 不加锁的并发,
* @date 21/12/19 15:40
*/
public class UnsafeSample {
public static void main(String[] args) {
Buy station = new Buy();
new Thread(station, "张三").start();
new Thread(station, "李四").start();
new Thread(station, "王五").start();
}
}
class Buy implements Runnable {
// 票数
private int ticketNums = 10;
// 线程停止标签
boolean flag = true;
@Override
public void run() {
while (flag) {
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void buy() throws InterruptedException {
if (ticketNums <= 0) {
flag = false;
return;
}
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + "买到了第" + ticketNums + "张票");
ticketNums--;
}
}
/**
* @author ber
* @version 1.0
* @description: 加锁的并发
* @date 21/12/19 16:10
*/
public class SafeSample {
public static void main(String[] args) {
SafeBuy station = new SafeBuy();
new Thread(station, "张三").start();
new Thread(station, "李四").start();
new Thread(station, "王五").start();
}
}
class SafeBuy implements Runnable {
// 票数
private int ticketNums = 10;
// 线程停止标签
boolean flag = true;
@Override
public void run() {
while (flag) {
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private synchronized void buy() throws InterruptedException {
if (ticketNums <= 0) {
flag = false;
return;
}
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + "买到了第" + ticketNums-- + "张票");
}
}
import java.util.ArrayList;
import java.util.List;
/**
* @author ber
* @version 1.0
* @description: 安全的
* @date 21/12/19 16:45
*/
public class SafeList {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
for (int i = 0; i < 100000; i++) {
new Thread(() -> {
synchronized (list) {
list.add(Thread.currentThread().getName());
}
}).start();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
/**
* @author ber
* @version 1.0
* @description: 不安全的
* @date 21/12/19 19:41
*/
class UnsafeList {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
for (int i = 0; i < 100000; i++) {
new Thread(() -> {
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
☘️JUC相关
JUC就是java.util .concurrent工具包的简称。这是一个处理线程的工具包,JDK 1.5开始出现的。目的就是为了更好的支持高并发任务,让开发者利用这个包进行的多线程编程时可以有效的减少竞争条件和死锁线程。
如CopyOnWriteArrayList集合,本身就是线程安全的。
import java.util.concurrent.CopyOnWriteArrayList;
/**
* @author ber
* @version 1.0
* @description: JUC安全类型的集合
* @date 21/12/19 20:28
*/
public class JUC {
public static void main(String[] args) {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
☘️死锁
多个线程各自占有一些共享资源,并且互相等待其线程占有的资源才能运行,而导致两个或多个线程都在等待对方释放资源,都停止执行的场景。
/**
* @author ber
* @version 1.0
* @description: 死锁
* @date 21/12/19 21:18
*/
public class DeadLock {
public static void main(String[] args) {
Borrow b1 = new Borrow(0, "张三");
Borrow b2 = new Borrow(1, "李四");
b1.start();
b2.start();
}
}
// 书籍1
class Book1 {
}
// 书籍2
class Book2 {
}
// 借书
class Borrow extends Thread {
// 需要的资源只有一份,这里用static保证资源只有一份
static Book1 book1 = new Book1();
static Book2 book2 = new Book2();
int bookId;
String userName;
Borrow(int bookId, String userName) {
this.bookId = bookId;
this.userName = userName;
}
@Override
public void run() {
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void makeup() throws InterruptedException {
if (bookId == 0) {
// 获得book1的锁
synchronized (book1) {
System.out.println(this.userName + "获得book1锁");
Thread.sleep(1000);
synchronized (book2) {
System.out.println(this.userName + "获得book2锁");
}
}
// synchronized (book2) {
// System.out.println(this.userName + "获得book2锁");
// }
} else {
// 获得book1的锁
synchronized (book2) {
System.out.println(this.userName + "获得book2锁");
Thread.sleep(2000);
synchronized (book1) {
System.out.println(this.userName + "获得book1锁");
}
}
// synchronized (book1) {
// System.out.println(this.userName + "获得book1锁");
// }
}
}
}
线程会相互等待对方释放资源,导致死锁。
这里解决方法是将获得锁的对象写在外面
if (bookId == 0) {
// 获得book1的锁
synchronized (book1) {
System.out.println(this.userName + "获得book1锁");
Thread.sleep(1000);
}
synchronized (book2) {
System.out.println(this.userName + "获得book2锁");
}
} else {
// 获得book1的锁
synchronized (book2) {
System.out.println(this.userName + "获得book2锁");
Thread.sleep(2000);
}
synchronized (book1) {
System.out.println(this.userName + "获得book1锁");
}
}
}
🪢死锁产生条件
死锁产生的四个必要条件:
- 互斥条件:一个资源每次只能被一个进程使用
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
- 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
解决死锁的方法就是需要破坏其中一个或多个条件。
☘️Lock(锁)
与synchronized
的对比
synchronized
是隐性锁,看不到开始和结束。Lock
是通过显式定义同步锁对象来实现同步的,需要手动释放锁。Lock
只有代码锁块,synchronized
有代码锁块和方法锁- 使用
Lock
锁,JVM将花费较少的时间来调度线程,性能更好,并具有更好的扩展性。具有很多的子类 - 优先使用顺序:Lock 、 同步代码块、 同步方法
/**
* @author ber
* @version 1.0
* @description: Lock
* @date 21/12/20 11:49
*/
public class Lock {
public static void main(String[] args) {
LockTest lockTest = new LockTest();
new Thread(lockTest, "张三").start();
new Thread(lockTest, "李四").start();
new Thread(lockTest, "王五").start();
}
}
class LockTest implements Runnable {
int ticketNums = 10;
// 定义lock锁
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
// 加锁
lock.lock();
try {
if (ticketNums > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNums-- + "张票");
} else {
break;
}
} finally {
// 解锁
lock.unlock();
}
}
}
}
🍀线程协作
☘️线程通信
- 生产者消费者模式
生产者消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件。
Java提供几个方法解决线程之间的通信问题的方法
方法名 | 作用 |
---|---|
wait() | 表示线程一直等待,知道其他线程通知,与sleep不同,会释放锁 |
wait(long timeout) | 指定等待的毫秒数 |
notify() | 唤醒一个处于等待状态的线程 |
notifyAll() | 唤醒同一个对象所调用wait()方法的线程,优先级高的线程优先调度 |
均属于object类的方法,都只能在同步方法或者同步代码块中使用,否者会抛出异常。
解决方法
- 管程法
/**
* @author ber
* @version 1.0
* @description: 管程法 实现生产者/消费者模式。
* 1. 需要四种角色:生产者、消费者、产品、缓冲区
* 2. 生产者生产产品放到缓冲区,缓冲区如果满了,生产者停止运作,进入等待
* 3. 消费者从缓冲区拿产品,如果缓冲区产品没有了,先唤醒生产者,然后进入等待
* @date 21/12/20 12:30
*/
public class ProductorAndConsumer {
public static void main(String[] args) {
SynContainer container = new SynContainer();
new Productor(container).start();
new Consumer(container).start();
}
}
// 生产者
class Productor extends Thread {
SynContainer container;
public Productor(SynContainer container) {
this.container = container;
}
// 生产
@Override
public void run() {
for (int i = 0; i < 100; i++) {
container.push(new Product(i));
}
}
}
// 消费者
class Consumer extends Thread {
SynContainer container;
public Consumer(SynContainer container) {
this.container = container;
}
// 消费
@Override
public void run() {
for (int i = 0; i < 100; i++) {
container.pop();
}
}
}
// 产品
class Product {
// 产品编号
int id;
public Product(int id) {
this.id = id;
}
public int getId() {
return id;
}
}
// 缓冲区
class SynContainer {
// 容器大小
Product[] chickens = new Product[10];
// 容器计数器
int count = 0;
// 生产者放入产品
public synchronized void push(Product product) {
// 如果容器满了,就需要等待消费者消费
if (count == chickens.length) {
//生产者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 如果没有满,我们需要丢入产品
chickens[count] = product;
count++;
System.out.println("生产了第"+product.getId()+"个产品,当前容器容量:"+count+"个");
// 可以通知消费者消费了.
this.notifyAll();
}
// 消费者消费product
public synchronized Product pop() {
// 判断能否消费
if (count == 0) {
// 消费者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 如果可以消费
count--;
Product product = chickens[count];
System.out.println("消费了-->" + product.getId() + "产品,容器剩余:"+count);
// 缓存区消费完了,通知生产者生产
this.notifyAll();
return product;
}
}
- 信号灯法
/**
* @author ber
* @version 1.0
* @description: 信号灯法 即生产即消费
* @date 22/1/4 14:59
*/
public class Signal {
public static void main(String[] args) {
Market market = new Market();
new ProductorS(market).start();
new ConsumerS(market).start();
}
}
// 生产者
class ProductorS extends Thread {
Market market;
public ProductorS(Market market) {
this.market = market;
}
@Override
public void run() {
for(int i=0;i<100;i++) {
market.in(i);
}
}
}
// 消费者
class ConsumerS extends Thread {
Market market;
public ConsumerS(Market market) {
this.market = market;
}
@Override
public void run() {
for(int i=0;i<100;i++) {
market.out();
}
}
}
//
class Market {
// 有货标志位
// 有:T,生产者等待
// 无:F,消费者等待
boolean flag = false;
int id;
// 生产
public synchronized void in(int id) {
if (flag) {
try {
this.wait();
} catch (InterruptedException e ) {
e.printStackTrace();
}
}
this.id = id;
this.flag = !this.flag;
this.notifyAll();
System.out.println("生产者生产 " + id);
}
public synchronized void out() {
if (!flag) {
try {
this.wait();
} catch (InterruptedException e ) {
e.printStackTrace();
}
}
this.flag = !this.flag;
this.notifyAll();
System.out.println("消费者消费 "+this.id);
}
}
🍀线程池
线程池即可以将提前创建好线程放入线程池,使用时直接从线程池中获取,线程用完后再放入线程池中。
如此可以避免线程频繁创建/销毁带来的系统开销,实现资源重复利用。
- 提高响应速度,减少线程创建时间
- 降低资源消耗,重复利用线程池中线程,即用即取,不需要每次创建
- 便于管理线程
☘️线程池使用
- ExecutorService接口:Java标准库提供,表示线程池。
// 创建固定大小为3的线程池:
ExecutorService executor = Executors.newFixedThreadPool(3);
// 提交任务:
executor.submit(task1);
executor.submit(task2);
executor.submit(task3);
executor.submit(task4);
executor.submit(task5);
Java标准库提供的几个常用实现类有:
- FixedThreadPool:线程数固定的线程池;
- CachedThreadPool:线程数根据任务动态调整的线程池;
- SingleThreadExecutor:仅单线程执行的线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author ber
* @version 1.0
* @description: 线程池
* @date 22/1/4 17:02
*/
public class ThreadPool {
public static void main(String[] args) {
// 1.创建固定大小的线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
executorService.execute(new MyThread(i));
}
// 2.关闭线程池
executorService.shutdown();
}
}
class MyThread implements Runnable {
private int id;
MyThread(int id){
this.id = id;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}