Java多线程复习

一.多线程前置知识

1.进程和线程

进程是:一个正在运行应用程序如火绒
线程是:一个进程中的执行单元 如火绒的垃圾清理

2.内存的共享

不同的进程是独立的:不共享资源。
不同的线程:堆内存和方法区内存共享,但是栈内存独立,一个线程一个栈。

3.多线程

存在的意义就是提高工作效率同时也会伴随线程安全问题。即对共享的数据操作
但单核的电脑实际意义上不能做到线程并行,只能近似于线程并行。

4.线程对象的生命周期

新建状态
就绪状态
运行状态
阻塞状态
死亡状态
在这里插入图片描述

二.Thread

1.概述

hread类位于java.lang包,JDK1.0引入。线程的创建、调度、执行、销毁等由内核进行控制,调度过程通过抢占式策略进行调度。

2.构造器方法

方法描述
Thread()创建一个默认设置的线程对象实例
Thread(Runnable target)创建一个包含可执行对象的线程实例
Thread(Runnable target, String name)创建一个包含可执行对象,指定名称的线程对象
Thread(String name)创建一个指定名称的线程对象
Thread(ThreadGroup group, Runnable target)创建一个指定线程组,包含可执行对象的线程对象实例
Thread(ThreadGroup group, Runnable target, String name)创建一个指定线程组,包含可执行对象,指定线程名称的线程对象实例
Thread(ThreadGroup group, Runnable target, String name, long stackSize)创建一个指定线程组、包含可执行对象、指定名称以及堆栈大小的线程对象实例
Thread(ThreadGroup group, String name)创建一个指定线程组,线程名称的线程实例

线程组的解释:可以包含多个线程,且每个线程可访问本线程组的相关信息

3.Thread的常见属性和方法

静态方法

在这里插入图片描述

非静态方法

在这里插入图片描述

join()

切换为中途加入的线程,
有参数时:参数为阻塞的时间
无参数时:直到线程阻塞完成。
使用场景:当一个线程需要另一个线程的结果。
join() 方法被interrupt()会抛出异常,join()方法使用后会释放锁,sleep(long)方法却不释放锁;

//        设置守护线程 (备胎 当主线程执行之后自动结束)
        new Thread().setDaemon(true);
//        让出执行权  原线程还可以继续抢
        Thread.yield();
        Thread thread = new Thread();
//        插入线程 插入到其它线程  且join线程执行完之后 继续执行下面的
         thread.join();
线程状态

在这里插入图片描述
注意:优先级只是抢占锁时的概率大。并非优先级高的一定比优先级低的先执行
设置守护线程之后,主线程结束了,守护线程也随之结束,守护线程并不一定会执行完

三.实现多线程的方式

1.继承Thread类

步骤

1.继承Thread类
2.重写run方法
3.得到对象
4.调用其start方法

public class MyThread extends Thread{
//    重写run方法
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println("name:"+Thread.currentThread().getName()+" i="+i);
        }
    }
}
代码
/**
     * @description: 集成Thread
     * @author: lx
     * @date: 2023/3/31 13:53
     * @param: []
     * @return: void
     **/
    @Test
    public void test2(){
        MyThread thread1 = new MyThread();
        MyThread thread2 = new MyThread();
        MyThread thread3 = new MyThread();
        thread1.start();
        thread2.start();
        thread3.start();

    }
结果

在这里插入图片描述

2.实现Runable接口

步骤

1.实现Runnable接口
2.重写run方法
3.得到Thread对象把 Runnable当参数传进去
4.调用其start方法

代码
public class MyRunnable implements  Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println("name:"+Thread.currentThread().getName()+" i="+i);
        }
    }
}
  /**
     * @description: 实现Runnable接口
     * @author: lx
     * @date: 2023/3/31 14:01
     * @param: []
     * @return: void
     **/
    @Test
    public void test3(){
        MyRunnable myRunnable = new MyRunnable();
        Thread thread1 = new Thread(myRunnable);
        Thread thread2 = new Thread(myRunnable);
            thread1.start();
            thread2.start();
    }
