线程的创建与使用

124 阅读4分钟

线程的创建与使用

创建线程池的集中方式

  1. 通过Executors工厂类创建线程池:
  2. 通过ThreadPoolExecutor构造函数创建线程池:
@Configuration  
public class ExecutorServiceConfig {  

    @Bean(name = "datasourceExecutorService")  
    public ExecutorService datasourceExecutorService() {  
        /*
        // 1. 通过Executors工厂类创建线程池:  
  
        // 创建一个可缓存线程池,线程数根据任务数量动态调整  
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();  
        // 创建一个固定大小的线程池,最多同时执行5个线程  
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);  
        // 创建一个定时执行任务的线程池,核心线程数为3  
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);  
        // 创建一个单线程的线程池,保证所有任务按照指定顺序执行  
        ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();  


        // 2. 通过ThreadPoolExecutor构造函数创建线程池:  

        // 创建一个固定大小的线程池  
        ExecutorService fixedThreadPool1 = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());  
        // 创建一个单线程的线程池  
        ExecutorService singleThreadPool1 = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());  
        // 创建一个可缓存的线程池  
        ExecutorService cachedThreadPool1 = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());  
        // 创建一个定时执行任务的线程池  
        ScheduledExecutorService scheduledThreadPool1 = new ScheduledThreadPoolExecutor(3);
        
        */
    
        return new ThreadPoolExecutor(  
        //线程池中核心线程数的最大值  
        Runtime.getRuntime().availableProcessors(),  
        //线程池中能拥有最多线程数(阻塞队列满了才用到)  
        (int) (Runtime.getRuntime().availableProcessors() * 1.5),  
        //空闲线程的存活时间  
        60, java.util.concurrent.TimeUnit.SECONDS,  
        //用于缓存任务的阻塞队列  
        new LinkedBlockingQueue<>(50 * 1024),  
        new ThreadFactoryBuilder().setNameFormat("thread-pool-%d").build(),  
        //在任务被拒绝添加后,会由主线程去执行被拒绝的任务,当线程池有空闲时,才继续执行其他的任务。所以此策略可能会阻塞主线程  
        new ThreadPoolExecutor.CallerRunsPolicy());  
    }  
  
}

创建线程的方式

  1. 通过实现Runnable接口
  2. 通过实现Callable接口
  3. 通过继承Thread
  4. 通过线程池创建

1. 通过实现Runnable接口创建线程

@Test  
public void testRunnable() {  
    List<Integer> runnableTestResult = runnableTest();  
    System.out.println("runnableTestResult = " + runnableTestResult);  
}  
  
  
/**  
* 通过实现 Runnable接口创建线程  
*/  
public List<Integer> runnableTest() {  
    List<Integer> xList = Lists.newArrayList(1, 2, 3, 4, 5);  
    List<Integer> yList = Lists.newArrayList(1, 2, 3, 4, 5);  
    // 线程安全的队列实现,适用于高并发环境下的队列操作。
    ConcurrentLinkedQueue<Integer> result = new ConcurrentLinkedQueue<>();  
    List<Future<?>> futureList = new ArrayList<>();  
    for (int i = 0; i < xList.size(); i++) {  
        Integer x = xList.get(i);  
        Integer y = yList.get(i);  
        futureList.add(  
            // 提交任务给线程池
            executorService.submit(new Runnable() {  
                @Override  
                public void run() {  
                    // todo 工作逻辑处理  
                    String threadName = Thread.currentThread().getName();  
                    long threadId = Thread.currentThread().getId();  
                    log.info("当前线程ID:[{}],当前线程名称:[{}]", threadId, threadName);  
                    result.add((x != null ? x : 0) + (y != null ? y : 0));  
                }  
            })  
        );  
    }  
    for (Future<?> future : futureList) {  
    try {  
        future.get();  
    } catch (Exception e) {  
        log.error("获取线程池结果出现异常", e);  
        Thread.currentThread().interrupt();  
    }  
    }  
    executorService.shutdown();  
    return new ArrayList<>(result);  
}

2. 通过实现Callable接口创建线程

  1. 创建CallableTest类实现Callable接口
  2. 重写call()方法,编写业务逻辑
@Slf4j  
public class CallableTest implements Callable<Integer> {  
  
    private final Integer x;  
    private final Integer y;  

    public CallableTest(Integer x,Integer y) {  
        this.x = x;  
        this.y = y;  
    }  


    @Override  
    public Integer call() throws Exception {  
        return call( x, y);  
    }  

