java.util.concurrent 学习笔记(2) 线程池基础


在什么情况下使用线程池?


(1).单个任务处理的时间比较短

(2).将需处理的任务的数量大


使用线程池的好处:


(1)、减少在创建和销毁线程上所花的时间以及系统资源的开销

(2)、如不使用线程池,有可能造成系统创建大量线程而导致消耗完系统内存以及”过度切换”。


1、FixedThreadPool

每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。
在线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源

public class FixedThreadPoolTest {

    public static void main(String[] args) {
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);
        for (int i = 0; i < 10; i++) {
            Runnable task = new MyTask();
            System.out.println( " Main add Task : " + i);
            fixedThreadPool.execute(task);
        }
        fixedThreadPool.shutdown();
        System.out.println( "Main end ");
    }
}

class MyTask implements Runnable {

    @Override
    public void run() {
        Random random = new Random();
        System.out.println(Thread.currentThread().getName() + " begin ");
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " : " + i);
            try {
                TimeUnit.SECONDS.sleep(random.nextInt(5));
            } catch (InterruptedException ex) {
            }
        }
        System.out.println(Thread.currentThread().getName() + " end ");
    }
}


 
 

输出:

   Main add Task : 0 

Main add Task : 1 

Main add Task : 2 

Main add Task : 3

pool-1-thread-1 begin  

Main add Task : 4

pool-1-thread-2 begin 

pool-1-thread-2 : 0

pool-1-thread-1 : 0

pool-1-thread-2 : 1

Main end 

pool-1-thread-1 : 1

pool-1-thread-1 : 2

pool-1-thread-2 : 2

pool-1-thread-1 : 3

pool-1-thread-2 : 3

pool-1-thread-1 : 4

pool-1-thread-1 end 

pool-1-thread-1 begin 

pool-1-thread-1 : 0

pool-1-thread-2 : 4

pool-1-thread-2 end 

pool-1-thread-2 begin 

pool-1-thread-2 : 0

pool-1-thread-1 : 1

pool-1-thread-2 : 1

pool-1-thread-2 : 2

pool-1-thread-1 : 2

pool-1-thread-2 : 3

pool-1-thread-2 : 4

pool-1-thread-1 : 3

pool-1-thread-1 : 4

pool-1-thread-1 end 

pool-1-thread-2 end 

pool-1-thread-2 begin 

pool-1-thread-2 : 0

pool-1-thread-2 : 1

pool-1-thread-2 : 2

pool-1-thread-2 : 3

pool-1-thread-2 : 4

pool-1-thread-2 end    

结论:

(1)、线程池大小为2,加入实际线程数大于2时候,线程池也不会阻塞的,也会加进去,只是不马上执行而已。

(2)、线程池的线程可以复用,一直使用两个线程,而不是等线程结束以后,开始新的线程。

(3)、excute() 不会阻塞主线程,即excute() 是异步的。

(4)、线程池必须使用shutdown来显式关闭,否则主线程就无法退出。shutdown也不会阻塞主线程,即shutdown是异步的

2、SingleThreadExecutor

单线程化的Executor,即只创建唯一的工作者线程来执行任务,如果这个线程异常结束,会有另一个取代它,保证顺序执行(我觉得这点是它的特色)。

单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。

        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 5; i++) {
            Runnable task =  new MyTask();
            System.out.println( " Main add Task : " + i);
            singleThreadExecutor.execute(task);
        }
        singleThreadExecutor.shutdown();
        System.out.println( "Main end ");

输出:

 Main add Task : 0
 Main add Task : 1
 Main add Task : 2
 Main add Task : 3
 Main add Task : 4
pool-1-thread-1 begin 
pool-1-thread-1 : 0
Main end 
pool-1-thread-1 : 1
pool-1-thread-1 : 2
pool-1-thread-1 : 3
pool-1-thread-1 : 4
pool-1-thread-1 end 
pool-1-thread-1 begin 
pool-1-thread-1 : 0
pool-1-thread-1 : 1
pool-1-thread-1 : 2
pool-1-thread-1 : 3
pool-1-thread-1 : 4
pool-1-thread-1 end 
pool-1-thread-1 begin 
pool-1-thread-1 : 0
pool-1-thread-1 : 1
pool-1-thread-1 : 2
pool-1-thread-1 : 3
pool-1-thread-1 : 4
pool-1-thread-1 end 
pool-1-thread-1 begin 
pool-1-thread-1 : 0
pool-1-thread-1 : 1
pool-1-thread-1 : 2
pool-1-thread-1 : 3
pool-1-thread-1 : 4
pool-1-thread-1 end 
pool-1-thread-1 begin 
pool-1-thread-1 : 0
pool-1-thread-1 : 1
pool-1-thread-1 : 2
pool-1-thread-1 : 3
pool-1-thread-1 : 4
pool-1-thread-1 end 