结果

在这里插入图片描述
注:也可以通过实现匿名内部类的方式

3.实现Collable接口

步骤

1.实现Callable接口
2.重写call方法
3.得到FutureTask对象
4.得到Thread对象
5.执行start方法

代码
public class MyCallable implements Callable<Integer> {
/**
 * @description: Callable中的泛型就是call返回的结果
 * @author: lmk
 * @date: 2023/3/30 13:46
 * @param: []
 * @return: java.lang.Integer
 **/
    @Override
    public Integer call() throws Exception {
        int result = 0;
        for (int i = 0; i <50; i++) {
            result +=i;
            System.out.println(Thread.currentThread().getName()+"   "+result);
            Thread.sleep(10);
        }
        return result;
    }
}

    public static void main(String[] args) throws ExecutionException, InterruptedException {
//        myCallable 多线程要执行任务的对象
        MyCallable myCallable = new MyCallable();
//        FutureTask 管理多线程的结果
        FutureTask<Integer> f = new FutureTask<Integer>(myCallable);
        FutureTask<Integer> f1 = new FutureTask<Integer>(myCallable);
//        创建线程的对象
        Thread thread =  new Thread(f);
//       启动线程
        thread.start();
//        创建线程的对象
        Thread thread1 =  new Thread(f1);
//       启动线程
        thread1.start();
//       获取多线程运行的结果
        System.out.println(f.get());
    }
结果

在这里插入图片描述

4.三者的区别

主要是继承只能继承一个类,但接口可以多实现。
callable的优势:有返回值。且可以处理异常

四.线程池

1.概念

线程池其实就是一种多线程处理形式,处理过程中可以将任务添加到队列中,然后在创建线程后自动启动这些任务。这里的线程就是我们前面学过的线程,这里的任务就是我们前面学过的实现了Runnable或Callable接口的实例对象;

2.优势

1:线程和任务分离,提升线程重用性;
2:控制线程并发数量,降低服务器压力,统一管理所有线程;
3:提升系统响应速度,假如创建线程用的时间为T1,执行任务用的时间为T2,销毁线程用的时间为T3,那么使用线程池就免去了T1和T3的时间;

3.Demo

代码
package top.remained.juc.base;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
 * @Author: lx
 * @CreateTime: 2023-03-31  16:49
 * @Description: TODO
 */
public class BatchAddDemo {
    private static final int THREAD_POOL_SIZE = 10;

    public static void main(String[] args) throws InterruptedException {
        // 创建线程池
        ExecutorService executorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE);

        // 批量增加任务
        for (int i = 0; i < 100; i++) {
            executorService.execute(new Task(i));
//            Thread.sleep(1);
        }

        // 关闭线程池
        executorService.shutdown();
    }

    private static class Task implements Runnable {
        private final int id;

        public Task(int id) {
            this.id = id;
        }

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+" Task " + id + " is running");
        }
    }
}

运行效果

在这里插入图片描述

解释

在上述代码中,我们使用Executors.newFixedThreadPool()方法创建一个大小为10的固定线程池。然后使用execute()方法提交100个任务,每个任务都是一个实现了Runnable接口的Task对象。
最后,我们调用shutdown()方法关闭线程池。这将使线程池停止接受新任务,并等待所有已提交的任务完成。如果不调用shutdown()方法,线程池将一直运行直到应用程序退出。

4.ThreadPoolExecutor

ThreadPoolExecutor继承自AbstractExecutorService,而AbstractExecutorService实现了ExecutorService接口

        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                3,//核心线程数
                5,//最大线程数  设计
                100,//最大存活时间
                TimeUnit.SECONDS,//时间单位
                new ArrayBlockingQueue<>(3),//任务队列 设计:核心线程数/单个任务执行时间 *2
                Executors.defaultThreadFactory(),//创建线程工厂
                new ThreadPoolExecutor.AbortPolicy() //拒绝策略
        );

