【Java多线程与并发库】8.java5线程并发库之线程池的应用

Java5中的线程的并发库

并发库的代码均在Java的java.util.concurrent包下:


并发库中最常用的,是“线程池”

(1)线程池出现的原因

什么是“线程池”呢?用“线程池”有什么好处呢?
首先我们来举个例子。
假设我们想写一个类似TomCat的服务器,其实是很难的,
难点在于我们的性能升不上去。

假设我们写好了这个伪TomCat,假设有一个客户端连接TomCat,
我们就要新启动一个线程,以供给这个客户端进行交互。

为什么要新起一个线程而不是直接在TomCat主线程上进行交互呢?
原因是我们要有很多客户端进行交互,如果一个客户端始终在和TomCat进行
交互,那么其它的客户端都要等待交互结束,效率十分低下,所以要开启线程
让许多客户端可以同时和TomCat服务端进行交互。

假设我们现在要开一个线程让客户端与服务端进行交互,我们的伪代码可以这样么写:
new Thread(){
  run(){
    客户端xx与服务端交互;
  }
}.start();

如果有一个客户就算了,但是如果同时有一百个,一千个客户端需要交互,我们的代码是
无法满足的,因为这个线程只能满足一个客户端xx与服务端交互,对于多个线程,我们需要
一个一个给他们分配线程让他们执行,这个时候就需要一个暂时来存放将要在线程中执行的
任务的“容器池”,然后每次从“容器池”中取出任务去给其开启线程执行。

我们的伪代码可以这么改进:
new Thread(){
  run(){
    while(查询“容器池”是否还有需要交互的客户端){
      把任务从“容器池”中取出;
      客户端xx与服务端交互;
    }
  }
}.start();

当“容器池”中为空的时候,线程就等待新的任务出现,那么当新的任务出现
的时候,要解除这个等待状态,需要一个类似notifyAll()的方法来唤醒所有线程,

而Java5已经给我们提供了一个类似我们上面说的“容器池”的工具类,就是“线程池”。

(2)线程池的概念
线程池负责管理工作线程,包含一个等待执行的任务队列。线程池的任务队列是一个Runnable集合,工作线程负责从任务队列中取出并执行Runnable对象。

在线程池的编程模式下,任务是提交给整个线程池,而不是交给某个线程,线程池在拿到任务后,它就在内部找有无空闲的线程,再把任务交给内部某个
空闲的线程,这就是封装。值得注意的是,任务是提交给整个线程池,一个线程同时只能执行一个任务,但可以同时向一个线程池提交多个任务。

(3)java.util.concurrent.Executors类
Executors类可以用来创建线程池。

A.创建固定大小的线程池
Executors.newFixedThreadPool(int num);
其中num为要创建的线程池的容量大小(即是能同时容纳执行多少线程)。

例子:
package cn.edu.hpu.test;

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

public class ThreadPoolTest {
    public static void main(String[] args) {
        ExecutorService threadPool= Executors.newFixedThreadPool(3);
        for (int i = 1; i <= 10; i++) {
            final int task=i;
            threadPool.execute(new Runnable(){
                public void run() {
                    for (int j = 1; j <= 10; j++) {
                        System.out.println(Thread.currentThread().getName()
                                +"为第"+task+"个线程,正在执行第"+j+"次循环.");
                    }
                }
            });
        }
                threadPool.shutdown();
    }
}

执行效果:

其中threadPool.shutdown();为线程池的所有任务结束后,就停止线程池的运作。
而threadPool.shutdownNow();是不管线程池的任务结束与否,都要关闭线程池。

可以看到,我们开启的三个线程都已经同时执行了。但是我们能够看到,直到前3个
线程的某一个执行完,才开始执行第四个线程,所以我们的线程池的大小的确为3。

也就是说,线程池设置一个数值,用来设定每一次该池子同时可执行多少个线程,等池子中
的某个线程结束后,空出了一个位置,就会接纳新的线程来执行。

B.创建缓存线程池
刚刚我们使用Executors.newFixedThreadPool(int corePoolSize)建立的
是固定的线程池。

使用Executors.newCachedThreadPool();可以建立一个“缓存”线程池,当
线程池忙不过来的时候,此时又有新加入的任务,这时候会在任务队列中开
辟一个新的空间去处理新加入的线程,也就是说缓存线程池的大小是根据任
务情况动态变化的。
package cn.edu.hpu.test;

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

public class ThreadPoolTest {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newCachedThreadPool();
        for (int i = 1; i <= 10; i++) {
            final int task=i;
            threadPool.execute(new Runnable(){
                public void run() {
                    for (int j = 1; j <= 10; j++) {
                        System.out.println(Thread.currentThread().getName()
                                +"为第"+task+"个线程,正在执行第"+j+"次循环.");
                    }
                }
            });
        }
    }
}

结果:

我们看到,不再像之前那样,只有3个线程为任务服务,可以看到,这里运行第9、10个
任务的时候,线程也是开辟了第9、10线程(通过线程名可看出)为这两个任务服务。


C.创建单一线程池
使用ExecutorService threadPool = Executors.newSingleThreadExecutor();来创建一个
只包含一个线程的线程池,就像单独开启了一个线程一样。但是和一般的单独线程不同的是,
如果该线程池的线程死掉了,单一线程池会重新创建一个替补线程来保证线程池中始终有一个线程。

也就是说,单一线程池的好处就是,可以实现线程死掉之后重新启动的需求。

(4)用线程池启动定时器

使用Executors.newScheduledThreadPool(int corePoolSize).schedule(command, delay, unit);
可以创建一个定时执行的线程池,在schedule方法中command为需要线程执行的Runnable任务,
delay为延时多久执行,unit为延时的格式是什么(秒/分/时/天等)。

例子:
Executors.newScheduledThreadPool(3)
    .schedule(
            new Runnable(){
                public void run() {
                    System.out.println("爆炸!!!!");
                }
            }
            ,10
            ,TimeUnit.SECONDS);
指定10秒之后执行爆炸的线程。

如果想要延迟多少时间执行每隔多少执行一次的循环线程,可以使用
Executors.newScheduledThreadPool(int corePoolSize).scheduleAtFixedRate(command, initialDelay, period, unit)
其中command为需要线程执行的Runnable任务,
initialDelay为延时多久执行,period为每隔多久执行一次线程,
unit为延时的格式是什么(秒/分/时/天等)。

例子:
Executors.newScheduledThreadPool(3)
    .scheduleAtFixedRate(
            new Runnable(){
                public void run() {
                    System.out.println("爆炸!!!!");
                }
            }
            ,6
            ,2
            ,TimeUnit.SECONDS);
指定6秒之后每隔2秒执行一次。

转载请注明出处:http://blog.csdn.net/acmman/article/details/52858042

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

光仔December

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值