带着面试官畅游Java线程池(上篇)

79 阅读2分钟

一、为什么使用线程池

java中经常需要用到多线程来处理一些业务,如果单纯使用继承Thread或者实现Runnable接口的方式来创建线程,那样势必有创建及销毁线程耗费资源、线程上下文切换问题。同时创建过多的线程也可能引发资源耗尽的风险,这个时候引入线程池比较合理,方便线程任务的管理。

java中涉及到线程池的相关类均在jdk1.5开始的 java.util.concurrent(JUC)包中,涉及到的几个核心类及接口包括:Executor、Executors、ExecutorService、ThreadPoolExecutor、FutureTask、Callable、Runnable等。

二、线程池的优势

总体来说,线程池有如下的优势:

(1)降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

(2)提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。

(3)提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

三、线程池的简单使用

1、Executors.newCachedThreadPool()

cached的池子非常大,可以有很多的线程并发运行。

package threadPool;

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

public class ThreadPoolDemo01 {

    public static void main(String[] args) {
    	
    	ExecutorService e1 = Executors.newCachedThreadPool();

//		ExecutorService e2 = Executors.newFixedThreadPool(10);
//		ExecutorService e3 = Executors.newSingleThreadExecutor();

    	//使用线程
    	e1.execute(new Task(1));
    }

}

class Task implements Runnable{
int i;
public Task(int i) {
this.i = i;
}

    @Override
    public void run() {
    	//打印当前线程的名字
    	System.out.println(Thread.currentThread().getName() + "-----" + i);
    }	

}

已经打出了当前线程的名字。

我们将调用线程的方法执行100次,由于run方法执行有时长,线程来不及回池子时就需要再执行,所以需要开启另一个线程。执行100次大概需要开启三十多个线程。

public class ThreadPoolDemo01 {

    public static void main(String[] args) {
    	
    	ExecutorService e1 = Executors.newCachedThreadPool();

//		ExecutorService e2 = Executors.newFixedThreadPool(10);
//		ExecutorService e3 = Executors.newSingleThreadExecutor();

    	//使用线程
    	for(int i=0; i<100; i++) {
    		e1.execute(new Task(i));
    	}
    }

}

class Task implements Runnable{
int i;
public Task(int i) {
this.i = i;
}

    @Override
    public void run() {
    	//打印当前线程的名字
    	System.out.println(Thread.currentThread().getName() + "-----" + i);
    }	

}

         线程池的序号大概是到三十几,说明从线程池中拿了三十多个线程。

        我们在run方法中加一个一秒的睡眠,再看看结果。

class Task implements Runnable{
int i;
public Task(int i) {
this.i = i;
}

    @Override
    public void run() {
    	//打印当前线程的名字
    	System.out.println(Thread.currentThread().getName() + "-----" + i);
    	//睡1s
    	try {
    		Thread.sleep(1000);
    	} catch (InterruptedException e) {
    		e.printStackTrace();
    	}
    }	

}

可以看到大概使用了100个线程。说明每一次调用都需要用一个新的线程。