一、线程的简介

线程、进程、多线程

Process与Thread

1、程序是指令和数据的有序集合,进程的一次执行过程,是一个动态的概念,是系统资源分配的单位
2、一个进程可以包含若干个线程,当一个进程中至少有一个线程,
3、线程是CPU调度和执行的单位

1、线程就是独立执行的路径
2、main()称之为主线程,为系统的入口,用于执行整个程序
3、在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的操作,4、先后顺序是不能人为的干预的。
5、对于同一份资源操作时,会存在资源抢夺的情况,需要加入并发控制
6、线程会带来额外的开销,如cpu调度时间,并发控制开销
7、每个线程在自己的工作内存交互,内存控制不当,会引起数据的不一致

run方法和start方法的对比

image.png

二、线程的实现(重点)

线程的创建
image.png

创建方法一:继承Thread

步骤:
1、自定义线程类继承Thread类
2、重写run()方法,编写线程执行体
3、创建线程对象,调用start()方法启动线程

  1. //创建线程的方法一:继承Thread类,重写run()方法,调用start()方法开启线程
  2. //注意,线程开启不一定运行,由CPU调度
  3. public class TestThread1 extends Thread {
  4. //线程入口
  5. @Override
  6. //run方法属于子线程
  7. public void run() {
  8. //run方法体(线程体)
  9. for (int i = 0; i < 200; i++) {
  10. System.out.print("我在看代码------" + i);
  11. }
  12. }
  13. //main属于主线程
  14. public static void main(String[] args) {
  15. //创建线程对象
  16. TestThread1 testThread1 = new TestThread1();
  17. //调用start方法和调用run方法的区别
  18. //调用start(),线程是同时进行运行的
  19. //如果testThread1.run(),则先运行run在运行main()
  20. testThread1.start();
  21. for (int i = 0; i < 1000; i++) {
  22. System.out.print("我在学习多线程------" + i);
  23. }
  24. }
  25. }
  1. //需要引用commons-io架包
  2. import org.apache.commons.io.FileUtils;
  3. import java.io.File;
  4. import java.io.IOException;
  5. import java.net.URL;
  6. public class TestThread2 extends Thread {
  7. private String url;
  8. private String name;
  9. //构造器
  10. public TestThread2(String url, String name) {
  11. this.url = url;
  12. this.name = name;
  13. }
  14. @Override
  15. public void run() {
  16. WebDownloader webDownloader = new WebDownloader();
  17. webDownloader.downloader(url, name);
  18. System.out.print("下载的文件名称为:" + name);
  19. }
  20. public static void main(String[] args) {
  21. String urltemp="https://t7.baidu.com/it/u=1819248061,230866778&fm=193&f=GIF";
  22. TestThread2 testThread2 = new TestThread2(urltemp,"TESTNAME1");
  23. testThread2.start();
  24. }
  25. //下载器
  26. class WebDownloader{
  27. //下载方法
  28. public void downloader(String url,String name){
  29. try {
  30. FileUtils.copyURLToFile(new URL(url), new File(name));
  31. } catch (IOException e) {
  32. e.printStackTrace();
  33. System.out.print("IO异常,downloader方法出现问题");
  34. }
  35. }
  36. }
  37. }

创建方法二:定义MyRunnable类实现Runnable接口

步骤
1、定义MyRunnable类实现Runnable接口
2、实现run()方法,编写线程执行
3、创建线程对象,调用start()方法启动线程
推荐使用线程对象、
对比第一种方式,推荐这种,因为java单继承具有局限性,其实Thread也是实现类Runnable接口,
步骤
继承Thread类

继承Thread类 实现Runnable接口
子类继承Thread类具备多线程能能力 实现接口Runnable具有多线程能力
启动线程:子类对象。start() 启动线程:传入目标对象+Thread对象。start()
不建议使用,避免OOP单继承局限性 推荐使用,避免局限性,同一对象可被多个线程使用
  1. public class TestThread3 implements Runnable {
  2. //创建线程的方式2:实现runnable,重写run方法,执行线程需要丢入runnable接口实现类,调用start方法
  3. @Override
  4. public void run() {
  5. for (int i = 0; i < 200; i++)
  6. System.out.print("我在看代码----" + i);
  7. }
  8. public static void main(String[] args) {
  9. //创建runnable接口的实现类对象
  10. TestThread3 testThread3=new TestThread3();
  11. //创建线程对象,通过线程对象来开启线程
  12. //Thread thread = new Thread(testThread3);
  13. //thread.start();
  14. Thread t1 = new Thread(testThread3);
  15. t1.start()//开启线程
  16. for (int i = 0; i < 300; i++)
  17. System.out.println("我在学习多线程" + i);
  18. }
  19. }

