java多线程、线程池的实现

本文深入解析Java实现多线程的三种方法:继承Thread类、实现Runnable接口、使用Callable、Future实现有返回值的多线程,并详细讲解如何创建不同类型的线程池,包括固定大小的线程池、单任务线程池、可变尺寸的线程池和延迟线程池。同时,文章阐述了线程池的饱和策略,如AbortPolicy、CallerRunsPolicy、DiscardPolicy和DiscardOldestPolicy。

一、多线程的创建

Java实现多线程的3种方法:继承Thread类、实现runnable接口、使用ExecutorService,Callable、Future实现有返回值的多线程。前2种线程的实现方式没有返回值,第三种实现方式可以获取线程执行的返回值。

1、继承java.lang.Thread类

public class MyThread extends Thread {

	@Override
	public void run() {
		System.out.println( "my thread begin." );
		try {
			// 休眠1000毫秒
			Thread.sleep( 1000 );
		} catch ( InterruptedException e ) {
			e.printStackTrace();
		}
		System.out.println( "my thread over." );
	}

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

Thread类本身实现了Runnable接口。

2、实现java.lang.Runnable接口

public class MyThread implements Runnable {

	@Override
	public void run() {
		System.out.println( "my thread begin." );
		try {
			// 休眠1000毫秒
			Thread.sleep( 1000 );
			// do something...
		} catch ( InterruptedException e ) {
			e.printStackTrace();
		}
		System.out.println( "my thread over." );
	}

	public static void main( String[] args ) {
		Thread thread = new Thread( new MyThread() );
		thread.start();
	}
}
实现Runnable接口会比继承Thread类更灵活一些,因为Java是单继承,继承了一个类就不能再继承另外一个类,而接口可以实现多个。但是无论是继承Thread类还是实现Runnable接口都不能获取多线程的返回值,除非借助额外的变量,在run方法中修改一个变量,在其他地方使用这个变量,这种实现方式比较"鸡肋"。

3、实现java.util.concurrent.Callable接口

public class MyThread implements Callable<Object> {

	@Override
	public Object call() throws Exception {
		System.out.println( "my thread begin." );
		try {
			// 休眠1000毫秒
			Thread.sleep( 1000 );
		} catch ( InterruptedException e ) {
			e.printStackTrace();
		}
		System.out.println( "my thread over." );
		return "ok";
	}

	public static void main( String[] args ) {
		// 创建一个线程池
		ExecutorService pool = Executors.newFixedThreadPool( 2 );
		Future<Object> f = pool.submit( new MyThread() );
		//关闭线程池
		pool.shutdown();
		try {
			System.out.println( f.get() );
		} catch ( InterruptedException e ) {
			e.printStackTrace();
		} catch ( ExecutionException e ) {
			e.printStackTrace();
		};
	}
}

通过创建一个线程池提交一个Callable任务就可以通过Future类来获取线程的返回值了,调用Futureget()方法的时候,如果任务没有完成则阻塞直到任务完成。正如get()的注释:Waits if necessary for the computation to complete, and then retrieves its result.

面代码中的ExecutorService接口继承自Executor接口,Executor的实现基于生产者-消费者模式。提交任务的执行者是生产者(产生待完成的工作单元),执行任务的线程是消费者(消耗掉这些工作单元)。如果要实现一个生产者-消费者的设计,使用Executor通常是最简单的方式。

二、线程池的实现

Executors类提供了几种创建线程池的方法,通过Exectors创建的线程池也实现了ExecutorService接口,方法如下:

1、固定大小的线程池:newFixedThreadPool(int nThreads)

创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

2、单任务线程池:Executors.newSingleThreadExecutor()

建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

3、可变尺寸的线程池:Executors.newCachedThreadPool()

创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

4、延迟线程池:Executors.newScheduledThreadPool(int corePoolSize)

创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

注意:shutdown()方法并不是终止线程的执行,而是禁止在这个Executor中添加新的任务。

最终实现都是依赖下面的类实现

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        //...
    }
也可以直接 new ThreadPoolExecutor(...)来定制自己的线程池实现。

三、线程池的饱和策略

1、AbortPolicy

