JUC-线程池

一,问题

在没有使用线程池的时候,每次需要一个线程都得手动new Thread()方式创建线程,用完了再销毁。

我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题:

如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间

那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务

在Java中可以通过线程池来达到这样的效果。

Executor线程池框架,通过它把任务的提交和执行进行解耦,只需要定义好任务,然后提交给线程池而不用关心该任务是如何执行、被哪个线程执行,以及什么时候执行

 

一、线程池:提供了一个线程队列,队列中保存着所有等待状态的线程。避免了创建与销毁额外开销,提高了响应的速度。

二、线程池的体系结构:

java.util.concurrent.Executor : 负责线程的使用与调度的根接口
 |--**ExecutorService 子接口: 线程池的主要接口
    |--ThreadPoolExecutor 线程池的实现类
    |--ScheduledExecutorService 子接口:负责线程的调度
      |--ScheduledThreadPoolExecutor :继承 ThreadPoolExecutor, 实现 ScheduledExecutorService(即实现了接口,又继承了ThreadPoolExecutor实现类)

三、工具类 : Executors

ExecutorService newFixedThreadPool() : 创建固定大小的线程池
ExecutorService newCachedThreadPool() : 缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量。
ExecutorService newSingleThreadExecutor() : 创建单个线程池。线程池中只有一个线程

ScheduledExecutorService newScheduledThreadPool() : 创建固定大小的线程,可以延迟或定时的执行任务(调度)

四:操作步骤:以newFixedThreadPool为例:

1,创建一个5个线程的线程池:

ExecutorService pool = Executors.newFixedThreadPool(5);

2,创建任务对象,任务对象可以是Runnable接口对象,也可以是Callable接口对象,这里以Runnable接口对象为例:

类定义:

class ThreadPoolDemo implements Runnable { public void run(){ do something}}

创建任务:

ThreadPoolDemo tpd = new ThreadPoolDemo();

2,向线程池中加任务:使用pool.submit(任务对象)添加任务。

线程池添加任务有好几种方式:

        submit(Callable<T> task)
        submit(Runnable task, T result)
        submit(Runnable task)

以最后一直为例:

pool.submit(tpd);

3,线程执行完,关闭线程:

线程关闭有两种方法:

第一种:shutdown(),这种方式会等线程池中线程执行完了就关闭线程池。

第二张:shutdownNow(),不管线程池里面任务有没有执行完全,都会立刻关闭线程池。

五,Submit:一共有三种构造方法:

1,参数是Callable对象,可以获取任务的返回结果

 <T> Future<T> submit(Callable<T> task);

2,参数是Runnable对象,有返回结果

 <T> Future<T> submit(Runnable task, T result);

3,参数也是Runnable对象

 Future<?> submit(Runnable task);


5.1   分析 submit(Runnable task, T result)

之前的章节知道,Callable是任务是可以有返回值的,而Runnable是没有返回值的。但是对于第二个构造方法,使用Runnable对象作为参数也能返回结果。

那么结果是如何返回的呢,又是返回什么结果呢?我跟了一下源码,源码关键代码如下:

有一个实现类AbstractExecutorService,里面有一个方法:

    public <T> Future<T> submit(Runnable task, T result) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task, result);
        execute(ftask);
        return ftask;
    }

跟踪:newTaskFor(task, result);

通过这个方法一直跟踪,发现最后调用的是Excutors里面一个方法:

    public static <T> Callable<T> callable(Runnable task, T result) {
        if (task == null)
            throw new NullPointerException();
        return new RunnableAdapter<T>(task, result);
    }

可知最后在返回的结果是下面这个类返回的

new RunnableAdapter<T>(task, result)

类RunnableAdapter很简单:

    static final class RunnableAdapter<T> implements Callable<T> {
        final Runnable task;
        final T result;
        RunnableAdapter(Runnable task, T result) {
            this.task = task;
            this.result = result;
        }
        public T call() {
            task.run();
            return result;
        }
    }