5.参数详解

1.corePoolSize

线程池核心线程大小

2.maximumPoolSize

线程池最大线程数量

3.keepAliveTime

空闲线程存活时间

4.unit

空闲线程存活时间单位

5.workQueue 工作队列

新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务。jdk中提供了四种工作队列:
①ArrayBlockingQueue
基于数组的有界阻塞队列,按FIFO排序。新任务进来后,会放到该队列的队尾,等待被调度,有界的数组可以防止资源耗尽问题(不会一直接收任务)。如果队列已经是满的,则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略。
②LinkedBlockingQuene
基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而基本不会去创建新线程直到maxPoolSize(很难达到Interger.MAX这个数),因此使用该工作队列时,参数maxPoolSize其实是不起作用的。
③SynchronousQuene
一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。
④PriorityBlockingQueue
具有优先级的无界阻塞队列,优先级通过参数Comparator实现

6.threadFactory 线程工厂

创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等

7.handler 拒绝策略

当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,该如何处理呢。这里的拒绝策略,就是解决这个问题的,jdk中提供了4中拒绝策略:
①CallerRunsPolicy
该策略下,在调用者线程中直接执行被拒绝任务的run方法,除非线程池已经shutdown,则直接抛弃任务。
②AbortPolicy
该策略下,直接丢弃任务,并抛出RejectedExecutionException异常。
③DiscardPolicy
该策略下,直接丢弃任务,什么都不做。
④DiscardOldestPolicy
该策略下,抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列

6.Executors

线程池的工具类 通过调用不同的方法返回不同的返回类型
使用工厂类获取线程池对象
Executors类的底层实现便是ThreadPoolExecutor

1.newCachedThreadPool

里面的线程可重用 且在第一次使用时才创建 数量不做限制,以任务优先,任务多的话,耗性能
适用场景:
需要快速处理突发性强、耗时较短的任务场景,如Netty的NIO处理场景、REST API接口的瞬时削峰场景

 @Test
    public void test1(){
//        1.使用工厂类获取线程池对象
        ExecutorService pool = Executors.newCachedThreadPool();
//        2.提交任务
        for (int i = 0; i <10 ; i++) {
            pool.submit(new Runnable1(i));
        }
    }
    @Test
    public void test2(){
//        1.使用工厂类获取线程池对象
        ExecutorService pool = Executors.newCachedThreadPool(new ThreadFactory() {
            int n = 1;
            @Override
            public Thread newThread(Runnable r) {

                return new Thread(r,"线程名称"+n++);
            }
        });
//        2.提交任务
        for (int i = 0; i <10 ; i++) {
            pool.submit(new Runnable1(i));
        }
    }
2.newFixedThreadPool

创建可重用固定线程数的线程池
特点
如果线程数没有达到“固定数量”,每次提交一个任务线程池内就创建一个新线程,直到线程达到线程池固定的数量
线程池的大小一旦达到“固定数量”就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程
在接收异步任务的执行目标实例时,如果池中的所有线程均在繁忙状态,新任务会进入阻塞队列中(无界的阻塞队列)
适用场景:
需要任务长期执行的场景
CPU密集型任务

  @Test
    public void test3(){
//        1.使用工厂类获取线程池对象

        ExecutorService pool = Executors.newFixedThreadPool(4);// 参数 线程数量
//        2.提交任务
        for (int i = 0; i <10 ; i++) {
            pool.submit(new Runnable1(i));
        }
    }
    @Test
    public void test4(){
//        1.使用工厂类获取线程池对象
        ExecutorService pool = Executors.newFixedThreadPool(3, new ThreadFactory() {
            int n = 1;
            @Override
            public Thread newThread(Runnable r) {

                return new Thread(r,"线程名称"+n++);
            }
        });// 参数 线程数量
//        2.提交任务
        for (int i = 0; i <10 ; i++) {
            pool.submit(new Runnable1(i));
        }
    }
3.newSingleThreadExecutor