创建方法三:实现Callable接口(了解即可)

image.png

匿名内部类创建线程

匿名内部类方式实现线程的创建
作用:简化代码
把子类继承父类,重写父类方法,创建子类对象合成一步完成
把实现类实现接口,重写接口中的方法,创建实现类对象合成一步完成
匿名内部类的最终产物:子类/实现类对象,而这个类没有名字
格式:
new 父类/接口() {
重写父类方法
};

  1. public static void main(String[] args) {
  2. //线程的父类是Thread
  3. //new MyThread().start();
  4. new Thread(){
  5. @Override
  6. public void run() {
  7. for (int i = 0; i < 10; i++) {
  8. System.out.println(Thread.currentThread().getName()+ "-->"+i);
  9. }
  10. }
  11. }.start();
  12. //线程的接口
  13. Runnable r = new Runnable(){
  14. @Override
  15. public void run() {
  16. for (int i = 0; i < 10; i++) {
  17. System.out.println(Thread.currentThread().getName()+ "-->"+"程序员");
  18. }
  19. }
  20. };
  21. new Thread(r).start();
  22. //接口的线程简化
  23. new Thread(new Runnable(){
  24. @Override
  25. public void run() {
  26. for (int i = 0; i < 10; i++) {
  27. System.out.println(Thread.currentThread().getName()+ "-->"+"林枫");
  28. }
  29. }
  30. }).start();
  31. }

静态代理

静态代理,,模式终结
要求:
1、真实对象和代理对象要实现同一个接口
2、代理对象必要代理真实角色
静态代理实际上就是线程的实现原理
好处:
1、代理对象可以帮助真实对象做很多事情
2、真实对象可以专注做好自己的事情

  1. package com.company;
  2. //静态代理,,模式终结
  3. //真是对象和代理对象又要实现同一个接口
  4. //代理对象必要要代理真实角色
  5. //好处:
  6. //1、代理对象可以帮助真实对象做很多事情
  7. //2、真实对象可以专注做好自己的事情
  8. public class StaticProxy{
  9. public static void main(String[] args) {
  10. You you=new You();
  11. new Thread(new Runnable() {
  12. @Override
  13. public void run() {
  14. System.out.println("我爱你");
  15. }
  16. }).start();
  17. WeddingCompany weddingCompany = new WeddingCompany(you);
  18. weddingCompany.HapplyMarry();
  19. }
  20. }
  21. //定义一个接口,让真实对象实现
  22. interface Marry {
  23. void HapplyMarry();
  24. }
  25. class You implements Marry{
  26. @Override
  27. public void HapplyMarry(){
  28. System.out.println("张三要结婚");
  29. }
  30. }
  31. //代理角色,帮助你结婚
  32. class WeddingCompany implements Marry{
  33. //代理——真实目标
  34. private Marry target;
  35. public WeddingCompany(Marry target) {
  36. this.target = target;
  37. }
  38. @Override
  39. public void HapplyMarry() {
  40. before();
  41. this.target.HapplyMarry();//这是真实对象
  42. after();
  43. }
  44. private void after(){
  45. System.out.println("结婚之后,收取尾款");
  46. }
  47. private void before(){
  48. System.out.println("结婚之前,布置现场");
  49. }
  50. }

Lamda表达式

image.png
image.png
image.png
image.png

  1. public class TestLamda {
  2. public static void main(String[] args) {
  3. //使用匿名内部类
  4. Ilove love=new Ilove(){
  5. public void love(){
  6. System.out.println("i love you");
  7. }
  8. };
  9. love.love();
  10. }
  11. }
  12. interface Ilove {
  13. public void love();
  14. }
  1. //使用lamda表达式代替
  2. public class TestLamda {
  3. public static void main(String[] args) {
  4. //使用匿名内部类
  5. ILove ilove = () -> {
  6. System.out.println("i love you");
  7. };
  8. ilove.love();
  9. }
  10. }
  11. interface ILove {
  12. void love();
  13. }