结论:

SingleThreadExecutor 也就是 newFixedThreadPool(1) 而已。


3、CachedThreadPool

(1). 工作线程的创建数量几乎没有限制(其实也有限制的,数目为Interger.MAX_VALUE), 这样可灵活的往线程池中添加线程。

(2).  如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程。

在使用CachedThreadPool时,一定要注意控制任务的数量,否则,由于大量线程同时运行,很有会造成系统瘫痪


        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        for (int i = 0; i < 5; i++) {
            Runnable task =  new MyTask();
            System.out.println( " Main add Task : " + i);
            cachedThreadPool.execute(task);
        }
        cachedThreadPool.shutdown();
        System.out.println( "Main end ");

输出:

 Main add Task : 0
 Main add Task : 1
 Main add Task : 2
 Main add Task : 3
pool-1-thread-1 begin 
pool-1-thread-1 : 0
pool-1-thread-2 begin 
 Main add Task : 4
pool-1-thread-3 begin 
pool-1-thread-3 : 0
pool-1-thread-4 begin 
pool-1-thread-4 : 0
pool-1-thread-2 : 0
pool-1-thread-2 : 1
pool-1-thread-5 begin 
pool-1-thread-5 : 0
Main end 
pool-1-thread-1 : 1
pool-1-thread-2 : 2
pool-1-thread-3 : 1
pool-1-thread-5 : 1
pool-1-thread-4 : 1
pool-1-thread-1 : 2
pool-1-thread-4 : 2
pool-1-thread-1 : 3
pool-1-thread-3 : 2
pool-1-thread-2 : 3
pool-1-thread-5 : 2
pool-1-thread-2 : 4
pool-1-thread-3 : 3
pool-1-thread-3 : 4
pool-1-thread-2 end 
pool-1-thread-5 : 3
pool-1-thread-4 : 3
pool-1-thread-4 : 4
pool-1-thread-3 end 
pool-1-thread-1 : 4
pool-1-thread-4 end 
pool-1-thread-5 : 4
pool-1-thread-1 end 
pool-1-thread-5 end 


结论:

(1)、加入线程后马上执行,不会阻塞的,加多少就运行多少。 

(3)、excute() 不会阻塞主线程,即excute() 是异步的。

(4)、线程池必须使用shutdown来显式关闭,否则主线程就无法退出。shutdown也不会阻塞主线程,即shutdown是异步的。


4、ScheduledThreadPool、SingleScheduledThreadPool

创建一个定长的线程池,而且支持定时的以及周期性的任务执行,类似于Timer


        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(2);
        for (int i = 0; i < 5; i++) {
            Runnable task =  new MyTask();
            System.out.println( " Main add Task : " + i);
            scheduledThreadPool.schedule(task, 1, TimeUnit.SECONDS);
        }
        scheduledThreadPool.shutdown();
       //  scheduledThreadPool.scheduleAtFixedRate(task, 1, 1, TimeUnit.HOURS);
            //scheduledThreadPool.scheduleWithFixedDelay(task, 1, 1, TimeUnit.DAYS); System.out.println( "Main end ");

 较好理解。 
 

5、Callable 和 Future

class CallableTask implements Callable<Integer> {

    public CallableTask() {
    }

    @Override
    public Integer call() throws Exception {
        TimeUnit.SECONDS.sleep(new Random().nextInt(2));
        return new Random().nextInt();
    }
}

public class CallableTest {

    public static void main(String[] args) {
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
        List<Future> futures = new ArrayList<Future>();
        for (int i = 0; i < 10; i++) {
            Callable<Integer> task = new CallableTask();
            Future<Integer> future = fixedThreadPool.submit(task);
            futures.add(future);
        }
        for (Future<Integer> future : futures) {
            try {
                if (future.isDone()) {
                    System.out.println("Done: " + future.get());
                } else {
                    future.cancel(true);
                    System.out.println("Canceled: " + future.isCancelled());
                }
            } catch (InterruptedException | ExecutionException ex) {
                ex.printStackTrace();
            }
        }
        fixedThreadPool.shutdown();
        System.out.println("Main end ");
    }
}


 
 

输出:

Done: 1282190456

Done: -729572913Done: 2080564117Canceled: trueDone: 1361725593Canceled: trueDone: 2038751796Canceled: trueCanceled: trueDone: -1923347889Main end 

结论:

1、submit方法提交 Callable的任务,通过Future 获得结果

2、Future 的get() 方法不马上返回结果,而是要在完成之后返回,

3、Future 的 cancel()方法 取消任务