单线程 无界 重点在于安全
适用场景:任务按照提交次序,一个任务一个任务地逐个执行的场景

    @Test
    public void test5(){
//        1.使用工厂类获取线程池对象

        ExecutorService pool = Executors.newSingleThreadExecutor();// 单个无界线程
//        2.提交任务
        for (int i = 0; i <10 ; i++) {
            pool.submit(new Runnable1(i));
        }
//        3.关闭线程池  不接受新的任务 原来的任务仍在执行
        pool.shutdown();
//        pool.shutdownNow(); 如有缓存的任务,返回这些任务并停止 运行
    }
    @Test
    public void test6(){
//        1.使用工厂类获取线程池对象

        ExecutorService pool = Executors.newSingleThreadExecutor(new ThreadFactory() {
            int n = 1;
            @Override
            public Thread newThread(Runnable r) {

                return new Thread(r,"线程名称"+n++);
            }
        });// 单个无界线程
//        2.提交任务
        for (int i = 0; i <10 ; i++) {
            pool.submit(new Runnable1(i));
        }
    }
}
4.newScheduledThreadPool

可重复 可延迟 可定期重复
1.延迟不循环

package top.remained.juc.pool.executorservice;

import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @Author: lx
 * @CreateTime: 2023-04-04  18:26
 * @Description: TODO 有线程池的特性,也可以实现任务循环执行,可以看作是一个简单地定时任务组件,
 * 因为有线程池特性,所以任务之间可以多线程并发执行,互不影响,当任务来的时候,才会真正创建线程去执行
 * schedule()可以对任务进行延迟处理
 */
public class ScheduledExecutorServiceTest {

    public static void main(String[] args) {
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);
        System.out.println("开启的时间:"+new Date(System.currentTimeMillis()));
        pool.schedule(new Runnable3(1),5, TimeUnit.SECONDS);
        pool.schedule(new Runnable3(2),5, TimeUnit.SECONDS);
        pool.shutdown();

    }
}
class Runnable3 implements Runnable {
    private int id;

    public Runnable3(int id) {
        this.id = id;
    }

    @Override
    public void run() {
        System.out.println("执行"+id+"的时间:"+new Date(System.currentTimeMillis()));
    }
}

在这里插入图片描述

2.延时重复循环

package top.remained.juc.pool.executorservice;

import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @Author: lx
 * @CreateTime: 2023-04-04  18:26
 * @Description: TODO 有线程池的特性,也可以实现任务循环执行,可以看作是一个简单地定时任务组件,
 * 因为有线程池特性,所以任务之间可以多线程并发执行,互不影响,当任务来的时候,才会真正创建线程去执行
 * schedule()可以对任务进行延迟处理
 */
public class ScheduledExecutorServiceTest {

    public static void main(String[] args) {
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);
        System.out.println("开启的时间:"+new Date(System.currentTimeMillis()));
//        5为第一次初始化的时间 4为循环周期
            pool.scheduleAtFixedRate(new Runnable3(1),5,4,TimeUnit.SECONDS );
            pool.scheduleAtFixedRate(new Runnable3(2),5,4,TimeUnit.SECONDS );
    }
}
class Runnable3 implements Runnable {
    private int id;

    public Runnable3(int id) {
        this.id = id;
    }

    @Override
    public void run() {
        System.out.println("执行"+id+"的时间:"+new Date(System.currentTimeMillis()));
    }
}


注意:任务执时间小于任务时间间隔为,否则就会导致任务连续执行,该方法不能严格保证任务按照规定的时间间隔执行,如果你的任务执行时间可以保证忽略不计,则可以使用该方法。
3.严格按照一定时间间隔执行
方法变为

  pool.scheduleWithFixedDelay(new Runnable3(1),5,4,TimeUnit.SECONDS );
5.特点

