线程池

1.1 线程池思想概述

池化技术

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

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

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

在Java中可以通过线程池来达到这样的效果。今天我们就来详细讲解一下Java的线程池。

1.2 线程池概念

程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。因为启动线程的时候会在内存中开辟一块空间,消耗系统资源,同时销毁线程的时候首先要把和线程相关东西进行销毁,还要把系统的资源还给系统。这些操作都会降低操作性能。尤其针对一个线程用完就销毁的更加降低效率。

而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,生存期较短的线程指的是用完一次线程就丢掉。更应该考虑使用线程池。

线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。

线程池工作原理如下图所示:

需求:我有一段任务,需要执行100次。

说明:

每次线程执行完任务以后,线程不会销毁,会放回线程池中,每次在执行任务的时候又会到线程池中去取线程。这样会提高效率。

合理利用线程池能够带来三个好处:

  1. 降低资源消耗。减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
  2. 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
  3. 提高线程的可管理性。可以根据系统的承受能力,调整线程池中工作线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。

1.3 线程池的使用

在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池。

Java里面线程池的顶级接口是java.util.concurrent.Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是java.util.concurrent.ExecutorService

如何获取线程池?

要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此使用java.util.concurrent.Executors线程池工具类来生成一些常用的线程池。官方建议使用Executors工具类来创建线程池对象。

Executors是一个线程池的工具类,专门用来生产线程池。

线程池Executors的工具类中的函数如下所示,通过以下函数可以创建线程池:

  • public static ExecutorService newFixedThreadPool(int nThreads):创建一个可重用固定线程数的线程池。

说明:以上这个函数是按照指定线程个数来创建线程池,在创建线程池的时候直接指定线程的个数。

问题:我们发现上述获取线程池之后,函数的返回值是ExecutorService ,既然是获取线程池,那么为什么返回的不是Executor线程池却是一个接口ExecutorService 呢?

进一步解释上述获取线程池的函数返回值类型ExecutorService :

ExecutorService 属于真正的线程池接口。

public interface ExecutorService extends Executor

ExecutorService 接口的父接口是Executor。

如何执行我们的线程任务呢?

获取到了一个线程池ExecutorService 对象,那么怎么使用呢,在这里定义了一个使用线程池对象的方法如下:

  • void execute(Runnable command) 在将来的某个时间执行给定的命令。  
    

说明:这个方法是ExecutorService 的父类Executor中的方法。表示获取线程池中的某一个线程对象,并执行。

使用线程池中线程对象的步骤:

​ A:自定义一个类,作为任务类并实现Runnable接口;

​ B:实现Runnable接口中的run方法;

​ C:创建任务类的对象;

​ D:获取线程池对象;

​ E:直接执行任务;

用线程池执行卖票程序

需求:使用线程池来完成卖票任务。

Runnable实现类代码:

package com.SiyualChen.day03.test06;

public class SellTicketTask implements Runnable{
    private int tickets=100;
    @Override
    public void run() {

        while (true) {
            synchronized (this) {
                if (tickets > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "出票" + "这是" + (100 - tickets + 1) + "第张票");
                    tickets--;
                } else {
                    break;
                }

            }
        }
    }
}

package com.SiyualChen.day03.test06;



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

public class SellTickestDemo {
    public static void main(String[] args) {
        SellTicketTask sellTicketTask = new SellTicketTask();
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
            executorService.execute(sellTicketTask);
        }


    }
}

pool-1-thread-4出票这是90第张票
pool-1-thread-4出票这是91第张票
pool-1-thread-3出票这是92第张票
pool-1-thread-3出票这是93第张票
pool-1-thread-3出票这是94第张票
pool-1-thread-3出票这是95第张票
pool-1-thread-3出票这是96第张票
pool-1-thread-3出票这是97第张票
pool-1-thread-3出票这是98第张票
pool-1-thread-3出票这是99第张票
pool-1-thread-3出票这是100第张票

大家以为这样就结束啦? 有没有发现一个问题,线程是跑完了,程序没关闭。我类个去!!!这里有个坑。必须调用shutdown()函数结束线程。

shutdown()。 //结束线程
package com.SiyualChen.day03.test06;



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

public class SellTickestDemo {
    public static void main(String[] args) {
        SellTicketTask sellTicketTask = new SellTicketTask();
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
            executorService.execute(sellTicketTask);
        }
        executorService.shutdown();   //这里是坑


    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值