4、Future 的 isCancel()方法  isDone() 方法返回状态。

6、CompletionService

 public class CompletionServiceTest {

    public static void main(String[] args) {
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
        CompletionService completionService = new ExecutorCompletionService(fixedThreadPool);
        for (int i = 0; i < 10; i++) {
            Callable<Integer> task = new CallableTask2();
            completionService.submit(task);
        }
        for (int i = 0; i < 10; i++) {
            try {
                Future take = completionService.take();
                System.out.println(" end: " + take.get());
            } catch (InterruptedException | ExecutionException ex) {
                ex.printStackTrace();
            }
        }
        fixedThreadPool.shutdown();
        System.out.println("Main end ");
    }
}

 
 

输出:

 end: -1156022272 

end: -466547416 

end: 1348425806 

end: 558625149 

end: 1573057831 

end: 156442554 

end: 309780892 

end: -333067071 

end: 906757921 

end: -2083965743 

Main end 

结论:

CompletionService接口有两个重要方法:submit()和take()。submit用于提交一个runnable或者callable,一般会提交给一个线程池处理;而take就是取出已经执行完毕runnable或者callable实例的Future对象,如果没有满足要求的,就等待了; 方法poll()与take类似,只是不会等待,如果没有满足要求,就返回null对象。

public class InvokeAnyTest {

    public static void main(String[] args) {

        ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(5);
        List<CallableTask> tasks = new LinkedList<CallableTask>();
        for (int i = 0; i < 10; i++) {
            CallableTask callableTask = new CallableTask(i + "");
            tasks.add(callableTask);
        }
        try {
            Integer result = newFixedThreadPool.invokeAny(tasks);
            System.out.println("first result at " + System.currentTimeMillis() + ":" + result);
        } catch (InterruptedException | ExecutionException ex) {
            System.out.println("invokeAny exception at " + System.currentTimeMillis() + ":" + ex.getMessage());
        }

        newFixedThreadPool.shutdown();
    }
}

class CallableTask implements Callable<Integer> {

    String name;

    public CallableTask(String name) {
        this.name = name;
    }

    @Override
    public Integer call() {
        try {
            TimeUnit.SECONDS.sleep(new Random().nextInt(5));
        } catch (InterruptedException ex) {
            System.out.println(this.name + " exception at " + System.currentTimeMillis() + ":" + ex.getMessage());
        }
        int nextInt = new Random().nextInt(1000);
        System.out.println(this.name + " return at " + System.currentTimeMillis() + ":" + nextInt);
        return nextInt;
    }


 
 

输出:

2 return at 1411354042128:781

return at 1411354042128:1833 

return at 1411354042128:2307 

return at 1411354042129:795

first result at 1411354042129:781

6 exception at 1411354042130:sleep interrupted

0 exception at 1411354042130:sleep interrupted

5 exception at 1411354042130:sleep interrupted

0 return at 1411354042130:435

8 exception at 1411354042130:sleep interrupted

8 return at 1411354042130:939

1 exception at 1411354042130:sleep interrupted

6 return at 1411354042130:428

1 return at 1411354042130:519

5 return at 1411354042130:807

结论:

1、只返回最早那一个结果

2、其余没完成的线程中断,结果忽略。

8. InvokeAll

public class InvokeAllTest {

    public static void main(String[] args) {

        ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(5);
        List<CallableTask2> tasks = new LinkedList<CallableTask2>();
        for (int i = 0; i < 10; i++) {
            CallableTask2 callableTask = new CallableTask2(i + "");
            tasks.add(callableTask);
        }
        try {
            List<Future<Integer>> listReslut = newFixedThreadPool.invokeAll(tasks);
            for (Future<Integer> result : listReslut) {
                System.out.println(" result :" + result.get());

            }
        } catch (InterruptedException | ExecutionException ex) {
            System.out.println("invokeAny exception at " + System.currentTimeMillis() + ":" + ex.getMessage());
        }
        newFixedThreadPool.shutdown();
    }
}

class CallableTask2 implements Callable<Integer> {

    String name;

    public CallableTask2(String name) {
        this.name = name;
    }

    @Override
    public Integer call() {
        try {
            TimeUnit.SECONDS.sleep(new Random().nextInt(5));
        } catch (InterruptedException ex) {
            System.out.println(this.name + " exception at " + System.currentTimeMillis() + ":" + ex.getMessage());
        }
        int nextInt = new Random().nextInt(1000);
        System.out.println(this.name + " return at " + System.currentTimeMillis() + ":" + nextInt);
        return nextInt;
    }
}

输出:

1 return at 1411354530402:993
3 return at 1411354531402:706
4 return at 1411354533402:998
0 return at 1411354534402:116
2 return at 1411354534402:432
6 return at 1411354534402:298
5 return at 1411354534402:206
8 return at 1411354534402:812
7 return at 1411354535402:57
9 return at 1411354537402:957
 result :116
 result :993
 result :432
 result :706
 result :998
 result :206
 result :298
 result :57
 result :812
 result :957


结论:全部任务完成再返回。


7、FutureTask


FutureTask 实现 Runnable 和Future 接口,没有实现Callable接口。


public class FutureTask<V> implements RunnableFuture<V>

public interface RunnableFuture<V> extends Runnable, Future<V> {

