上一篇文章介绍了Runable、匿名类、带返回值的线程创立形式

接下来咱们这篇要介绍应用定时器、线程池、Lambda表达式等形式来创立

一、应用定时器创立线程


定时器其实也是相当于开拓一个线程来进行执行定时工作,就是咱们所相熟的Timer类

比如说咱们想在某一个工夫点上执行一件事,比方凌晨要跑数据,或者实现20分钟之后提醒咱们一些事等等,都能够通过定时器来执行

定时器对于定时工作,除了JDK所给咱们提供的Timer类这个API以外,还有很多的第三方的对于定时的工作的框架。

比方Spring就对定时工作进行十分好的反对,还有一个十分弱小的对于打算工作的框架叫quartz,quartz也是说的企业中咱们做定时工作的专门的一个零碎

上面咱们看一下定时器的实现,看看怎么实现一个定时的工作

class Demo4{    public static void main(String[] args) {        Timer task = new Timer();    }}

咱们能够通过schedule()这个办法来提交一个定时工作,定时工作就是TimerTask task,前面那个参数就是你能够通过指定提早多长时间执行等等

class Demo4{    public static void main(String[] args) {        Timer timer = new Timer();                /* time为Date类型,在指定工夫执行一次*/        timer.schedule(TimerTask task, Date time)        /* firstTime为Date类型,period为long,         在firstTime时刻第一次执行,之后每隔period毫秒执行一次*/        timer.schedule(TimerTask task, Date firstTime, long period)        /* delay为long类型,从以后开始delay毫秒后执行一次*/        timer.schedule(TimerTask task, long delay)        /* delay为long类型,period为long,         从以后开始delay毫秒后执行一次,之后每隔period毫秒执行一次*/        timer.schedule(TimerTask task, long delay, long period)    }}

咱们想让它立即执行而后每隔1秒执行一次,那么咱们就能够应用这个构造方法

第一个参数线程工作TimerTask是一个抽象类,一起看看他的源码是怎么样的