终止

2、CallerRunsPolicy

调用者线程执行

3、DiscardPolicy

丢弃

4、DiscardOldestPolicy

丢弃最老的任务


Java多线程线程池Java并发编程中的重要概念。 ### Java多线程 Java多线程允许程序在同一时间执行多个任务,提升了程序的性能和响应能力。线程是程序执行的最小单位,多线程即多个线程同时运行。 - **线程状态转化**:线程存在多种状态,状态之间可相互转化,有新建、就绪、运行、阻塞、死亡等状态。 - **常用方法**:线程相关常用方法有`wait()`、`sleep(long timeout)`、`join()`、`yield()`、`notify()`和`notifyAll()`等。其中,`wait()`和`sleep()`存在区别,`wait()`用于线程间的通信,会释放对象锁;`sleep()`让线程暂停执行指定时间,不会释放对象锁。`wait()`、`notify()`、`notifyAll()`方法定义在`Object`类里,因为这些方法是用于线程间通信,需要操作对象的锁,而每个对象都有锁,所以定义在`Object`类中,而非`Thread`类 [^2]。 - **实现方式**: - **继承`Thread`类**:创建一个类继承`Thread`类,重写`run()`方法,只能继承一个类。 - **实现`Runnable`接口**:创建一个类实现`Runnable`接口,实现`run()`方法,可以实现多个接口。 - **使用`Callable`、`FutureTask`**:可实现有返回结果的多线程。 - **使用线程池**:能有效控制线程的创建和销毁,提高系统性能和资源利用率 [^2]。 ### Java线程池 线程池是一种多线程处理形式,处理过程中将任务添加到队列,创建线程后自动启动这些任务。主要由线程池管理器、工作线程、任务队列和任务接口四个部分组成 [^1]。 - **好处**:可有效控制线程的创建和销毁,提高系统的性能和资源利用率。 - **七大参数**:可用银行柜台举例理解。 - **四大方法**: - `Executors.newCachedThreadPool()`:创建一个可缓存的线程池,线程数会根据任务动态调整。 - `Executors.newFixedThreadPool()`:创建一个固定大小的线程池,线程数固定。 - `Executors.newSingleThreadPool()`:创建一个单线程的线程池,只有一个线程执行任务。 - `Executors.newScheduleThreadPool()`:创建一个可定时执行任务的线程池 [^2]。 - **任务提交与执行机制**:线程池执行`execute/submit`方法向线程池添加任务。当任务小于核心线程数`corePoolSize`,线程池中可创建新线程,即便有空闲线程;当任务大于核心线程数`corePoolSize`,向阻塞队列`workQueue`添加任务;当阻塞队列`workQueue`满了,且最大线程池大小`maximumPoolSize`大于核心线程数`corePoolSize`,新提交任务会创建新线程执行;当提交任务数超过最大线程池大小`maximumPoolSize`,执行饱和策略;当线程池中超过核心线程数`corePoolSize`的线程,空闲时间达到`keepAliveTime`,关闭空闲线程;当设置`allowCoreThreadTimeOut(true)`时,核心线程空闲时间达到`keepAliveTime`也将关闭 [^3]。 - **`execute()`和`submit()`方法区别**:`execute()`用于提交不需要返回值的任务,无法判断任务是否执行成功;`submit()`用于提交需要返回值的任务,线程池会返回`future`类型对象,可判断任务是否执行成功,通过`get()`方法获取返回值,`get()`会阻塞当前线程直到任务完成,`get(long timeout, TimeUnit unit)`会阻塞一段时间后立即返回 [^3]。 - **四大拒绝策略**:当线程池无法处理新提交的任务时,会执行相应的拒绝策略。 以下是使用线程池的示例代码: ```java import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolExample { public static void main(String[] args) { // 创建一个固定大小的线程池 ExecutorService executor = Executors.newFixedThreadPool(3); // 提交任务 for (int i = 0; i < 5; i++) { final int taskId = i; executor.submit(() -> { System.out.println("Task " + taskId + " is running on thread " + Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Task " + taskId + " is completed."); }); } // 关闭线程池 executor.shutdown(); } } ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值