通过这个类,我们可以大胆猜想到返回的结果是什么:

通过submit(Runnable task, T result)分别传入两个参数,一个是task,一个是result。

线程运行的时候会调用CALL方法,里面先通过task.run调用Runnable对象里面自定义的run方法,然后直接返回result。

可见整个线程执行,没有对result进行其他操作,只是传入对象引用,最后返回。

可见:唯一可以修改result值的地方,就是通过Ruannabe对象task的run方法里面执行,线程任务执行完了就返回值。

也就是result传入是哪个对象,最终Future对象的get()获取到的就是哪个对象,只不过值的改变是在Runnbale里面的run方法里面修改的

看下面这个例子感受一下:

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class TestSubmit {

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Data data = new Data();
//通过Future对象接收执行结果 Future
<Data> future = executor.submit(new Task(data), data); System.out.println(future.get().getName()); } } class Data { String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } class Task implements Runnable { Data data; public Task(Data data) { this.data = data; } public void run() { System.out.println("This is ThreadPoolExetor#submit(Runnable task, T result) method."); data.setName("kevin"); } }

执行结果:

This is ThreadPoolExetor#submit(Runnable task, T result) method.
kevin

分析执行过程:

先定义了一个Data实例data,然后分别把引用传给Task类里面的构造方法,和result参数。

线程执行的过程中,修改了data的值,线程执行完了,返回result的值,也就是data的值。通过future.get()获取该data修改后的值。

5.2  操作submit(Runnable task);

例子如下:

package com.atguigu.juc;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class TestThreadPool 
{    
    public static void main(String[] args) throws Exception {
        //1. 创建线程池
        ExecutorService pool = Executors.newFixedThreadPool(5);

        ThreadPoolDemo tpd = new ThreadPoolDemo();
        //2. 为线程池中的线程分配任务
        for (int i = 0; i < 10; i++) 
        {
            pool.submit(tpd);
        }
        //3. 关闭线程池
        pool.shutdown();
    }
}
class ThreadPoolDemo implements Runnable{

    private int i = 0;
    
    @Override
    public void run() {
        while(i <= 100){
            System.out.println(Thread.currentThread().getName() + " : " + i++);
        }
    }
}

5.3 使用:submit(Callable<T> task)

因为使用Callable有返回结果,可以通过Future对象获取。

先以单个线程为例:

package com.atguigu.juc;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class TestThreadPool 
{    
    public static void main(String[] args) throws Exception {
        //1. 创建线程池
        ExecutorService pool = Executors.newFixedThreadPool(5);

        Future<Integer> future = pool.submit(new Callable<Integer>(){

            @Override
            public Integer call() throws Exception {
                int sum = 0;
                
                for (int i = 0; i <= 100; i++) {
                    sum += i;
                }
                return sum;
            }
        });
        System.out.println(future.get());
        
        pool.shutdown();
    }
}

如果多线程呢:为了接收多线程的结果,使用一个List接收结果:

package com.atguigu.juc;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class TestThreadPool 
{    
    public static void main(String[] args) throws Exception {
        //1. 创建线程池
        ExecutorService pool = Executors.newFixedThreadPool(5);
        //创建一个List对象接收结果
        List<Future<Integer>> list = new ArrayList<>();
        
        for (int i = 0; i < 10; i++) {
            Future<Integer> future = pool.submit(new Callable<Integer>(){

                @Override
                public Integer call() throws Exception {
                    int sum = 0;
                    
                    for (int i = 0; i <= 100; i++) {
                        sum += i;
                    }
                    return sum;
                }
            });
            //向list里面添加结果
          list.add(future);
        }
        //关闭线程
        pool.shutdown();
        
        for (Future<Integer> future : list) {
            System.out.println(future.get());
        }
    }
}

猜你喜欢

转载自www.cnblogs.com/alsf/p/9142737.html