关闭

【java并发】线程并发库的使用

标签: java并发线程池线程Exetors
5772人阅读 评论(4) 收藏 举报
分类:

1. 线程池的概念

  在java5之后,就有了线程池的功能了,在介绍线程池之前,先来简单看一下线程池的概念。假设我开了家咨询公司,那么每天会有很多人过来咨询问题,如果我一个个接待的话,必然有很多人要排队,这样效率就很差,我想解决这个问题,现在我雇几个客服,来了一个咨询的,我就分配一个客服去接待他,再来一个,我再分配个客服去接待……如果第一个客服接待完了,我就让她接待下一个咨询者,这样我雇的这些客服可以循环利用。这些客服就好比不同的线程,那么装这些线程的容器就称为线程池。

2. Executors的使用

  Executors工具类是用来创建线程池的,这个线程池可以指定线程个数,也可以不指定,也可以指定定时器的线程池,它有如下常用的方法:

方法名 作用
newFixedThreadPool(int nThreads) 创建固定数量的线程池
newCachedThreadPool() 创建缓存的线程池
newSingleThreadExecutor() 创建单个线程
newScheduledThreadPool(int corePoolSize) 创建定时器线程池

2.1 固定数量的线程池

  先来看下Executors工具类的使用:

public class ThreadPool {
//线程池的概念与Executors类的使用
    public static void main(String[] args) {
        //固定线程池:创建固定线程数去执行线程的任务,这里创建三个线程
        ExecutorService threadPool = Executors.newFixedThreadPool(3);

        for (int i = 1; i <= 10; i++) {//向池子里扔10个任务
            final int task = i;
            threadPool.execute(new Runnable() {//execute方法表示向池子中扔任务,任务即一个Runnable

                @Override
                public void run() {
                    for (int j = 1; j <= 5; j++) { 
                        try {
                            Thread.sleep(20);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName()
                                + " looping of " + j + " for task of " + task);
                    }
                }
            });
        }
        System.out.println("all of 10 tasks have committed!");
        threadPool.shutdown(); //执行完任务后关闭
//      threadPool.shutdownNow(); //立即关闭
    }
}

  从代码中可以看出,有了Executors工具类,我们创建固定数量的线程数就方便了,这些线程都去做同样的任务。threadPool.execute表示从池子里取出一个线程去执行任务,上面定义了三个线程,所以每次会取三个任务去让线程执行,其他任务等待,执行完后,再从池子里取三个任务执行,执行完,再取三个任务,最后一个任务三个线程有一个会抢到执行。所以定义了线程数量的话,每次会执行该数量的任务,因为一个线程一个任务,执行完再执行其他任务。
  因为这个执行结果有点多,就不贴结果了,反正每次三个任务一执行,直到执行完10个任务为止。

2.2 缓存线程池

  所谓缓存线程池,指的是线程数量不固定,一个任务来了,我开启一个线程为其服务,两个任务我就开启两个,N个任务我就开启N个线程为其服务。如果现在只剩1个任务了,那么一段时间后,就把多余的线程给干掉,保留一个线程为其服务。所以可以改写一下上面的代码:

public class ThreadPool {
//线程池的概念与Executors类的使用
    public static void main(String[] args) {
        //缓存的线程池
        //自动根据任务数量来设定线程数去服务,多了就增加线程数,少了就减少线程数
        //这貌似跟一般情况相同,因为一般也是一个线程执行一个任务,但是这里的好处是:如果有个线程死了,它又会产生一个新的来执行任务
        ExecutorService threadPool = Executors.newCachedThreadPool();
        for (int i = 1; i <= 10; i++) {//扔5个任务
            final int task = i;
            threadPool.execute(new Runnable() {//向池子中扔任务,任务即一个Runnable

                @Override
                public void run() {
                    for (int j = 1; j <= 5; j++) {
                        try {
                            Thread.sleep(20);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName()
                                + " looping of " + j + " for task of " + task);
                    }
                }
            });
        }
        System.out.println("all of 10 tasks have committed!");
        threadPool.shutdown(); //执行完任务后关闭
    }
}

  使用缓存线程池的时候,会自动根据任务数量来产生线程数,即线程跟着任务走。运行结果也不贴了,有点多。
  那么创建单个线程池newSingleThreadExecutor()就写了,把上面那个方法改掉就行了,就只有一个线程去执行10个任务了,但是这跟我们平常直接new一个线程还有啥区别呢?它还有个好处就是,如果线程死了,它会自动再生一个,而我们自己new的就不会了。如果线程死了还要重新产生一个,也就是说要保证有一个线程在执行任务的话,那么newSingleThreadExecutor()是个很好的选择。

3. 线程池启动定时器

  我们可以用静态方法newScheduledThreadPool(int corePoolSize)来定义一个定时器线程池,可以指定线程个数。然后再调用schedule方法,传进去一个Runnable和定时时长即可,见代码:

public class ThreadPool {

    public static void main(String[] args) {
        Executors.newScheduledThreadPool(3).schedule(new Runnable() {

            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " bombing");
            }
        }, 2, TimeUnit.SECONDS);        
    }
}

  定义了三个线程,会有一个率先抢到任务执行在2秒后执行。这只是创建了一个任务,如果我们要使用这个线程池去执行多个任务咋办呢?schedule中只能传入一个Runnable,也就是说只能传入一个任务,解决办法跟上面那些程序一样,先拿到创建的线程池,再循环多次执行schedule,每次都传进去一个任务即可:

public class ThreadPool {