三、线程的状态

NEW 初始状态 尚未启动的线程处于此状态
RUNNABLE 运行状态 在JAVA虚拟机中执行的线程处于此状态,Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。
线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)
BLOCKED 阻塞 被阻塞等待监视器的线程处于此状态
WAITING 等待 在等待另一个线程执行动作到达指定等待时间的线程处于此状态
TIMED WAITING 超时状态 正在等待另一个线程执行动作到达指定等待时间
TERMINATED 终止 表示该线程已经执行完毕,已退出的线程处于此状态

JAVA多线程 - 图8

image.png
image.png

几种方法的比较

  1. Thread.sleep(long millis),一定是当前线程调用此方法,当前线程进入TIMED_WAITING状态,但不释放对象锁,millis后线程自动苏醒进入就绪状态。作用:给其它线程执行机会的最佳方式。
  2. Thread.yield(),一定是当前线程调用此方法,当前线程放弃获取的CPU时间片,但不释放锁资源,由运行状态变为就绪状态,让OS再次选择线程。作用:让相同优先级的线程轮流执行,但并不保证一定会轮流执行。实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。Thread.yield()不会导致阻塞。该方法与sleep()类似,只是不能由用户指定暂停多长时间。
  3. thread.join()/thread.join(long millis),当前线程里调用其它线程t的join方法,当前线程进入WAITING/TIMED_WAITING状态,当前线程不会释放已经持有的对象锁。线程t执行完毕或者millis时间到,当前线程一般情况下进入RUNNABLE状态,也有可能进入BLOCKED状态(因为join是基于wait实现的)。
  4. obj.wait(),当前线程调用对象的wait()方法,当前线程释放对象锁,进入等待队列。依靠notify()/notifyAll()唤醒或者wait(long timeout) timeout时间到自动唤醒。
  5. obj.notify()唤醒在此对象监视器上等待的单个线程,选择是任意性的。notifyAll()唤醒在此对象监视器上等待的所有线程。
  6. LockSupport.park()/LockSupport.parkNanos(long nanos),LockSupport.parkUntil(long deadlines), 当前线程进入WAITING/TIMED_WAITING状态。对比wait方法,不需要获得锁就可以让线程进入WAITING/TIMED_WAITING状态,需要通过LockSupport.unpark(Thread thread)唤醒。

    停止线程

    注意:
    1、不推荐使用JDK提供的stop()、destroy()方法。已经弃用
    2、推荐线程自己停下来
    3、建议使用一个标志位进行终止变量,当flag=false,则终止线程运行

    1. import java.awt.*;
    2. public class TestStop implements Runnable {
    3. //1、设置一个标志位
    4. boolean flag=true;
    5. int i = 0;
    6. @Override
    7. public void run() {
    8. while (flag) {
    9. System.out.println(i++);
    10. }
    11. }
    12. //2、设置一个公开的方法停止线程,转换标志位
    13. public void stopWhile() {
    14. this.flag = false;
    15. }
    16. public static void main(String[] args) {
    17. TestStop testStop = new TestStop();
    18. new Thread(testStop).start();
    19. for (int i = 0; i < 2; i++) {
    20. System.out.println("main"+i);
    21. if (i == 1) {
    22. //调用stopWhile方法切换标志位,使得线程停止
    23. testStop.stopWhile();
    24. System.out.println("线程停止了");
    25. }
    26. }
    27. }
    28. }

    线程休眠

  7. Sleep(time)方法,指定当前线程阻塞的毫秒数

  8. sleep存在异常InterruptedException
  9. sleep时间达到后线程进入倒计时就绪状态
  10. sleep可以模拟网络延时效果,倒计时等
  11. 每个对象都有一个锁,sleep不会释放锁

