[JDK8] Java线程的几种使用方法

单线程的使用方式

java.lang.Runnable

代码实现方式:

  1. 构造线程任务类MyRunnable,实现Runnable接口
  2. MyRunnable对象为参数,构造线程Thread实例
public class MyRunnable implements Runnable {
	@Override
	public void run() {
		System.out.println("任务实现");
	}

	public static void main(String[] args) {
		Thread thread = new Thread(new MyRunnable());
		thread.start();
	}
}

这种线程实现方式侧重于“线程”和“任务”的独立性,也就是概念的解藕,“线程”不关心“任务”的具体实现,“任务”也不关心“线程”的管理方式。

java.lang.Thread

代码实现方式:

  1. 继承线程管理类Thread,重写run()实现具体的线程任务
  2. 构造线程类MyThread实例
public class MyThread extends Thread {
	@Override
	public void run() {
		System.out.println("任务实现");
	}

	public static void main(String[] args) {
		Thread thread = new MyThread();
		thread.start();
	}
}

这种线程实现方式侧重于“任务线程”的整体概念,实现类MyThread既具备线程管理功能,又含有具体的任务实现。“线程”和“任务”同生共死,相依为命。

因为Java不支持多继承,所以,如果实现类MyThread还有另外的extends需求,就不能采用这种线程实现方式了。

java.util.concurrent.FutureTask

代码实现方式:

  1. 实现java.util.concurrent.Callable<V>接口,重写call()实现具体任务
  2. MyCallable对象实例为参数,构造出FutureTask对象实例
  3. 再以FutureTask对象实例为参数,构造出Thread对象实例
  4. 任务线程执行的时候,可以通过FutureTask对象的get()阻塞当前线程,直到任务线程执行完毕,返回任务执行结果。
public class MyCallable implements Callable<String> {
	@Override
	public String call() throws Exception {
		System.out.println("任务实现");
		return "线程执行结果";
	}

	public static void main(String[] args) throws ExecutionException, InterruptedException {
		FutureTask<String> task = new FutureTask<>(new MyCallable());
		Thread thread = new Thread(task);
		thread.start();
		String result = task.get();
		System.out.println(result);
	}
}

这种线程实现方式略显复杂,编码的时候会付出一定的代价,但是换来的结果是有价值的。不仅解藕了“线程”和“任务”的概念,并且“当前线程”还能获取到“任务线程”异步执行的结果。

也就是说,任务执行过程可控,这个特性在某些业务场景下非常具有实用价值。而这,也是前面两种线程实现方式很难达到的效果。

线程池的使用方式

创建线程池

Java线程池的整体框架主要是基于以下几个接口实现的:

java.util.concurrent.Executor
java.util.concurrent.ExecutorService
java.util.concurrent.ScheduledExecutorService
java.util.concurrent.ThreadFactory
java.util.concurrent.Callable

大多数情况下,开发者会借助java.util.concurrent.Executors创建并管理线程池。

Executors就是围绕线程池框架产生的一个工具类,可类比集合工具类java.util.Collections,还有数组工具类java.util.Arrays。都是JDK提供给开发者的福利,具有很高实用价值的工具类,通常情况下,开发者无需重复造轮子。

Executors可创建以下几类线程池:

package java.util.concurrent;

public class Executors {
    // 单线程工作的线程池,任务按提交顺序依次执行
    public static ExecutorService newSingleThreadExecutor() { ... }
    public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) { ... }

    // 最大线程数量固定的线程池,线程数量达到最大后保持不变
    public static ExecutorService newFixedThreadPool(int nThreads) { ... }
    public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) { ... }
    
    // 线程空闲后可以缓存一段时间的线程池,最大线程数量没有限制,多线程并发场景下使用很危险!
    public static ExecutorService newCachedThreadPool() { ... }
    public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) { ... }

    // 提交的任务可以被周期性执行的线程池
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { ... }
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory) { ... }

    // 单线程的线程池,周期性执行提交的任务
    public static ScheduledExecutorService newSingleThreadScheduledExecutor() { ... }
    public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) { ... }

    // 线程抢占式执行提交任务的线程池,底层原理和上面的几种线程池不太一样
    public static ExecutorService newWorkStealingPool(int parallelism) { ... }
    public static ExecutorService newWorkStealingPool() { ... }
}

提交线程任务

通过Executors工具类创建线程池后,提交任务的方法有四个,一个定义在Executor接口当中,三个定义在ExecutorService接口当中:

public interface Executor {
    void execute(Runnable command);
}

public interface ExecutorService extends Executor {
    Future<?> submit(Runnable task);
    <T> Future<T> submit(Callable<T> task);
    <T> Future<T> submit(Runnable task, T result);
}

单看这四个方法定义,就可以发现它们最大的区别在于方法返回值.通过execute提交的任务没有返回值,通过submit提交的任务有返回值。

关闭线程池

使用完线程池后,还应该主动关闭线程池。关闭线程池的方法有两个,都定义在ExecutorService接口里面:

public interface ExecutorService extends Executor {
    // 线程池不再接收新任务,等待池中已有任务执行完毕后,销毁线程池资源
    void shutdown();
    
    // 线程池不再接收新任务,尽可能停止正在执行的任务,尚未执行的任务列表作为方法的返回值,销毁线程池资源
    List<Runnable> shutdownNow();
}

需要注意的是,主动调用关闭线程池的方法后,线程池也不是一定就能很快的关闭,因为线程池可以被关闭的条件是池中没有处于活动状态的任务。

如果线程池正在执行的某些任务非常耗时,线程池也会等到这些耗时任务都执行完毕后再销毁资源。

如果线程池中某些正处于活动状态的任务不能响应线程停止的消息,无法被有效的停止,线程池就没办法关闭,也不能销毁资源。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值