Executors创建线程池的4种方法十分方便,但是构造器创建普通线程池、可调度线程池比较复杂,这些构造器会涉及大量的复杂参数,已经较少使用。
newFixedThreadPool和newSingleThreadExecutor: 阻塞队列无界,会堆积大量任务导致OOM(内存耗尽)
newCachedThreadPool和newScheduledThreadPool: 线程数量无上界,会导致创建大量的线程,从而导致OOM
建议直接使用线程池ThreadPoolExecutor的构造器

7.自定义线程池

1.任务
package top.remained.juc.pool;

/**
 * @Author: lx
 * @CreateTime: 2023-03-31  17:39
 * @Description: TODO
 */
public class MyTask implements Runnable {
    private int id;
//    run不能带参数 所以初始化的时候把id初始化成功
    MyTask(int id) {
        this.id = id;
    }
    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        System.out.println(name+"即将执行任务:"+id);
        try {
            Thread.sleep(20);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(name+"完成了任务:"+id);
    }

    @Override
    public String toString() {
        return "MyTask{" +
                "id=" + id +
                '}';
    }
}


2.编写线程类
package top.remained.juc.pool;

import java.util.List;

/**
 * @Author: lx
 * @CreateTime: 2023-03-31  17:42
 * @Description: TODO  编写线程类
 */
public class MyWorker extends Thread{
    private String name;
    private List<Runnable> takes ;

    public MyWorker( String name, List<Runnable> takes) {
        super(name);
        this.takes = takes;

    }
/**
 * @description: 一直执行的原因:线程1完成任务时 (因为中途休眠了20ms)task的长度已经达到20 当然也有意外,就是你线程执行任务了 task的长度还没增加。
 *                  即剩下的任务虽然被缓存 但不会有线程去执行该任务   解决方法:让主线程执行加快或令线程1执行变慢。
 * @author: lmk
 * @date: 2023/4/4 15:04
 * @param: []
 * @return: void
 **/
    @Override
    public void run() {
//        判断集合中是否有任务 只要有就一直执行
        while (takes.size() > 0) {
//            移除第一个任务 返回的是移除的对象 即 Task
            Runnable r = takes.remove(0);
//            一直执行  一直执行的是task里的任务
            r.run();

        }
    }
}


3.自定义线程池类
package top.remained.juc.pool;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

/**
 * @Author: lx
 * @CreateTime: 2023-03-31  17:52
 * @Description: TODO 自定义线程池类
 *
 */
public class MyThreadPool {
    /**
     * @description:
     * @author: lmk
     * @date: 2023/3/31 17:53
     * @param: 1. 2.,3.。4.。5.
     * @return:
     **/
    private List<Runnable> tasks = Collections.synchronizedList(new LinkedList<>());//任务队列(用线程安全的)
    private int num;//当前线程数量
    private int corePoolSize;//核心线程数量
    private int maxPoolSize;//最大线程数量
    private int workSize;//任务队列的长度

    public MyThreadPool(int corePoolSize, int maxPoolSize, int workSize) {
        this.corePoolSize = corePoolSize;
        this.maxPoolSize = maxPoolSize;
        this.workSize = workSize;
    }

    //    提交任务
    public void submit(Runnable runnable){
//        判断当前集合中任务的数量是否超出最大任务数
        if (tasks.size() >= workSize) {
            System.out.println("任务" + runnable + "被丢弃");
        }else {
            tasks.add(runnable);
            int a = 0;
            System.out.println(a++);
//            执行任务
            execTask(runnable);
        }
    }
//    执行任务
    private void execTask(Runnable runnable) {
//        判断当前线程数量是否超出核心线程数
        if (num <corePoolSize){
            new MyWorker("核心线程:"+num,tasks).start();
            num ++;
        }else if (num<maxPoolSize){
            new MyWorker("非核心线程:"+num,tasks).start();
            num ++;
        }  else {
            System.out.println("任务" + runnable + "被缓存");
        }
    }
}


4. 测试
package top.remained.juc.pool;

import static java.lang.Thread.currentThread;
import static java.lang.Thread.sleep;