作用:
1、模拟网络延时
2、模拟倒计时

  1. import java.text.SimpleDateFormat;
  2. import java.util.Date;
  3. import java.util.logging.SimpleFormatter;
  4. public class TestSleep2 {
  5. public static void main(String[] args) {
  6. //获取当前系统时间
  7. Date startTime = new Date(System.currentTimeMillis());
  8. while (true) {
  9. try {
  10. //模拟延时
  11. Thread.sleep(1000);
  12. System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
  13. startTime = new Date(System.currentTimeMillis());
  14. } catch (InterruptedException e) {
  15. e.printStackTrace();
  16. }
  17. }
  18. }
  19. //模拟倒计时
  20. public static void tenDown() throws InterruptedException {
  21. int num = 10;
  22. while (true) {
  23. Thread.sleep(1000);
  24. System.out.println(num--);
  25. if (num <= 0) {
  26. break;
  27. }
  28. }
  29. }
  30. }

线程的礼让

  1. 礼让线程,让当前的正在执行的线程暂停,但不阻塞
  2. 将线程从运行状态转为就绪状态
  3. 让CPU重新调度,礼让不一定成功,需要根据CPU实际的调度

image.png

线程的强制执行

Jion()方法合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞
可以想象成插队

image.png

线程状态的观测

Thread.State
线程状态,线程可以处于以下状态之一:
NEW

NEW 尚未启动的线程处于此状态
RUNNABLE 在JAVA虚拟机中执行的线程处于此状态
BLOCKED 被阻塞等待监视器的线程处于此状态
WAITING 在等待另一个线程执行动作到达指定等待时间的线程处于此状态
TIMED WAITING 正在等待另一个线程执行动作到达指定等待时间
TERMINATED 已退出的线程处于此状态

一个线程可以在给定的时间点处于一个状态,这些状态是不反映任何操作系统线程状态的虚拟机状态

  1. public class TestState {
  2. public static void main(String[] args) throws InterruptedException {
  3. Thread thread = new Thread(() -> {
  4. for (int i = 0; i < 5; i++) {
  5. try {
  6. Thread.sleep(1000);
  7. } catch (InterruptedException e) {
  8. e.printStackTrace();
  9. }
  10. System.out.println("/////////////");
  11. }
  12. });
  13. //观察状态
  14. Thread.State state = thread.getState();
  15. System.out.println(state);
  16. //观察启动后
  17. thread.start();
  18. state = thread.getState();
  19. System.out.println(state);
  20. //只要线程不终止
  21. while (state != Thread.State.TERMINATED) {
  22. Thread.sleep(100);
  23. state = thread.getState();//跟新线程状态
  24. System.out.println(state);
  25. }
  26. }
  27. }

线程的优先级

JAVA提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行
线程的优先级用数字表示。范围从1-10

Thread. MIN_PRIORITY 1
Thread. MAX_PRIORITY = 10 10
Thread. NORM_PRIORITY 5

1、优先级低只是意味着获得调度的概率低,并不是优先级低就不会被调用,取决于CPU调度
2、优先级的更改建议在start()之前
使用以下方法可以改变或获取优先级

  1. getPriority().stePriority(int XXX);

优先级最大是10,最小是1,默认程序的优先级是5

  1. //测试线程的优先级
  2. public class TestPriority implements Runnable {
  3. @Override
  4. public void run() {
  5. System.out.println(Thread.currentThread().getName()+"----"+Thread.currentThread().getPriority());
  6. }
  7. public static void main(String[] args) {
  8. TestPriority testPriority = new TestPriority();
  9. Thread t1 = new Thread(testPriority,"t1");
  10. Thread t2 = new Thread(testPriority,"t2");
  11. Thread t3 = new Thread(testPriority,"t3");
  12. Thread t4 = new Thread(testPriority,"t4");
  13. Thread t5 = new Thread(testPriority,"t5");
  14. //设置优先级
  15. t2.setPriority(1);
  16. t3.setPriority(Thread.MAX_PRIORITY);
  17. t4.setPriority(6);
  18. t1.start();
  19. t2.start();
  20. t3.start();
  21. t4.start();
  22. t5.start();
  23. }
  24. }

输出结果:
image.png

守护线程

daemon,守护线程,
线程分为,用户线程和守护线程
1、虚拟机必须确保用户线程执行完毕
2、虚拟不用等待守护线程执行完毕
3、如后台记录操作日志,监控内存,垃圾回收等待等

四、线程的同步(重点)

多个线程操作同一个资源
image.png
image.png
线程同步的条件:队列和锁