    void run();
}

public interface Future<V> {
 
    boolean cancel(boolean mayInterruptIfRunning);
 
    boolean isCancelled();
 
    boolean isDone();
 
    V get() throws InterruptedException, ExecutionException;
  
    V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}

例子

public class FutureTaskTest {

    public static void main(String[] args) throws InterruptedException, ExecutionException {

        ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(2);
        
        //run FutureTask by ThreadPool 
        List<FutureTask> futureTasks = new LinkedList<FutureTask>();
        for (int i = 0; i < 5; i++) {
            CallableTask callableTask = new CallableTask(i + "--pool");
            FutureTask ft = new FutureTask(callableTask);
            futureTasks.add(ft);
            newFixedThreadPool.execute(ft);
        }
        for (FutureTask ft : futureTasks) {
            System.out.println("By ThreadPool : " + ft.get());
        }

          //run FutureTask by standalone Thread
        for (int i = 0; i < 5; i++) {
            CallableTask callableTask = new CallableTask(i + "--alone");
            FutureTask ft = new FutureTask(callableTask);
            new Thread(ft).start();
        }
        for (FutureTask ft : futureTasks) {
            System.out.println("Standalone Thread : " + ft.get());
        }

        newFixedThreadPool.shutdown();
    }
}




输出:

0--pool return at 1411356206910:86
By ThreadPool : 86
1--pool return at 1411356208910:128
By ThreadPool : 128
2--pool return at 1411356208911:109
By ThreadPool : 109
3--pool return at 1411356211910:258
By ThreadPool : 258
4--pool return at 1411356212911:792
By ThreadPool : 792
1--alone return at 1411356212912:556
Standalone Thread : 86
Standalone Thread : 128
Standalone Thread : 109
Standalone Thread : 258
Standalone Thread : 792
4--alone return at 1411356214913:404
0--alone return at 1411356215913:949
3--alone return at 1411356215914:507
2--alone return at 1411356216913:769


8、自定义线程池的分析和使用

我们可以通过ThreadPoolExecutor来创建一个线程池。

new  ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, milliseconds,runnableTaskQueue, handler);


创建一个线程池需要输入几个参数:


corePoolSize(线程池的基本大小):当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任务数大于线程池基本大小时就不再创建。如果调用了线程池的prestartAllCoreThreads方法,线程池会提前创建并启动所有基本线程。

runnableTaskQueue(任务队列):用于保存等待执行的任务的阻塞队列。 可以选择以下几个阻塞队列。

            ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。

            LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列。

            SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。

            PriorityBlockingQueue:一个具有优先级的无限阻塞队列。

maximumPoolSize(线程池最大大小):线程池允许创建的最大线程数。如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是如果使用了无界的任务队列这个参数就没什么效果。

threadFactory:用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字。

RejectedExecutionHandler(饱和策略):当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是                     AbortPolicy,表示无法处理新任务时抛出异常。以下是JDK1.5提供的四种策略。

             AbortPolicy:直接抛出异常。

             CallerRunsPolicy:只用调用者所在线程来运行任务。

             DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。

             DiscardPolicy:不处理,丢弃掉。

             当然也可以根据应用场景需要来实现RejectedExecutionHandler接口自定义策略。如记录日志或持久化不能处理的任务。

keepAliveTime(线程活动保持时间):线程池的工作线程空闲后,保持存活的时间。所以如果任务很多,并且每个任务执行的时间比较短,可以调大这个时间,提高线程的利用率。

TimeUnit(线程活动保持时间的单位):可选的单位有DAYS,HOURS,MINUTES,MILLISECONDS,MICROSECONDS,NANOSECONDS




当提交一个新任务到线程池时,线程池的处理流程如下:

1.   首先线程池判断基本线程池是否已满?没满,创建一个工作线程来执行任务。满了,则进入下个流程。

2.   其次线程池判断工作队列是否已满?没满,则将新提交的任务存储在工作队列里。满了,则进入下个流程。

3.   最后线程池判断整个线程池是否已满?没满,则创建一个新的工作线程来执行任务,满了,则交给饱和策略来处理这个任务


转载于:https://www.cnblogs.com/leeeee/p/7276426.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值