程序:用某种语言编写的一种指令的集合,即一段静态代码
进程:是程序的一次执行过程。或是正在运行的一个程序,一个动态的过程。
线程:进程进一步细化的结果,是一个程序内部的一条执行路径
虚拟机栈和程序计数器是每一个线程一份
方法区和堆是一个进程一份,多个线程共享一个进程中的方法区和堆
并行:多个CPU同时执行多个任务
并发:一个CPU采用时间片同时执行多个任务
提高应用程序的响应
提高计算机CPU的利用率
改善程序结构,将既长又复杂的进程分为多个线程
程序需要执行两个或多个任务
程序需要实现一些需要等待的任务,如用户输入,文件读写,网络操作,搜索等
需要后台运行的程序
Thread类中的start方法有两个作用:
启动当前线程
调用当前线程的run()方法
/**
* “@Auther: yaoyunfeng
*
* @Date: 2022/7/18 - 07 - 18 - 23:30
* @Description: PACKAGE_NAME
* @version: 1.0
*
* 1.创建一个继承自Thread类的子类
* 2.重写Thread类中的run方法 ——> 将此线程执行的操作声明在run()方法中
* 3.创建Thread类的子类的对象
* 4.调用此对象的start()方法
* start启动一个线程后 , 不可以再启动同一个线程
*
*/
class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0){
System.out.println(i);
}
}
}
}
public class ThreadCreated {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
for (int i = 0; i < 100; i++) {
if (i % 2 == 0){
System.out.println(i + "--------------------");
}
}
}
}
采用匿名内部类方式创建线程——创建三个线程
/**
* “@Auther: yaoyunfeng
*
* @Date: 2022/7/18 - 07 - 18 - 23:30
* @Description: PACKAGE_NAME
* @version: 1.0
*
* 1.创建一个继承自Thread类的子类
* 2.重写Thread类中的run方法 ——> 将此线程执行的操作声明在run()方法中
* 3.创建Thread类的子类的对象
* 4.调用此对象的start()方法
*
*/
public class ThreadInnerClassCreateed {
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0){
System.out.println(i + "--------------------" + Thread.currentThread().getName());
}
}
new Thread(){
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0){
System.out.println(i + "偶数" + Thread.currentThread().getName());
}
}
}
}.start();
new Thread(){
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 != 0){
System.out.println(i + "奇数" + Thread.currentThread().getName());
}
}
}
}.start();
}
}
class HelloThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0){
System.out.println(i + Thread.currentThread().getName());
}
}
}
}
public class ThreadMethodTest {
public static void main(String[] args) {
HelloThread helloThread = new HelloThread();
// 给我们的线程命名
helloThread.setName("线程1");
helloThread.start();
// 给main线程命名
Thread.currentThread().setName("主线程");
for (int i = 0; i < 100; i++) {
if (i % 2 == 0){
System.out.println(i + Thread.currentThread().getName());
}
}
}
}
1.start():启动当前线程 调用当前线程的run方法
2.run()方法通常 需要重写Thread类中的此方法 , 将创建的线程执行的操作声明在此方法中
3.currentThread():静态方法 返回执行当前代码的线程
4.getName():获取当前线程的名字
5.setName():设置当前线程的名字
6.yield():释放当前CPU执行权
7.join():在线程A中调用线程B的join()方法 , 此时 线程A进入阻塞状态 , 直到线程B完全执行完以后 , 线程A才结束阻塞状态。
8.stop():强制结束当前线程 此方法已过时
9.sleep(long time): 让当前线程睡眠指定的milltime毫秒。在指定毫秒时间内,当前线程是阻塞状态。
10.isAlive():判断当前线程是否还存活
Thread类中的三个枚举代表的优先级:
MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5 默认。
getPriority():查看线程的优先级
setPriority(int p):设置线程的优先级
说明:高优先级的线程要抢占低优先级的线程的CPU执行权 , 但是只是从概率上讲 ,高优先级的线程高概率的情况下被执行,并不以围着只有高优先级的线程执行完后,低优先级就线程才执行。
package created.two;
/**
* “@Auther: yaoyunfeng
*
* @Date: 2022/7/24 - 07 - 24 - 22:13
* @Description: created.two
* @version: 1.0
*
* 创建多线程的方式二 : 实现Runnable接口
* 1. 创建一个实现了Runnable接口的类
* 2. 实现类去实现Runnable中的抽象方法 run()
* 3. 创建实现类的对象
* 4. 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
* 5. 通过Thread类的对象调用start()方法
*
*/
class MThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0){
System.out.println(i + ":" + Thread.currentThread().getName());
}
}
}
}
public class ThreadCreatedRunnable {
public static void main(String[] args) {
MThread mThread = new MThread();
Thread thread1 = new Thread(mThread);
thread1.setName("线程1");
thread1.start();
// 再启动一个线程遍历100内的偶数
Thread thread2 = new Thread(mThread);
thread2.setName("线程2");
thread2.start();
}
}
开发中优先推荐选择实现Runnable接口的方式创建一个线程
原因:
- 实现的方式不会受类的单继承的局限
- 实现的方式更适合来处理多个线程有共享数据的情况。因为我们构建的多个线程相当于 可以传入同一个实现了Runnable接口的类的对象,这样天然我们对象中的变量就是多个线程共享的
联系:
两种方式都需要重写run()方法,将线程要执行的逻辑声明在run()方法中。

