Java中线程的几种使用方式:
Runnable
Runnable是一个接口,直接实现并在run
方法中执行相应的任务。
private class MyRunnableTask implements Runnable {
@Override
public void run() {
// running my task
int index = 0;
while (index < 10) {
System.out.println("I'm the task " + (++index));
}
}
}
public void createRunnable() {
MyRunnableTask runnableTask = new MyRunnableTask();
runnableTask.run();
}
Thread 线程
普通线程创建和启动的方式,创建Thread的实例时将Runnable对象传入其中,start()
启动线程即可:
public static void createThread() {
Thread myThread = new Thread(new MyRunnableTask());
myThread.start();
}
ThreadPool 线程池
Executor
public interface Executor {
void execute(Runnable command);
}
Executor是用于代替显示的Thread创建,ExecutorService是拥有服务生命周期的Executor(例如shutDown / terminal等),ExecutorService需要通过Executors使用工厂方法来进行创建:
public void createExecutor() {
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(new MyRunnableTask());
}
Executors
Executor的工厂方法工具集,提供了各类ThreadPool的创建方法。注意创建的都是ExecutorService类型的对象。如下所示创建一个含有固定线程数的线程池,nThreads
代表线程数量。更多方法可以查阅Executors.java
文件。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
ExecutorService
public interface ExecutorService extends Executor
ExecutorService继承自Executor,提供了更多的生命周期相关的方法,例如:
void shutdown()
<T> Future<T> submit(Callable<T> task)
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException
- 省略…
源码注释中提供了Usage Examples:
class NetworkService implements Runnable {
private final ServerSocket serverSocket;
private final ExecutorService pool;
public NetworkService(int port, int poolSize)
throws IOException {
serverSocket = new ServerSocket(port);
pool = Executors.newFixedThreadPool(poolSize);
}
public void run() { // run the service
try {
for (;;) {
pool.execute(new Handler(serverSocket.accept()));
}
} catch (IOException ex) {
pool.shutdown();
}
}
}
class Handler implements Runnable {
private final Socket socket;
Handler(Socket socket) { this.socket = socket; }
public void run() {
// read and service request on socket
}
}
以上代码可以看到,主要分为三个部分
- 构造函数的初始化,建立了一个Socket对象,同时创建了一个固定线程数的线程池实例。
- 内部类Handler实现了Runnable方法,通过Socket进行相应的服务请求与读取
run()
方法中循环执行了Handler的任务
这就是线程池相应的使用。简单来说其实线程池只是由于Thread的种种不便应运而生的一种线程处理机制,没有想象的那么复杂与麻烦。并且相对的比起Thread的普通创建方式来说,线程池能够节省资源开销,更合理和优雅的管理线程的状态。
Java通过Executor提供四种线程池:
CachedThreadPool : 将为每个任务都创建一个线程
FixedThreadPool : 预先执行代价高昂的线程分配,限制线程数量。优点是节省时间(创建线程需要开销)。直接从池中获取线程
SingleThreadExecutor : 以FIFO的入队方式处理任务。在第一个任务处理完之前第二个任务出于等待状态
- ScheduledThreadPool : 能够延迟、周期性的执行任务,可以用于替换Timer
线程池优点:
- 在任何线程池中,现有的线程都可能被自动复用(线程池的优点之一就是资源复用)
- 减少new Thread()的性能开销,每次使用线程都伴随Thread的创建和销毁开销,性能很差
- 上面我们可以看到创建时能够指定线程的并发数量,也就是说,我们能够对线程进行有效的管理,普通创建线程只好待其自我执行、销毁,并且执行的时间点我们也无法得知,而这些事情通过线程池我们都能做到
我们同样使用线程池来进行之前的循环打印,注意使用Executors和ExecutorService需要将其进行包的导入,两者都属于java.util.concurrent
路径下:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadDemo {
private static class MyRunnableTask implements Runnable {
@Override
public void run() {
int index = 0;
while (index < 10) {
System.out.println("I'm the task " + (++index));
}
}
}
public static void createRunnable() {
MyRunnableTask runnableTask = new MyRunnableTask();
runnableTask.run();
}
public static void createThread() {
Thread myThread = new Thread(new MyRunnableTask());
myThread.start();
}
public static void createThreadPool() {
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new MyRunnableTask());
}
public static void main(String[] args) {
// createThread();
// createRunnable();
createThreadPool();
}
}
三种方式都可以打印出相应的结果:
➜ ~ javac ThreadDemo.java
➜ ~ java ThreadDemo
I'm the task 1
I'm the task 2
I'm the task 3
I'm the task 4
I'm the task 5
I'm the task 6
I'm the task 7
I'm the task 8
I'm the task 9
I'm the task 10