public abstract class TimerTask implements Runnable {        //省略其余关键性代码......}

咱们发现它也实现了Runnable接口,所以咱们能够在run()办法外面就能够实现定时工作

class Demo4{    public static void main(String[] args) {        Timer timer = new Timer();        timer.schedule(new TimerTask() {            @Override            public void run() {                System.out.println("timeTask start.....");            }        },0,1000);    }}//运行后果如下:timeTask start.....timeTask start.....timeTask start.....timeTask start.....timeTask start.....timeTask start.....

咱们设置提早0,设置每隔1s执行一次实现进行输入

这种写法咱们发现它有一个十分大的问题就是不可控,管制起来十分麻烦。

而且当这个工作没有执行结束或者咱们想每次都提交不同的工作的话,那么咱们没法对它进行长久化等操作

定时器咱们就说到这里

二、应用线程池创立线程


在JDK中给咱们提供线程池,不便咱们应用线程池可能创立一个多线程

什么是线程池?就是它首先是一个池子,这个池子外面装的是线程也就是说一个池子外面装了很多的线程

当咱们在去应用线程的时候,而不须要再去创立线程了而是间接从池子外面去获取线程

当咱们用完线程的时候也不去开释它,而是还给所谓的线程池,这就跟数据库连接池的原理是一样的它次要是升高线程的创立和销毁的资源的节约,就相当于拿空间换工夫,就是一个典型的缓存

首先咱们先介绍一下线程池最上层的接口:Executor

咱们发现它上面有很多的实现,个别用的最多的其实就是ExecutorService

刚刚提到Executor就代表一个线程池,但咱们发现Executor是一个接口,如何创立它呢?咱们没法new,不过能够通过Executors类下的办法创立

咱们能够通过这些办法来创立一个线程池,这些都是创立线程池的办法

同时咱们发现这些办法的返回值要么是ExecutorService,要么是ScheduledExecutorService。

而咱们刚刚也晓得个别应用最多的是ExecutorService子接口实现

所以咱们应用父接口去指向子类接口并没有什么问题

//创立一个可缓存线程池如果线程池长度超过解决须要//可灵便回收闲暇线程,若无可回收,则新建线程。newCachedThreadPool//创立一个定长线程池,可控制线程最大并发数,超出的线程会在队列中期待。newFixedThreadPool //创立一个定长线程池,反对定时及周期性工作执行。newScheduledThreadPool //创立一个单线程化的线程池,它只会用惟一的工作线程来执行工作//保障所有工作依照指定程序(FIFO, LIFO, 优先级)执行newSingleThreadExecutor 

咱们应用FixedThreadPool来创立一个固定容量的线程池,对于其余的线程池前面来具体的学习

class Demo5{    public static void main(String[] args) {        //创立十个线程        Executor executor = Executors.newFixedThreadPool(10);            }}

当咱们用到线程的时候,就向它(本例中threadPool)申请,用完了之后在返回给它

class Demo5{    public static void main(String[] args) {        //创立十个线程        Executor executor = Executors.newFixedThreadPool(10);        executor.execute(new Runnable() {            @Override            public void run() {                System.out.println(Thread.currentThread().getName()+"start....");            }        });    }}//运行后果如下:pool-1-thread-1start....

这就是咱们的线程工作只执行了一次,每一次execute()提交的是一个线程工作。

咱们说线程池中有多个线程,那么也就是说能够同时提交多个线程工作

class Demo5{    public static void main(String[] args) {        //创立十个线程        Executor executor = Executors.newFixedThreadPool(10);        for (int i = 0; i<10; i++){            executor.execute(new Runnable() {                @Override                public void run() {                    System.out.println(Thread.currentThread().getName()+"start....");                }            });        }    }}//运行后果如下:pool-1-thread-1start....pool-1-thread-7start....pool-1-thread-2start....pool-1-thread-3start....pool-1-thread-6start....pool-1-thread-4start....pool-1-thread-9start....pool-1-thread-10start....pool-1-thread-5start....pool-1-thread-8start....

当你运行起来就会发现当执行结束之后这个程序并没有进行,因为这是一个线程池,而你并没有通知它让它停掉

class Demo5{    public static void main(String[] args) {        //创立十个线程        ExecutorService executor = Executors.newFixedThreadPool(10);        for (int i = 0; i<10; i++){            executor.execute(new Runnable() {                @Override                public void run() {                    System.out.println(Thread.currentThread().getName()+"start....");                }            });        }        //进行线程池        executor.shutdown();    }}

如果以目前的代码提交一百次线程工作会输入什么呢?

class Demo5{    public static void main(String[] args) {        //创立十个线程        ExecutorService executor = Executors.newFixedThreadPool(10);        for (int i = 0; i<100; i++){            executor.execute(new Runnable() {                @Override                public void run() {                    System.out.println(Thread.currentThread().getName()+"start....");                }            });        }        //进行线程池        executor.shutdown();    }}//运行后果如下:pool-1-thread-1start....pool-1-thread-2start....pool-1-thread-1start....pool-1-thread-1start....pool-1-thread-1start....pool-1-thread-1start....pool-1-thread-1start....pool-1-thread-7start....

这时咱们发现当咱们提交一百个定时工作,发现并不是由100个线程所执行

也就是说当初相当于什么呢?第一个线程工作提交进来了,被第一个线程执行了可能,而后接着第二个线程工作被提交了,那么可能第二个线程就接着来干活。

哪个线程抢到执行权就那个线程去干活,这时有可能下面的还没有干完,而第二个线程干活比拟利落,它干完了之后返回给线程池(本例中的线程池是threadPool)

接着咱们又要提交一个线程工作,于是从线程池中拿到第二个线程接着干活于是就是这么一个状况,也就是说你即便提交了100个线程工作,它仍然是由这10个线程来进行干活

接下来咱们看看CachedThreadPool是一个什么样的,也是先来提交10个线程工作


咱们发现它差不多也是给咱们创立了10个线程,那么,咱们再来提交100个线程工作

咱们发现它的最大值达到了50了,其实这个是没有法则的

也就是说它这个线程池的大小,它是怎么来决定的呢?

其实它就是由你线程工作,你一直的提交线程工作,那么它就一直的创立

它认为不够用了它就去创立,它认为够用了它就回收

这是对于CachedThreadPool,比拟智能的一个线程池

参考资料


龙果学院:并发编程原理与实战(叶子猿老师)