/**
 * @Author: lx
 * @CreateTime: 2023-03-31  18:04
 * @Description: TODO 测试
 */
public class MyTest {
    public static void main(String[] args) throws InterruptedException {
//        1.创建线程池类
        MyThreadPool pool = new MyThreadPool(2, 4, 20);
//        2.提交多个任务
        for (int i = 0; i <20 ; i++) {
//        3.创建任务对象,并提供给线程池
            MyTask task = new MyTask(i);
            pool.submit(task);

        }

    }
}


8. 向线程池提交任务的两种方式

execute方法

void execute(Runnable command): Executor接口中的方法

submit方法

Future submit(Callable task);
Future submit(Runnable task, T result);
Future<?> submit(Runnable task);
这3个submit方法都是ExecutorService接口中的方法
两种方法的区别:
execute()方法只能接收Runnable类型的参数,而submit()方法可以接收Callable、Runnable两种类型的参数
Callable类型的任务是可以返回执行结果的,而Runnable类型的任务不可以返回执行结果
submit()提交任务后会有返回值,而execute()没有
submit()方便Exception处理

9. Future

异步计算结果

package top.remained.juc.pool.executorservice;

import java.util.concurrent.*;

/**
 * @Author: lx
 * @CreateTime: 2023-04-04  10:18
 * @Description: TODO Future 异步计算结果
 */
public class FutureTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
//        1.获取线程池对象
        ExecutorService pool = Executors.newCachedThreadPool();
//        2.创建Callable类型的任务对象
        Future<Integer>  future = pool.submit(new Callable1(1, 2));
        System.out.println(future.isDone());//是否完成
        System.out.println(future.isCancelled());//是否取消
//        boolean cancel = future.cancel(true);  取消  取消成功返回true
        Thread.yield();
        System.out.println(future.toString());
        System.out.println(future.get());//得到结果 一直等待
        System.out.println(future.get(2,TimeUnit.SECONDS));//在规定时间内得到结果
    }
}
class Callable1 implements Callable<Integer> {
    private int a;
    private int b;

    public Callable1(int a, int b) {
        this.a = a;
        this.b = b;
    }

    @Override
    public Integer call() throws Exception {
        System.out.println(Thread.currentThread().getName()+"计算");
        System.out.println(Thread.currentThread().getName()+"计算完成");
        return a+b;
    }
}

10.线程池的一些方法

execute(Runnable task): 向线程池提交一个任务,让线程池执行。

submit(Callable task): 向线程池提交一个Callable任务,返回一个Future对象,可以通过Future对象获取任务执行结果。

shutdown(): 关闭线程池,不再接受新的任务,等待已经提交的任务执行完毕后关闭线程池。

shutdownNow(): 立即关闭线程池,中断正在执行的任务,并返回尚未执行的任务列表。

isShutdown(): 判断线程池是否已经关闭。

isTerminated(): 判断线程池中所有任务是否已经执行完毕。

awaitTermination(long timeout, TimeUnit unit): 等待线程池中所有任务执行完毕,或者超时。

getActiveCount(): 获取线程池中正在执行任务的线程数量。

getCompletedTaskCount(): 获取线程池中已经完成执行的任务数量。

getTaskCount(): 获取线程池中已经提交的任务数量。

getQueue(): 获取线程池中等待执行的任务队列。

getThreadFactory(): 获取线程池中的线程工厂。

setThreadFactory(ThreadFactory factory): 设置线程池中的线程工厂。

getRejectedExecutionHandler(): 获取线程池中的拒绝策略。

setRejectedExecutionHandler(RejectedExecutionHandler handler): 设置线程池中的拒绝策略

11.总结

创建线程池步骤
1:利用Executors工厂类的静态方法,创建线程池对象;
2:编写Runnable或Callable实现类的实例对象;
3:利用ExecutorService的submit方法或ScheduledExecutorService的schedule方 法提交并执行线程任务
4:如果有执行结果,则处理异步执行结果(Future)
5:调用shutdown()方法,关闭线程池

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值