    public Integer call(Integer x,Integer y) {  
        // todo 业务逻辑  
        String threadName = Thread.currentThread().getName();  
        long threadId = Thread.currentThread().getId();  
        log.info("当前线程ID:[{}],当前线程名称:[{}]",threadId,threadName);  
        return (x != null ? x : 0) + (y != null ? y : 0);  
    }  
}

[使用示例]

@Test  
public void testCallable() {  
    List<Integer> testCallableResult = callableTest();  
    System.out.println("testCallableResult = " + testCallableResult);  
}  
  
/**  
* 通过实现Callable接口创建线程  
*/  
public List<Integer> callableTest() {  
    List<Integer> xList = Lists.newArrayList(1, 2, 3, 4, 5);  
    List<Integer> yList = Lists.newArrayList(1, 2, 3, 4, 5);  
    List<Future<Integer>> futureList = new ArrayList<>();  
    for (int i = 0; i < xList.size(); i++) {  
        futureList.add(executorService.submit(new CallableTest(xList.get(i), yList.get(i))));  
    }  
    List<Integer> result = new ArrayList<>();  
    for (Future<Integer> future : futureList) {  
        try {  
            result.add(future.get(30, java.util.concurrent.TimeUnit.SECONDS));  
        } catch (Exception e) {  
            log.error("获取线程池结果出现异常", e);  
            result.add(null);  
            Thread.currentThread().interrupt();  
        }  
    }  
    executorService.shutdown();  
    return result;  
}

3. 通过继承Thread

  1. 创建MyThreadTest类继承Thread
  2. 重写run()方法,编写业务逻辑
@Slf4j  
public class MyThreadTest extends Thread{  
  
    @Override  
    public void run() {  
        // todo 业务处理  
        String threadName = Thread.currentThread().getName();  
        long threadId = Thread.currentThread().getId();  
        log.info("当前线程ID:[{}],当前线程名称:[{}]",threadId,threadName);  
        System.out.println("threadId = " + threadId);  
    }  
}

[使用示例]

/**  
* 通过继承 Thread 类创建线程类  
*/  
@Test  
public void testThread() {  
    MyThreadTest myThreadTest = new MyThreadTest();  
    myThreadTest.start();  
}

4. 通过线程池创建

/**  
* 通过线程池创建线程  
*/  
@Test  
public void testThreadPoll() {  
    List<Integer> threadPollTestResult = threadPollTest();  
    System.out.println("threadPollTestResult = " + threadPollTestResult);  
}
public List<Integer> threadPollTest(){  
    // 创建固定大小为 3 的线程池  
    ExecutorService executor = Executors.newFixedThreadPool(3);  
    List<Integer> xList = Lists.newArrayList(1, 2, 3, 4, 5);  
    List<Integer> yList = Lists.newArrayList(1, 2, 3, 4, 5);  
    // 线程安全的队列实现,适用于高并发环境下的队列操作。
    ConcurrentLinkedQueue<Integer> result = new ConcurrentLinkedQueue<>();  
    List<Future<?>> futureList = new ArrayList<>();  
    for (int i = 0; i < xList.size(); i++) {  
        Integer x = xList.get(i);  
        Integer y = yList.get(i);  
        // 提交任务给线程池  
        futureList.add(executor.submit(() -> {  
            // todo 工作逻辑处理  
            String threadName = Thread.currentThread().getName();  
            long threadId = Thread.currentThread().getId();  
            log.info("当前线程ID:[{}],当前线程名称:[{}]",threadId,threadName);  
            result.add((x != null ? x : 0) + (y != null ? y : 0));  
        }));  
    }  

    for (Future<?> future : futureList) {  
        try {  
            future.get();  
        } catch (Exception e) {  
            log.error("获取线程池结果出现异常", e);  
            Thread.currentThread().interrupt();  
        }  
    }  
    executor.shutdown();  
    return new ArrayList<>(result);  
}

线程池创建的使用区别

实现Callable接口和实现Runnable接口创建的线程之间区别

1. 返回结果
     实现Runnable接口的线程任务不返回结果,`run()`方法没有返回值。
     实现Callable接口的线程任务可以返回结果,任务的`call()`方法可以返回计算结果。
2. 抛出异常
     实现Runnable接口的线程任务的`run()`方法无法抛出已检查异常(checked exception),只能在方法内部捕获处理。
    实现Callable接口的线程任务的`call()`方法可以抛出已检查异常,允许在方法声明中抛出异常,调用者需要处理异常。
3. 返回值处理
    Runnable接口的线程任务无法获取任务执行的结果,因为`run()`方法没有返回值。
    Callable接口的线程任务可以通过`Future`对象获取线程执行结果,可以进一步对结果进行处理。