    public static void main(String[] args) {
        //拿到定时器线程池
        ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(3);
        for(int i = 1; i <= 5; i ++) { //执行5次任务
            threadPool.schedule(new Runnable() {

                @Override
                public void run() {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " bombing");
                }
            }, 2, TimeUnit.SECONDS);
        }   
    }
}

  因为线程池中只有3个线程,但是有5个任务,所以会先执行3个任务,剩下两个任务,随机2个线程执行,看下结果:

pool-1-thread-3 bombing
pool-1-thread-2 bombing
pool-1-thread-1 bombing
pool-1-thread-2 bombing
pool-1-thread-3 bombing

  如果我想5秒后执行一个任务,然后每个2秒执行一次该怎么办呢?我们可以调用另一个方法scheduleAtFixedRate,这个方法中传进去一个Runnable,一个定时时间和每次重复执行的时间间隔,如下:

public class ThreadPool {

    public static void main(String[] args) {
        Executors.newScheduledThreadPool(3).scheduleAtFixedRate(new Runnable() {

            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " bombing");
            }
        }, 5, 2, TimeUnit.SECONDS);     
    }
}

  这样就可以5秒后执行,并且以后每隔2秒执行一次了。这个方法有个瑕疵,就是无法设置指定时间点执行,官方JDK提供的解决办法是data.getTime()-System.currentTimeMillis()来获取相对时间,然后放到上面方法的第二个参数即可。
  线程并发库的使用就总结这么多吧~

  相关阅读:http://blog.csdn.net/column/details/bingfa.html


—–乐于分享,共同进步!
—–更多文章请看:http://blog.csdn.net/eson_15

7
0
查看评论

多线程并发库高级应用 之 java5中的线程并发库--线程池、Callable&Future

笔记摘要:                这里首先介绍了java5中的并发的小工具包:automatic,然后介绍了线程池的概念,对使用java5的方式创建不同形式的线程进行了演示,    ...
  • Before_Morning
  • Before_Morning
  • 2015-08-20 08:35
  • 637

多线程及线程并发库

1. 创建和启动线程的两种传统方式概念:java 实现一条线索 有两种方法: 1、继承thread 类 例子: // 继承Thread类 new Thread(){ publicvoid run(){ whi...
  • u012173245
  • u012173245
  • 2016-07-24 15:15
  • 896

多线程并发库高级应用 之 java5中的线程并发库--线程锁技术

转自:http://blog.csdn.net/xushuaic/article/details/8561859 笔记摘要:       这里介绍了java5中的线程锁技术:Lock和Condition,实现线程间的通信,其中的读锁和写锁的使用通过一个缓...
  • jdsjlzx
  • jdsjlzx
  • 2016-05-03 19:24
  • 869

Java中的并发库学习总结

我们都知道,在JDK1.5之前,Java中要进行业务并发时,通常需要有程序员独立完成代码实现,当然也有一些开源的框架提供了这些功能,但是这些依然没有JDK自带的功能使用起来方便。而当针对高质量Java多线程并发程序设计时,为防止死蹦等现象的出现,比如使用java之前的wait()、notify()和...
  • jiangwei0910410003
  • jiangwei0910410003
  • 2014-03-03 15:12
  • 11962

【Java多线程与并发库】18.java线程面试题1

现有的程序代码模拟产生了16个日志对象,并且需要运行16秒才能打印完这些日志,请在程序 中增加4个线程去调用parseLog()方法来分头打印这16个日志对象,程序只需要运行4秒即可打印 玩这些日志对象。原始代码如下: package cn.edu.hpu.test; public class R...
  • u013517797
  • u013517797
  • 2016-11-10 15:23
  • 928

Java并发库(二):传统定时器技术回顾

02. 传统定时器技术回顾 传统定时器的创建:直接使用定时器类Timer【轻松实现定时关机或者过多长时间关机】 a、过多长时间后炸 new Timer().schedule(TimerTask定时任务, Date time定的时间); b、过多长时间后炸,以后每隔多少时间再炸 newTimer()....
  • xxssyyyyssxx
  • xxssyyyyssxx
  • 2015-12-25 09:47
  • 287

Java并发库

:Java并发库:(重要) Java1.5以后为了解决多线程并发,死锁等问题引入的工具包。java.util.concurrent。 包含: ExecutorService:线程池,submit有返回值,而execute没有 1.减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执...
  • TylerRoot
  • TylerRoot
  • 2017-06-02 19:08
  • 112

张孝祥_Java多线程与并发库高级应用

  • 2016-03-21 09:57
  • 695KB
  • 下载

【Java多线程与并发库】5.线程范围内共享变量的概念与作用

我们可以使用ThreadLocal实现线程范围的共享变量。 ThreadLocal的作用和目的,用于实现线程内的数据共享,即对于相同的程序代码,多个模块在 同一个线程中运行时要共享一份数据,而在另外线程中运行又共享另外一份数据。 线程范围内共享数据的示意图: 每个线程调用全局ThreadLo...
  • u013517797
  • u013517797
  • 2016-10-11 12:58
  • 1326

多线程及java5的线程并发库

package com.partner4java.itcast.util.thread; /** * 两种传统的线程创建方式 * * @author partner4java * */ public class TraditionalThread { // 创建线程的两种传统方式...
  • partner4java
  • partner4java
  • 2011-12-27 21:48
  • 4203
    个人资料
    • 访问:1631419次
    • 积分:16115
    • 等级:
    • 排名:第780名
    • 原创:163篇
    • 转载:5篇
    • 译文:0篇
    • 评论:1681条
    友情链接
    博客专栏
    休闲时刻
      学累了吗?用鼠标逗逗它吧^_^