java并发编程(二)-多线程实现的几种方式

一般人说到java多线程方式只有三种,继承Thread类、实现Runnable接口、实现Callable接口通过FutureTask包装器来创建Thread线程方式,在这里补充一种使用ExecutorService、Callable、Future实现有返回结果的多线程的方式。

其中前两种方式线程执行完后都没有返回值,后两种是带返回值的。

1、继承Thread类创建线程
Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。

启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。例如:

package com.caojiulu.runnable;

public class TestByThread extends Thread{
	public void run(){
		System.out.println(Thread.currentThread().getName()+": run");
	}
	
	public static void main(String[] args) {
		TestByThread testByThread1 = new TestByThread();
		TestByThread testByThread2 = new TestByThread();
		testByThread1.start();
		testByThread2.start();
	}

}

2、实现Runnable接口创建线程

使用Thread的方式有一个弊端,java是单继承的,如果继承了Thread,将不能继承其他类,这时候需要用到Runnbale。

例如:

package com.caojiulu.runnable;

public class TestByRunnable implements Runnable{

	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println(Thread.currentThread().getName()+": run");
	}
	public static void main(String[] args) {
		TestByRunnable testByRunnable = new TestByRunnable();
		Thread thread1 = new Thread(testByRunnable,"线程A");
		Thread thread2 = new Thread(testByRunnable,"线程B");
		thread1.start();
		thread2.start();
	}

}

3、实现Callable接口通过FutureTask包装器来创建Thread线程

package com.caojiulu.runnable;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class TestByCallable implements Callable<Integer>{

	@Override
	public Integer call() throws Exception {
		// TODO Auto-generated method stub
		System.out.println("测试多线程");
		return 96;
	}
	
	public static void main(String[] args) {
		Callable callable = new TestByCallable();
		FutureTask<Integer> futureTask = new FutureTask<Integer>(callable); 
		Thread thread = new Thread(futureTask);   
		thread.start();   
		
		try {
			Thread.sleep(3000);
			System.out.println(futureTask.get());
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ExecutionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}

}

使用Callable和FutureTask创建多线程方式可以获取返回值,FutureTask实现了两个接口,Runnable和Future。假设计算一个返回值,需要很长时间,可以创建一个新的线程去获取,然后在需要这个值的时候,调用futureTask.get()获取。

4、使用ExecutorService、Callable、Future的线程

注意:get方法是阻塞的,即:线程无返回结果,get方法会一直等待。

package com.caojiulu.runnable;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class TestExecutorService {
	
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		//创建一个线程池,5个
		ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(5);
		
		List<Future> list = new ArrayList<Future>();
		
		for (int i = 0; i < 5; i++) {
			Callable c = new Callable<Integer>() {
				@Override
				public Integer call() throws Exception {
					// TODO Auto-generated method stub
					return 10+new Random().nextInt(100);
				}
			};
			Future submit = newFixedThreadPool.submit(c);
			list.add(submit);
		}
		//关闭线程池
		newFixedThreadPool.shutdown();
		
		for (Future future : list) {
			System.out.println("获取值"+future.get());
		}
	}

}

通过Executors提供四种线程池,分别为:
1、newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。(线程最大并发数不可控制)
2、newFixedThreadPool:创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
3、newScheduledThreadPool:创建一个定长线程池,支持定时及周期性任务执行。
4、newSingleThreadExecutor:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

线程池比较单线程的优势在于:

a. 重用存在的线程,减少对象创建、消亡的开销,性能佳。
b. 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
c. 提供定时执行、定期执行、单线程、并发数控制等功能。

猜你喜欢

转载自blog.csdn.net/a1173537204/article/details/88388928