龟兔赛跑

  1. package com.company;
  2. public class Race implements Runnable {
  3. private static String winner;
  4. @Override
  5. public void run(){
  6. //预设跑道长度是100米
  7. for (int i = 0; i <= 100; i++) {
  8. //判断比赛是否结束
  9. boolean flag = gameOver(i);
  10. if (flag){
  11. System.out.println(i);
  12. break;
  13. }
  14. System.out.println(Thread.currentThread().getName()+"-->跑了"+i+"米");
  15. //模拟兔子睡觉
  16. if (Thread.currentThread().getName().equals("兔子") && i % 10 == 0) {
  17. try {
  18. Thread.sleep(500);
  19. } catch (InterruptedException e) {
  20. e.printStackTrace();
  21. }
  22. }
  23. }
  24. }
  25. private boolean gameOver(int steps) {
  26. if (winner != null) {
  27. return true;
  28. }else if (steps >= 100) {
  29. winner = Thread.currentThread().getName();
  30. System.out.println(winner+"赢得比赛");
  31. return true;
  32. }
  33. return false;
  34. }
  35. public static void main(String[] args) {
  36. Race race = new Race();//
  37. new Thread(race,"乌龟").start();//乌龟的线程实例对象
  38. new Thread(race, "兔子").start();//兔子的线程实例对象
  39. }
  40. }

并发问题

不安全的买票

  1. package com.company;
  2. public class SafeBank implements Runnable{
  3. private int ticketNums = 10;
  4. Boolean flag = true;//外部停止条件
  5. @Override
  6. public void run() {
  7. while (flag){
  8. try {
  9. buy();
  10. } catch (InterruptedException e) {
  11. e.printStackTrace();
  12. }
  13. }
  14. }
  15. public void buy() throws InterruptedException {
  16. //判断是否售完票
  17. if (ticketNums == 0) {
  18. flag = false;
  19. return;
  20. }
  21. //模拟延时
  22. Thread.sleep(10);
  23. //输出抢到票的结果
  24. System.out.println(Thread.currentThread().getName()+"-->拿到了第" + ticketNums-- + "张票");
  25. }
  26. public static void main(String[] args) {
  27. SafeBank safeBank = new SafeBank();
  28. new Thread(safeBank,"张三").start();
  29. new Thread(safeBank, "黄牛").start();
  30. }
  31. }
  32. package com.company;
  33. //输出结果是有人拿到了同一张票
  34. public class TestThead2 implements Runnable {
  35. private int ticketNums = 10;
  36. @Override
  37. public void run() {
  38. while (true) {
  39. //判断是否售完票
  40. if (ticketNums <= 0) {
  41. break;
  42. }
  43. //模拟延时
  44. try {
  45. Thread.sleep(10);
  46. } catch (InterruptedException e) {
  47. e.printStackTrace();
  48. }
  49. //输出抢到票的结果
  50. System.out.println(Thread.currentThread().getName()+"-->拿到了第" + ticketNums-- + "张票");
  51. }
  52. }
  53. public static void main(String[] args) {
  54. TestThead2 testThead2 = new TestThead2();
  55. new Thread(testThead2,"张三").start();
  56. new Thread(testThead2, "黄牛").start();
  57. }
  58. }

输出的结果:
image.png

不安全的数组

  1. import java.util.ArrayList;
  2. import java.util.List;
  3. //不安全的数组
  4. public class UnsafeList {
  5. public static void main(String[] args) {
  6. final List<String> list = new ArrayList<String>();
  7. for (int i = 0; i < 2; i++) {
  8. new Thread(new Runnable() {
  9. @Override
  10. public void run() {
  11. list.add(Thread.currentThread().getName());
  12. }
  13. }).start();
  14. }
  15. System.out.print(list.size());
  16. }
  17. }

输出结果:
image.png