为什么需要线程同步呢?
因为多个线程同时操作共享数据的话,会导致数据的不正确性。
就比如抢票,我们很明显可以知道票是共享的变量。多个线程同时操作这个变量,就会出现重票、错票的情况,这时我们就需要使用锁去同步线程。下面是集中线程同步的方法。
synchronized(同步监视器){
需要被同步的方法
}
1、 针对实现Runnable接口的方式的多线程。
package lock;
/**
* “@Auther: yaoyunfeng
*
* 创建三个窗口卖票 , 总票数为100张
*
* 问题:卖票过程中,出现了重票、错票 ——> 出现了线程的安全问题
* 出现原因:
* ticket票这个变量被多个线程共享,当某个线程操作车票的过程中,尚未操作完成,其他线程参与进来,也操作车票
*
* 解决:加锁;当一个线程在操作票的时候,其他线程不能参与进来,直到线程A操作完ticket,其他线程才可以开始操作ticket。
* 这种情况,及时线程A出现阻塞也不能被改变。
*
* java中通过同步机制解决线程安全问题:
* 方式一:
* 同步代码块:
* synchronized(同步监视器){
* 需要被同步的代码
* }
* 说明:操作共享数据的代码即为需要同步的代码。 --> 不能包含代码多了也不能包含代码少了。
* 共享数据:多个线程共同操作的变量。比如该问题中得ticket就是共享数据。
* 同步监视器:俗称锁。任何一个类的对象都可以充当锁。
* 要求:多个线程必须共用同一把锁。
* 补充:在实现Runnable接口创建的多线程的方式中,我们可以考虑将this作为同步监视器。
*
*
*
* 方式二:
* 同步方法:
* 如果操作共享数据的代码完整的声明在一个方法中,我们可以将此方法声明一个同步的。
*
*
* 同步的方式:解决了线程的安全问题。--好处
* 操作同步代码时只能有一个线程参与,其他线程等待,相当于单线程,效率低。 -- 局限性
*
*
* @Date: 2022/7/24 - 07 - 24 - 21:57
* @Description: created.two
* @version: 1.0
*/
class WindowRunnable implements Runnable{
private int ticket = 100;
Object object = new Object();
@Override
public void run() {
while (true){
synchronized(object) { /** this即为调用该方法的对象,在这里也就是WindowRunnable类的对象,在我们的main中只创建过一个windowRunnable对象,所以在这里this也是唯一的*/
// synchronized(object) {
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + ": 卖票,票号为:" + ticket);
ticket--;
} else {
break;
}
}
}
}
}
public class WindowTestRunnable {
public static void main(String[] args) {
WindowRunnable windowRunnable = new WindowRunnable();
Thread window1 = new Thread(windowRunnable);
Thread window2 = new Thread(windowRunnable);
Thread window3 = new Thread(windowRunnable);
window1.setName("窗口1");
window2.setName("窗口2");
window3.setName("窗口3");
window1.start();
window2.start();
window3.start();
}
}
2、针对继承Thread类的实现方式
package lock;
/**
* “@Auther: yaoyunfeng
*
* 创建三个窗口卖票 , 总票数为100张
* 使用同步代码快解决继承Thread类的线程安全问题
*
* 在继承Thread类创建多线程的方式中,慎用this充当同步监视器,可以考虑使用当前类做同步监视器类.class。
*
* @Date: 2022/7/24 - 07 - 24 - 21:57
* @Description: created.two
* @version: 1.0
*/
class WindowThread extends Thread{
private static int ticket = 100;
static Object object = new Object();
@Override
public void run() {
while (true){
// synchronized (this){ // 错误的,在这里this即WindowThread的对象,在main中我们创建了三个WindowThread对象,在这里就不唯一了
// synchronized(WindowThread.class){ // 正确的,反射 , 类只会加载一次,意味着只有一个是唯一的。
synchronized(object){ // 正确的
if (ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + ": 卖票,票号为:" + ticket);
ticket --;
}else {
break;
}
}
}
}
}
public class WindowTestThread {
public static void main(String[] args) {
WindowThread window1 = new WindowThread();
WindowThread window2 = new WindowThread();
WindowThread window3 = new WindowThread();
window1.setName("窗口1");
window2.setName("窗口2");
window3.setName("窗口3");
window1.start();
window2.start();
window3.start();
}
}
private synchronized void show(){}
1、实现Runnable接口的方式
package lock;
/**
* 使用同步方法解决实现Runnable接口的线程安全问题。
*
* 同步方法扔涉及同步监视器,只是不需要我们显示的声明
* 非静态的同步方法,同步监视器是this
* 静态的同步方法,同步监视器是当前类本身。
* @Auther: yaoyunfeng
* @Date: 2022/7/31 - 07 - 31 - 23:26
* @Description: lock
* @version: 1.0
*/
class WindowRunnable3 implements Runnable {
private int ticket = 100;
Object object = new Object();
@Override
public void run() {
while (true) {
show();
}
}
private synchronized void show(){ // 同步监视器就是this
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + ": 卖票,票号为:" + ticket);
ticket--;
}
}
}
public class WindowTestRunnableFunction {
public static void main(String[] args) {
WindowRunnable3 windowRunnable = new WindowRunnable3();
Thread window1 = new Thread(windowRunnable);
Thread window2 = new Thread(windowRunnable);
Thread window3 = new Thread(windowRunnable);
window1.setName("窗口1");
window2.setName("窗口2");
window3.setName("窗口3");
window1.start();
window2.start();
window3.start();
}
}
2、继承Thread类的方式
package lock;
/**
* “@Auther: yaoyunfeng
*
* 创建三个窗口卖票 , 总票数为100张
* 使用同步方法解决继承Thread类的线程安全问题
*
*
*
* @Date: 2022/7/24 - 07 - 24 - 21:57
* @Description: created.two
* @version: 1.0
*/
class WindowThread2 extends Thread {
private static int ticket = 100;
@Override
public void run() {
while (true) {
show();
}
}
private static synchronized void show(){ // 同步监视器是 当前类 WindowThread2.class
// private synchronized void show(){ // 这里同步监视器默认是this 目前有三个。 此种解决方式是错误的。
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + ": 卖票,票号为:" + ticket);
ticket--;
}
}
}
public class WindowTestThreadFunction {
public static void main(String[] args) {
WindowThread2 window1 = new WindowThread2();
WindowThread2 window2 = new WindowThread2();
WindowThread2 window3 = new WindowThread2();
window1.setName("窗口1");
window2.setName("窗口2");
window3.setName("窗口3");
window1.start();
window2.start();
window3.start();
}
}
不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源就形成了死锁。
出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态。无法继续。
package lock;
/**
* 演示线程死锁的问题
*
* 1、死锁的理解
* 不同的线程都在等待对方的同步资源,不放弃自己的资源。
*
* 出现死锁不报错不抛异常。所有线程处于阻塞状态。
* 尽量避免。
*/
public class DeadLock {
public static void main(String[] args) {
StringBuffer s1 = new StringBuffer();
StringBuffer s2 = new StringBuffer();
new Thread() {
@Override
public void run() {
synchronized (s1) {
s1.append("a");
s2.append("1");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (s2){
s1.append("b");
s2.append("2");
System.out.println(s1);
System.out.println(s2);
}
}
}
}.start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (s2) {
s1.append("c");
s2.append("3");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (s1){
s1.append("d");
s2.append("4");
System.out.println(s1);
System.out.println(s2);
}
}
}
}).start();
}
}
如何避免死锁;
专门的算法,原则
尽量减少同步资源的定义
尽量避免嵌套同步
lock用法 , 我们在需要同步的代码起始位置加 lock() , 在结束同步的位置加unlock()解锁代码即可。
lock和synchronized的异同:
相同:都是解决线程不安全的方式
不同:lock需要手动调用方法进行锁定与解锁 , synchronized则是自动进行解锁和锁定的。
package lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 解决线程安全问题三 、 Lock锁。 jdk5新增。
*
* synchronized 与 lock 的异同:
* 同:都可解决线程安全问题
* 不同:
* synchronized : 在执行完相应的代码逻辑后 , 自动的释放同步监视器
* Lock:需要我们手动的启动同步,lock() 结束同步也需要手动调用 unlock()
*
* 解决线程安全的方式
* synchronized 同步代码块
* synchronized 同步方法
* lock锁
*
*/
class Window implements Runnable{
private int ticket = 100;
// 1、实例化Lock
private ReentrantLock lock = new ReentrantLock(true);
@Override
public void run() {
while (true){
try {
// 2、调用锁定 lock方法
lock.lock();
if (ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + ":售票,票号为:" + ticket);
ticket--;
}else {
break;
}
}finally {
// 解锁
lock.unlock();
}
}
}
}
public class LockTest {
public static void main(String[] args) {
Window window = new Window();
Thread thread1 = new Thread(window);
Thread thread2 = new Thread(window);
Thread thread3 = new Thread(window);
thread1.setName("窗口1");
thread2.setName("窗口2");
thread3.setName("窗口3");
thread1.start();
thread2.start();
thread3.start();
}
}
假设一种场景。
使用两个线程打印1到100 , 线程1 , 线程2 , 交替打印。
这时候我们就需要用到线程的通信。
我们介绍一下wait() notify() notifyAll()。这三个方法都是Object类中的方法。
首先我们想让线程1和线程2交替打印 ,那么我们就需要其中一个线程打印后阻塞,让下一个线程打印,下一个打印完,上一个要释放。
我们看一下下面的例子。
package communication;
/**
* 线程通信的例子:使用两个线程打印1——100.线程1 , 线程2 , 交替打印。
*
* 涉及到的三个方法:
* wait(): 一旦执行此方法,当前线程进入阻塞状态,并释放同步监视器
* notify():一旦执行此方法,就会唤醒被wait的一个线程,如果有多个线程被wait() , 就唤醒优先级高的那个
* notifyAll():一旦执行此方法,就会唤醒所有被wait()的线程
*
* 说明:
* 1、wait notify notifyAll 这三个方法必须使用在synchronized的同步代码块,或同步方法中。
* 2、notify和wait以及notifyAll三个方法的调用者必须是同步代码块或同步方法中的同步监视器
* 否则会出现 IllegalMonitorStateException 异常
*
* 3、这三个方法是定义在Object方法下的。
*
*
*/
class Number implements Runnable {
private int number = 1;
@Override
public void run() {
while (true) {
synchronized (this) {
this.notify();
if (number <= 100) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + ":" + number);
number++;
try {
// 使得调用wait方法的线程进入阻塞状态。 执行 wait() 会释放锁。
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
} else break;
}
}
}
}
public class CommunicationTest {
public static void main(String[] args) {
Number number = new Number();
Thread thread1 = new Thread(number);
Thread thread2 = new Thread(number);
thread1.setName("线程1");
thread2.setName("线程2");
thread1.start();
thread2.start();
}
}
注意: 这里我们需要强调几点:
1、首先我们的这三个通信方法都是Object类中的。
2、其次,我们的通信方法wait() notify() notifyAll() 这三个方法必须使用在synchronized的同步代码块,或同步方法中。
3、最后,我们的wait() 等这三个方法必须是我们同步监视器的对象去调用。否则会出现 IllegalMonitorStateException 异常
我们来介绍一下这三个方法的作用
- wait():释放锁(同步监视器) , 阻塞调用他的线程。
- notify():唤醒wait的其中一个线程,如果是多个wait线程,则唤醒优先级比较高的一个。
- notifyAll():唤醒所有wait的线程。
注意点: sleep()和wait()的异同:
相同点:
- sleep()和wait()都会使得当前线程进入阻塞状态。
不同点:
- sleep()声明在Thread类中,wait()声明在object类中。
- sleep()在任何位置都可以调用 , wait()只能在同步代码块或同步方法中调用。
- sleep()和wait()都在同步代码块或同步方法中被调用时, sleep()会阻塞不会释放锁,wait阻塞同时会释放锁。
经典例题:
package communication;
import com.sun.xml.internal.bind.v2.model.core.ID;
/**
* 生产者为店员提供产品,而消费者取走产品
* 店员一次只能持有固定数量的产品,如果生产者视图生产更多产品,会叫停生产者,同样,如果没有商品了,消费者需要等待。
*
* 生产者线程,消费者线程。
* 产品是共享数据。
* 处理线程安全问题:synchronized , lock
* 线程通信问题:
*/
class Clerk{
private int productCount = 0;
// 生产产品
public synchronized void produceProduct() {
if (productCount < 20){
productCount++;
System.out.println(Thread.currentThread().getName() + ":开始生产第" + productCount + "个产品");
// notify();
}else {
try {
wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
// 消费产品
public synchronized void consumerProduct() {
if (productCount > 0){
System.out.println(Thread.currentThread().getName() + ":开始消费第" + productCount + "个产品");
productCount--;
// notify();
}else {
try {
wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
class Producer extends Thread{
private Clerk clerk;
public Producer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
System.out.println("生产者" + Thread.currentThread().getName() + "开始生产产品。。。。");
while (true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
clerk.produceProduct();
}
}
}
class Consumer extends Thread{
private Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
while (true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
clerk.consumerProduct();
}
}
}
public class ProductTest {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Producer producer1 = new Producer(clerk);
producer1.setName("生产者1");
Consumer consumer1 = new Consumer(clerk);
consumer1.setName("消费者1");
producer1.start();
consumer1.start();
}
}
callable与runnable的区别:
使用callable的方式创建线程步骤如下:
1、创建一个类 实现callable接口。
2、重写call方法
3、实现callable接口的对象放入 futureTask 构造器中 , 创建futureTask对象。
4、创建Thread类对象,将futureTask对象放到Thread构造器中。
5、调用start方法启动线程。
6、获取callable实现类中call方法的返回值,futureTask.get()
package created.three;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
*
* 创建线程的方式三:实现callable接口的方式 jdk5.0新增的方法
*
* 1、创建一个实现callable接口的实现类
* 2、实现call方法 , 将此线程需要执行的操作声明在call方法中。
* 3、创建callable接口实现类的对象
* 4、将此callable接口实现类的对象传到FutureTask构造器中,创建FutureTask的对象
* 5、将FutureTask对象传入到Thread类构造器参数中 , 创建Thread类对象,并调用start
* 6、获取callable实现类中call方法的返回值,futureTask.get()
*
*
*
* “@Auther: yaoyunfeng
*
* @Date: 2022/8/2 - 08 - 02 - 23:00
* @Description: created.three
* @version: 1.0
*/
class NumberThread implements Callable{
@Override
public Object call() throws Exception {
int sum = 0;
for (int i = 1; i <= 100; i++) {
if (i % 2 == 0){
System.out.println(i);
sum+=i;
}
}
return sum;
}
}
public class ThreadCallable {
public static void main(String[] args) {
NumberThread numberThread = new NumberThread();
FutureTask futureTask = new FutureTask(numberThread);
new Thread(futureTask).start();
try {
// get方法返回值即为FutureTask构造函数参数Callable对象的重写的call方法返回值
Object sum = futureTask.get();
System.out.println("总和为:" + sum);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
}
提到线程池,我们就需要想一想,为什么需要线程池,线程池有什么好处呢,什么情况下我们使用线程池呢?
接下来我来和大家一起分享一下线程池的使用场景。
package created.four;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @Author 姚云峰
* @Email starcpdk@163.com
* @Date 2022/8/4 12:11
* @Version 1.0
*/
class NumberThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0){
System.out.println(Thread.currentThread().getName() + "---" + i);
}
}
}
}
class NumberThread1 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 != 0){
System.out.println(Thread.currentThread().getName() + "---" +i);
}
}
}
}
public class ThreadPool {
public static void main(String[] args) {
// 提供指定线程数量的线程池
ThreadPoolExecutor executorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
// 设置线程池的属性:
executorService.setCorePoolSize(15);
// executorService.setKeepAliveTime();
// 执行指定的线程的操作。需要提供实现runnable接口或callable接口实现类的对象。
// executorService.submit(); // 适合使用于callable
executorService.execute(new NumberThread()); // 适合使用于runnable的方式
executorService.execute(new NumberThread1()); // 适合使用于runnable的方式
// 关闭连接池
executorService.shutdown();
}
}
线程池:
newCachedThreadPool():

newFixedThreadPool():

newSingleThreadPool():

那么为什么阿里巴巴不推荐使用这三种创建线程池呢?
下面我们来说说线程池中的提交优先级和执行优先级:
提交优先级:核心线程 —— 队列 —— 非核心线程
执行优先级:核心线程 —— 非核心线程 —— 队列
总结: