Java多线程详细基础教程 全套

多线程详细基础教程

多线程必掌握的重点知识点

  1. 多线程,进程,程序之间的区别
  2. 多线程生命周期
  3. 多线程常用方法
  4. 多线程创建方式
  5. 多线程的线程安全问题(数据共享,同步synchronized,Lock,锁的概念)
  6. 多线程死锁
  7. 多线程协作(消费者生产者)
  8. volatile关键字
  9. 线程池

一、多线程的基本概念


1. 线程、进程、程序之间的区别

  • 定义

    • 进程:运行中的程序,操作系统会分配进程给该程序
    • 程序:静态的代码
    • 线程:不能独立存在,线程是存在在进程中,一个进程可以有多个线程
  • 区别

    • 进程:是由cpu创建并分配独立的存储空间,每一个进程都有独立的存储空间
    • 多线程:有独立的运行堆栈和程序计数器,但没有独立的存储空间,共享进程的存储空间
    • 多线程:有独立的运行堆栈和程序计数器,但没有独立的存储空间,共享进程的存储空间
    • 线程是一个轻量级的进程,必须依赖于进程的存在,线程之间的切换相对快一些
    • 进程是一个重量级,有独立的存储空间,进程之间的切换就要慢一些

2. 线程的生命周期

在这里插入图片描述

  • Thread t = new MyThread();//创建一个线程,只是空的线程对象,并没有分配运行堆栈

  • t.start();//启动一个线程,使线程进入就绪状态,才分配独立的执行堆栈

  • run();//运行run(),就是运行状态

  • run()执行完毕,就是线程结束状态


3. 线程的状态及转换

在这里插入图片描述

  1. 新建状态 new
  2. 就绪状态 start(),阻塞完毕,或者调用其他线程,yield()
  3. 运行状态 由CPU统一管理执行,什么时候进入运行状态不受程序员的控制
  4. 结束状态 当run()执行完毕的时候,线程结束,或者出现异常程序退出
  5. 阻塞状态 sleep(),wait()等
  • 新建—》就绪

  • 阻塞—》就绪

  • 就绪—》运行

  • 运行—》就绪

  • 运行—》阻塞

  • 运行—》结束

二、多线程的使用


1.多线程的创建

  1. implements Runnable(推荐)

​ 必须实现public void run(){}

​ 还要结合Thread才能使用线程

public class MyThread implements Runnable{
	int i = 0;
	public void run() {
		while(i < 100) {
			System.out.println(i);
			i++;
		}
	}
}


        MyThread mt = new MyThread();//这个类实现了Runnable接口
		Thread t = new Thread(mt);//定义一个线程类封装
		t.start();//启动线程,使线程进入就绪状态
  1. extends Thread
public class MyThread2 extends Thread{
	int i = 0;
	public void run() {
		while(i < 1000) {
			System.out.println("线程2:"+i);
			i++;
		}
	}
}

MyThread2 mt2 = new MyThread2();
mt2.start();
  1. Callable接口
    • 优点:可以在线程执行完提供返回值FutrueTask

2.多线程的方法

  1. start() ;//启动线程,是线程进入就绪状态,等待CPU调度执行
  2. sleep(long ms);//使线程进入阻塞状态,等待指定时间后,使线程进入就绪状态
  3. getId();//获取线程id值
  4. run();//线程体,当线程执行的时候,执行该方法
  5. getName();//获取线程的名称,该名称可以指定,可以通过构造方法指定Thread(String name)或Thread(Runnable r,String name)
  6. getPriority();//获取线程的优先级,默认的优先级是5 ,范围1-10
  7. setPriority();//线程的优先级只是给cpu一个建议,优先级高不能保证优先执行
  8. isAlive();//判断线程是否是存活状态,线程结束isAlive()就是false,其他都是true
  9. yield();//线程正在执行,让线程让出CPU的使用,回到就绪状态
  10. wait();//Object的方法
  11. notify();//Object的方法
  12. Thread.currentThread().getName();//获取当前线程的名字

3.多线程的应用

  1. join() 加入线程
    • 当一个线程A加入到另外一个线程B中,必须确保A线程执行完,才能继续执行B线程
public class MyThread extends Thread {
    public MyThread(){

    }
    public void run(){
        int i = 0;
        while(i < 10){
            System.out.println("B子线程执行" + i);
            i++;
        }
    }
}
public class Test01 {
    public static void main(String[] args) {
        //创建子线程
        Thread t = new MyThread();
        //启动子线程
        t.start();
        /*try {
            //使用该方式也可以增大主线程最后才被执行完,但:
            //1.无法控制时间确保主线程100%最后执行
            //2.休眠时间长可能会产生浪费时间的情况
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }*/
        try {
            //把线程t加入到主线程中,确保t一定执行完后,才能继续执行主线程
            t.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("A主线程结束......");
    }
}

  1. setDaemon()设置守护进程
    • 守护线程:当主线程结束的时候,守护线程也会跟着退出
public class MyThread2 extends Thread {
    public MyThread2(){
        //设置当前线程为守护线程,当主线程结束时,该线程也会退出
        setDaemon(true);
    }

    public void run(){
        while(true){
            System.out.println("B子线程执行...");
        }
    }
}
public class Test02 {
    public static void main(String[] args) {
        //创建子线程
        Thread t = new MyThread2();
        //启动子线程
        t.start();
        System.out.println("A主线程结束......");
    }
}

三、线程的数据共享与同步

  • 吃包子:张三、李四、王五3个人共吃10个包子

  • 分析:张三、李四、王五就是3个线程,如何保证一起吃10个包子

    实现:

public class Baozi implements  Runnable{
    //加上static,这个变量就是一个共享变量,所有的线程都共享这一个变量
    static int baozi = 10;
    @Override
    public void run() {
        while (baozi > 0){
            System.out.println(Thread.currentThread().getName() + "吃第" + baozi + "个包子...");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            baozi--;
        }
    }
    public static void main(String[] args) {
        Thread t1 = new Thread(new Baozi() , "张三");
        Thread t2 = new Thread(new Baozi() , "李四");
        Thread t3 = new Thread(new Baozi() , "王五");
        t1.start();
        t2.start();
        t3.start();
    }
}
王五吃第10个包子...
张三吃第10个包子...
李四吃第10个包子...
张三吃第8个包子...
李四吃第8个包子...
王五吃第7个包子...
王五吃第6个包子...
李四吃第4个包子...
张三吃第4个包子...
王五吃第3个包子...
李四吃第2个包子...
张三吃第1个包子...

注意:以上的输出和我们需求是不一致的,3个人同吃了一个包子,就是在数据共享的时候,产生了多线程的数据不安全性的


1.数据共享的问题

  • 线程安全: StringBuffer 、Vector 、HashMap

  • 线程不安全: StringBuilder、ArrayList、HashTable,当存在多线程的时候,共享数据不保证其数据的准确性

    线程不安全其实就是数据的不安全性

  • 分析数据不安全的原因:

  1. 线程执行的不确定性
  2. 多线程存在共享数据
  3. 线程交替执行

注意:当多个线程同时操作同一个数据的时候,有可能会出现数据不安全的情况,要保证数据安全,可以采用同步方式。

2.同步的概念

(1)同步和异步的区别

  • 异步: 多线程交替执行,可以充分的利用cpu,提升效率,缺点可能会造成数据的不安全性

  • 同步: 多线程排队执行,优点:保证数据的安全性,缺点:执行效率相对比较低

​ 当一个线程执行完同步代码后,另外一个线程才能进入执行

同步在一些特殊情况下不一定比异步的效率低,因为异步会存在频繁的切换线程,这时就需要把先线程的状态保存以及进行恢复,这些都有可能会浪费效率,所以说同步不一定比异步效率低。

(2)锁的概念

  • 锁就是应用在同步上,锁:原子性、排斥性,可见性等特点

  • 同步synchronized,应用锁lock

(3)同步的应用

  • 同步代码块

    synchronized(){  //类、对象等都可以作为锁,一般的锁应该都是线程共享的锁
        //需要同步的同步代码块
    }
    
    
    public class Baozi implements  Runnable{
        //加上static,这个变量就是一个共享变量,所有的线程都共享这一个变量
        static int baozi = 10;
        static Object lock = new Object();//锁,多个线程共享这一把锁
        @Override
        public void run() {
                while (baozi > 0) {
                    //同步代码块
                    synchronized (lock) {
                        //加判断,确保baozi>0,才能吃
                        if(baozi > 0 ) {
                            System.out.println(Thread.currentThread().getName() + "吃第" + baozi + "个包子...");
                            baozi--;
                        }
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
      
                }
        }
    }
    
    
  • 同步方法锁

    //代表当前方法是个同步方法,当一个线程执行该方法,其他的线程必须等待此线程的方法执行完成才能进入/该方法
    
    //非静态同步方法,是把当前对象作为锁
    public synchronized  void eat(){  
    
    }
    
    //静态同步方法,是把当前类作为锁
    public synchronized static void eat(){
    
    }
    

3.synchronized底层原理(了解)

jdk1.6之后的原理,jdk1.6之前synchronized只是一个重量级锁,1.6以后进行了优化,效率有明显的提升

第一阶段:偏向锁,第一次由哪个线程抢到锁,锁修改一个锁的标识位(偏向锁01),当只有一个线程的时候,他会减少锁的获取,锁偏向该线程,不需要重新获取该锁,可以直接进入同步代码块执行

第二阶段:当有其他的线程参与争抢该锁时,锁就会修改标志位变成轻量级锁(自旋锁,不停的循环判断锁是否被释放),自旋锁主动的判断锁是否可以被获取,效率相对比较高,但是判断的次数是有一定限制的,当超过一定次数后,自旋就停止,进入阻塞等待状态(从主动判断锁是否被释放变成被动等待锁被释放后通知),进入第三阶段

第三阶段:重量级锁,所有的线程只有等待锁释放后,锁会通知所有线程可以重新过来抢锁

锁的膨胀(升级): 偏向锁-----》轻量级锁(自旋锁)---->重量级锁,该过程不可逆


4.可重入锁(比较常用的一种方式)Lock接口

ReentrantLocksynchronized区别

ReentrantLock是手动的操作锁

synchronized是自动的操作锁

  • 创建锁:Lock lock = new ReentrantLock();

  • 上锁:lock.lock();

  • 释放锁: lock.unlock();//使用lock一定要执行该方法

公平锁和非公平锁区别

  • 非公平锁: 线程的执行是不可预知的,谁能抢就执行谁

  • 公平锁: 线程的执行是公平的,每个线程会按照次序公平均衡的执行

哪个锁好是需要看业务场景的,大多数情况为了执行效率,会采用非公平锁,假如为了保证线程均衡执行会采用公平锁

  • synchronized是非公平锁

  • ReentrantLock既可以是公平锁,也可以是非公平锁,默认是非公平锁

  • new ReentrantLock(boolean fair);//true是公平锁,false就是非公平锁,默认是false

  • 可重入锁: 当线程进入,只有当前线程可以重复获取同一把锁

​ lock.lock();//第一次上锁

​ eat();

​ lock.unlock();//必须释放锁,一定在finally语句块

public void eat(){

​ lock.lock();//第二次执行的,已经在第一次获取到该锁,第二次可以直接能继续获取该锁

}

四、线程的死锁

  • 只有在线程同步(锁)的时候,才有可能出现死锁。

  • 死锁的产生: 当多个线程在获取锁的时候进入阻塞状态,等待其他锁的释放而进入永久等待

public class DeadLock {
    private static Object lock1 = new Object();
    private static Object lock2 = new Object();

    private static class T1 extends Thread{
        @Override
        public void run() {
            synchronized (lock1){
                System.out.println("线程1获取到第一把锁......");
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock2){
                    System.out.println("线程1获取到第二把锁....");
                }
            }
        }
    }
    private static class T2 extends Thread{
        @Override
        public void run() {
            synchronized (lock2){
                System.out.println("线程2获取到第二把锁......");
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock1){
                    System.out.println("线程2获取到第一把锁....");
                }
            }
        }
    }

    public static void main(String[] args) {
        new T1().start();
        new T2().start();
    }
}

避免死锁:

1. 建议不要使用嵌套的环形锁

当出现嵌套锁的时候,假如获取到锁的顺序是一致的,不会产生死锁的问题

public class DeadLock {
    private static Object lock1 = new Object();
    private static Object lock2 = new Object();

    private static class T1 extends Thread{
        @Override
        public void run() {
            synchronized (lock1){
                System.out.println("线程1获取到第一把锁......");
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock2){
                    System.out.println("线程1获取到第二把锁....");
                }
            }
        }
    }
    private static class T2 extends Thread{
        @Override
        public void run() {
            synchronized (lock1){
                System.out.println("线程2获取到第二把锁......");
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock2){
                    System.out.println("线程2获取到第一把锁....");
                }
            }
        }
    }

    public static void main(String[] args) {
        new T1().start();
        new T2().start();
    }
}

假如有多把锁的时候,建议给锁进行排序,根据排序安排使用锁的顺序,这样所有线程就不会产生死锁的问题

2. 可以使用lock接口

lock.lock()------->lock.tryLock(int value , int timeunit)//会在单位的时候内假如没有获取到该锁就会放弃,不会一直在阻塞状态

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Baozi3 implements  Runnable{
    //加上static,这个变量就是一个共享变量,所有的线程都共享这一个变量
    static int baozi = 10;
    static Lock lock = new ReentrantLock(true);
    @Override
    public void run() {
            while (baozi > 0) {
                    //手动上锁
                try {
                    //lock.lock();
                    if(lock.tryLock(1, TimeUnit.SECONDS)) {
                        //加判断,确保baozi>0,才能吃
                        if (baozi > 0) {
                            System.out.println(Thread.currentThread().getName() + "吃第" + baozi + "个包子...");
                            baozi--;
                        }
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //使用可重入锁,必须要手动释放锁
                    lock.unlock();
                }
            }
    }
}

五、线程的协作

线程协作经典实例(生产者和消费者问题)

  • 需求: 假如有2个生产者生成热狗,有3个消费者吃热狗,生产到10个热狗时停止,消费者吃光的时候不能继续消费。 消费者和生成者是不是有个协作关系,消费者负责消费,生产者负责生成,消费和生产都是同一个商品

  • Object类中有3个方法(是与线程有关系的): wait(),notify(),notifyAll(),主要用在锁对象上

    • wait();//使线程进入阻塞状态

    • notify();//通知处于wait状态(阻塞状态)的线程可以继续执行了

    • notifyAll();//通知所有处于wait状态的线程都可以继续执行了

import java.util.ArrayList;
import java.util.List;

/**
 * 假如有2个生产者生成热狗,有3个消费者吃热狗,生产到10个热狗时停止,消费者吃光的时候不能继续消费。
 * 消费者和生成者是不是有个协作关系,消费者负责消费,生产者负责生成,消费和生产都是同一个商品
 */
public class Test01{
    //定义装热狗的容器,也是一把锁
    private static List<Integer> hotDogs = new ArrayList<>();
    //生产者线程
    private static class Producer extends Thread{
        int i = 1;
        int pid;
        public Producer(int pid){
            this.pid = pid;
        }
        public void run(){
            while(true){
                synchronized (hotDogs){
                    if(hotDogs.size() < 10){
                        System.out.println(pid + "号生产者生产" + (pid*1000 + i) + "号热狗");
                        hotDogs.add(pid*1000 + i);
                        i++;
                        hotDogs.notifyAll();//通知消费者已经有热狗了,你不要wait了,可以继续执行吃热狗
                        try {
                            Thread.sleep(200);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }else{
                        System.out.println("热狗已经满了10个。。。。。,暂停生产");
                        try {
                            hotDogs.wait();//热狗已经满了,强制生产者休息...
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
    //消费者线程
    private static class Consumer extends Thread{
        int cid;
        public Consumer(int cid){
            this.cid = cid;
        }
        public void run(){
            while(true){
                synchronized (hotDogs){
                    if(hotDogs.size() > 0){
                        System.out.println(cid + "消费者吃" + hotDogs.remove(0) + "号热狗");
                        hotDogs.notifyAll();//已经被我吃了一个,通知所有的生成者不要wait了,可以继续生成热狗
                        try {
                            Thread.sleep(200);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }else{
                        System.out.println("热狗数为0,已经不足,,,,,,");
                        try {
                            hotDogs.wait();//热狗没有了,强制消费者休息
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

    public static void main(String[] args) {
        for (int i = 1; i <= 2 ; i++) {
            new Producer(i).start();
        }
        for (int i = 1; i <=3 ; i++) {
            new Consumer(i).start();
        }
    }
}

注意:生成者和消费者一定是公用一把锁,才能达到通知等待的效果,否则notify和wait没有作用的

六、volatile关键字


1.作用

  1. 线程可见性

    class RunThread extends Thread{
    volatile boolean b = true;//可见性
    int x = 0;
    public void run() {
    	while(b) {
    		x++;
    	}
    	System.out.println(x);
    	System.out.println("线程停止。。。");
    }
    public void setB(boolean b) {
    	this.b = b;
    }
    }
    

public class Test01 {
public static void main(String[] args) {
RunThread r = new RunThread();
r.start();
try {
Thread.sleep(20);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
r.setB(false);
}
}

2. 禁止指令重排

- cpu有可能会根据相应的优化条件,会对一些语句的顺序进行调整,这种指令重排

​```java
 //这种指令重排在一般情况或者单线程下是没有影响的,但在一些多线程的特殊条件下有可能会出现问题。
 int a = 1;
 int b = 2;
 int c = a + b ;//假如有依赖关系的指令不会进行重排
package ch501.c03;

public class TestVola extends Thread{
	static int a = 1;
	static boolean b = false;
	
	public void change() {
		a = 2;
		//有可能会出现指令重排的问题,这是b = true优先a=2执行,那么在run()中有可能输出的就是1而不是2
		b = true; 
	}
	
	public void run() {
		if(b) {
			System.out.println(a);//有可能结果是1也可能是2
		}
	}
}

如何解决由于指令重排可能造成的结果的不确定性?

使用volatile关键字。

public class TestVola extends Thread{
	volatile static int a = 1;//使用volatile禁止指令重排,a=2就一定在b=true前边执行
	static boolean b = false;
	
	public void change() {
		a = 2;
		//有可能会出现指令重排的问题,这是b = true优先a=2执行,那么在run()中有可能输出的就是1而不是2
		b = true; 
	}
	
	public void run() {
		if(b) {
			System.out.println(a);//有可能结果是1也可能是2
		}
	}
}

2.不足

  • 不能保证原子性
public class TestVola2 {
	volatile static int i = 0;//volatile不能保证原子性
	
	static void change() {
		i++;//不具备原子性 (1)i=0 (2)0+1=1 (3)i=1
	}
	
	public static void main(String[] args) {
		for(int j = 0 ; j < 10000 ; j++) {
			new Thread(new Runnable() {
				@Override
				public void run() {
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					change();	
				}
			}).start();
		}
	
		System.out.println(i);
	}

}

我们想要的 结果应该是10000,但实际的效果是<=10000,如何解决。

import java.util.concurrent.atomic.AtomicInteger;

public class TestVola2 {
	//volatile static int i = 0;//volatile不能保证原子性
	volatile static AtomicInteger i = new AtomicInteger(0);//原子性整数值
	
	static void change() {
		//i++;//不具备原子性 (1)i=0 (2)0+1=1 (3)i=1
		i.incrementAndGet();//该操作具备原子性
	}

	public static void main(String[] args) {
		for(int j = 0 ; j < 10000 ; j++) {
			new Thread(new Runnable() {
				@Override
				public void run() {	
					change();	
				}
			}).start();
		}
		/*try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}*/
        while(Thread.activeCount() > 1) {
			System.out.println(Thread.activeCount());
			Thread.yield();
		}
		System.out.println(i);
	}

}

七、线程池

  • 线程池: 放多线程的池子。是多线程创建的一种常用方式。

  • 创建线程的3种方式:

    • (1)extends Thread

    • (2)implements Runnable

    • (3)implements Callable (多线程可以有返回值)

    • 上边3种的缺点:

      • (1)new Thread()新建一个对象性能比较低

      • (2)线程缺乏统一的管理,有可能会无限制的创建线程,造成线程之前互相争抢资源,造成cpu切换的浪费的资源以及内存资源的浪费,当线程过多,可能会出现OOM(内存溢出)

      • (3)功能比较单一,不能定时执行

        针对以上问题,可以使用线程池来解决。

        JDK提供了几种线程池的现成方案:(全是基于ThreadPoolExecutor)

  • Excutors提供的方法,返回的值是ExcutorService

​ int corePoolSize,
​ int maximumPoolSize,
​ long keepAliveTime,
​ TimeUnit unit,
​ BlockingQueue workQueue

  1. newCachedThreadPool缓存线程池

​ corePoolSize 0

​ maximumPoolSize Integer.MAX_VALUE

​ keepAliveTime 60L

​ unit TimeUnit.SECONDS

​ workQueue SynchronousQueue

public class Test {

	public static void main(String[] args) {
		//缓存型线程池
		ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
		for (int i = 1; i <= 100; i++) {
			final int index = i;
			newCachedThreadPool.execute(new Runnable() {
				public void run() {
					System.out.println(Thread.currentThread().getName() + "执行第" + index + "个任务");
				}
			});
		}
	}
}

1. newFixedThreadPool固定线程池

ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(5);
		for (int i = 1; i <= 100; i++) {
			final int index = i;
			newFixedThreadPool.execute(new Runnable() {
				public void run() {
					System.out.println(Thread.currentThread().getName() + "执行第" + index + "个任务");
				}
			});
		}

2. newSingleThreadExecutor单线程池

```java
ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
		for (int i = 1; i <= 100; i++) {
			final int index = i;
			newSingleThreadExecutor.execute(new Runnable() {
				public void run() {
					System.out.println(Thread.currentThread().getName() + "执行第" + index + "个任务");
				}
			});
		}
```

3. newScheduledThreadPool任务型线程池

       ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(5);
		/*		newScheduledThreadPool.schedule(new Runnable() {		
			@Override
			public void run() {
				System.out.println("延迟3秒后执行 该输出....");
			}
		}, 3, TimeUnit.SECONDS);*/
		newScheduledThreadPool.scheduleAtFixedRate(new Runnable() {
			@Override
			public void run() {
				System.out.println("1秒后每隔3秒输出该句话.....");
				
			}
		}, 1, 3, TimeUnit.SECONDS);

几种线程池的缺点:非核心线程数或者工作队列都有可能非常非常大(最大就是Integer.MAX_VALUE),可能会造成服务器的宕机。可以根据自身的需要直接使用自定义的线程池

4. 自定义线程池

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue)
    ExecutorService es = new ThreadPoolExecutor(10,20,30,TimeUnit.SECOND,new ArrayBlockingQueue(100));

5. 线程池相关方法

  • shutdown();//关闭线程池,要保证还没执行完线程执行完任务后才关闭

  • shutdownNow();//不能保证线程已经执行完任务

  • execute();//执行线程任务,没有返回值

  • submit();//执行线程任务,有返回值

小白第一篇文章,请大家多多指正,欢迎评论,欢迎私信
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值