不安全的银行取款

  1. public class UnsafeBank {
  2. public static void main(String[] args) {
  3. //账户
  4. Accout accout = new Accout(100, "the fund");
  5. Drawing you=new Drawing(accout,50,"you");
  6. Drawing grilFrriend = new Drawing(accout, 100, "GF");
  7. you.start();
  8. grilFrriend.start();
  9. }
  10. }
  11. class Accout{
  12. int money;//余额
  13. String name;//卡名
  14. public Accout(int money, String name) {
  15. this.money = money;
  16. this.name = name;
  17. }
  18. }
  19. class Drawing extends Thread{
  20. Accout accout;//账户
  21. int drawingMoney;//取了多少钱
  22. int nowMoney;//现在手上有多少钱
  23. public Drawing(Accout accout, int drawingMoney, String name) {
  24. super(name);
  25. this.accout = accout;
  26. this.drawingMoney = drawingMoney;
  27. }
  28. //取钱
  29. @Override
  30. public void run() {
  31. //判断有没有钱
  32. if (accout.money - drawingMoney < 0) {
  33. System.out.print(Thread.currentThread().getName() + "钱不够,取不了");
  34. return;
  35. }
  36. //卡内余额=余额-你取出来的钱
  37. accout.money = accout.money - drawingMoney;
  38. //你手里的钱
  39. nowMoney = nowMoney + drawingMoney;
  40. System.out.println(accout.name + "余额:" + accout.money);
  41. //Thread.currenthread().getName() 等价于 this.getName()
  42. System.out.println(this.getName()+"手里的钱");
  43. //Thread.currenthread().getName() 等价于 this.getName()
  44. System.out.println(this.getName());
  45. }
  46. }

队列和锁

同步方法

由于我们可以通过private关键字来保证数据对象只能被方法访问,所以只需要针对方法提出一套机制,这套机制就是synchroized关键字,包括两种方法:
synchronized关键字修饰和synchronized块

  1. public synchronized void method(int args){}

//syhchronized方法控制对象的访问,每个对象对应一把锁,每个syhchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程就会阻塞,方法一旦执行,就独占该锁,知道方法返回才会释放锁,后面的被阻塞的线程才能获得这个锁,继续执行

缺陷:若将一个大的方法声明为synchroized将会影响效率,synchronized方法只锁定当前this对象,

安全的购票方式

  1. package com.company;
  2. public class SafeBank implements Runnable{
  3. private int ticketNums = 10;
  4. Boolean flag = true;//外部停止条件
  5. @Override
  6. public void run() {
  7. while (flag){
  8. try {
  9. buy();
  10. } catch (InterruptedException e) {
  11. e.printStackTrace();
  12. }
  13. }
  14. }
  15. public synchronized void buy() throws InterruptedException {
  16. //判断是否售完票
  17. if (ticketNums == 0) {
  18. flag = false;
  19. return;
  20. }
  21. //模拟延时
  22. Thread.sleep(10);
  23. //输出抢到票的结果
  24. System.out.println(Thread.currentThread().getName()+"-->拿到了第" + ticketNums-- + "张票");
  25. }
  26. public static void main(String[] args) {
  27. SafeBank safeBank = new SafeBank();
  28. new Thread(safeBank,"张三").start();
  29. new Thread(safeBank, "黄牛").start();
  30. }
  31. }

同步块

同步块:sychronized(Objr){}
Obj称之为同步监视器
obj可以是任意对象了,但是推荐使用共享资源作为同步监视器
同步方法中无需指定同步监视器,因为同步方法中同步监视器是this,就是对象本身,或者是claa(反射中讲解)
同步监视器的执行过程:
1、第一个线程访问,锁定同步监视器,执行其中的代码
2、第二个线程访问,发现同步监视器被锁定,无法访问
3、第一个线程访问结束,解锁同步监视器
4、第二个线程访问,发现同步监视器没有锁,然后锁定并继续访问

五、线程通信

六、高级主题

线程池

背景:经常创建喝销毁,使用量特别大的资源,比如并发情况下的线程,对性能影响很大
思路:提前创建好多个线程,放入线程池中吗,使用时直接获取,使用完毕放回池中,可以避免频繁创建和销毁,实现重复利用,
好处:

  1. 提高响应速度,减少创建线程的时间
  2. 降低资源消耗
  3. 便于线程管理

    1. coerPoolSize,核心池大小
    2. maximumPoolSize,最大线程数
    3. keepAliveTIme,线程没有任务的时候最多长时间会终止

      相关API

  4. JDK5.0,提供了线程相关的API:ExecutorService和Executors

  5. ExrcutorService:真正的线程池的接口,常见的子类ThreadPoolExecutor
    1. void

image.png