`

java多线程初探---创建线程方式及Executor框架

 
阅读更多

JAVA多线程实现方式主要有三种:继承Thread类、实现Runnable接口、使Executor框架(JDK 1.5引入)

 

		// 方式一:继承Thread类,重写run()方法
		Thread thread = new Thread() {
			@Override
			public void run() {
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("开始");
			}
		};
		thread.start();
		System.out.println("111");

 

		// 方式二:实现Runnable接口
		Thread thread = new Thread(new Runnable() {
			public void run() {
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("开始");
			}
		});

		thread.start();
		System.out.println("111");

 

输出的结果都是:(先输出111,再输出开始

111
开始

 

传统线程两种实现方式的区别:

可以查看《Java中有两种实现多线程的方式以及两种方式之间的区别文章。

 

实现Runnable接口相对于继承Thread类来说,有如下显著的好处: 

(1)适合多个相同程序代码的线程去处理同一资源的情况,把虚拟CPU(线程)同程序的代码,数据有效的分离,较好地体现了面向对象的设计思想。 

(2)可以避免由于Java的单继承特性带来的局限。我们经常碰到这样一种情况,即当我们要将已经继承了某一个类的子类放入多线程中,由于一个类不能同时有两个父类,所以不能用继承Thread类的方式,那么,这个类就只能采用实现Runnable接口的方式了。 

(3)有利于程序的健壮性,代码能够被多个线程共享,代码与数据是独立的。当多个线程的执行代码来自同一个类的实例时,即称它们共享相同的代码。多个线程操作相同的数据,与它们的代码无关。当共享访问相同的对象是,即它们共享相同的数据。当线程被构造时,需要的代码和数据通过一个对象作为构造函数实参传递进去,这个对象就是一个实现了Runnable接口的类的实例。 

 

 

 

 

下面重点讲解Executor框架

Executor框架是指java 5中引入的一系列并发库中与executor相关的一些功能类,其中包括线程池,Executor,Executors,ExecutorService,CompletionService,Future,Callable等。

Executor框架可以实现子线程与主线程的“通信”,能够传递数据。

Executors类,提供了一系列工厂方法用于创先线程池,返回的线程池都实现了ExecutorService接口。

 

直接看代码

		Executor executor = Executors.newFixedThreadPool(10);
		Runnable task = new Runnable() {
			public void run() {
				System.out.println("task executing");
			}
		};
		executor.execute(task);

		System.out.println("111");

 

输出结果:

111
task executing

 

子线程会一直处于运行状态。

 

更换Executor 接口为ExecutorService 接口,可以调用executor.shutdown(); 在子线程运行完毕之后,结束子线程的运行。

 

		ExecutorService executor = Executors.newFixedThreadPool(10);//创建10个线程池
		Runnable task = new Runnable() {   
		public void run() {   
		        System.out.println("task executing");   
		    }   
		};   
		executor.execute(task);   
		  
		System.out.println("111");   
		executor.shutdown();//运行结束,关闭线程池  

ExecutorService 扩充了Executor 接口的方法,比较常见的实现类是ThreadPoolExecutor,详细用法可参考API手册。

 

shutdown() 

启动一次顺序关闭,执行以前提交的任务,但不接受新任务。 

 

 

public static ExecutorService newFixedThreadPool(int nThreads)

创建固定数目线程的线程池。

public static ExecutorService newCachedThreadPool()

创建一个可缓存的线程池,调用execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。

public static ExecutorService newSingleThreadExecutor()

创建一个单线程化的Executor。

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)

创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。

 

 

		Executor executor = Executors.newFixedThreadPool(10);
		Runnable task = new Runnable() {

			public void run() {
				System.out.println("task executing");
			}

		};
		executor.execute(task);

		ScheduledExecutorService scheduler = (ScheduledExecutorService) Executors
				.newScheduledThreadPool(10);
		scheduler.scheduleAtFixedRate(task, 5, 1, TimeUnit.SECONDS);

 

 输出结果是:

task executing

task executing

task executing

 

程序会先输出 task executing,过5秒之后,再次输出  task executing ,然后,每间隔1秒输出 task executing

 

关于多线程Callable和Future

       从java 5开始,Java提供了Callable接口,该接口是Runnable接口的增强版,Callable接口提供了一个call()方法,可以作为线程执行体,但call()方法比run()方法的功能更强大:有返回值,可以声明抛出异常

      因此我们完全可以提供一个Callable对象作为Thread的target,而该线程的线程执行体就是Callable对象的call()方法。

 

       在工具开发包中,java 5 提供了Future接口来代表Callable接口里的call()方法的返回值,并且为Future接口提供了一个FutureTask实现类,
       该实现类实现了Future接口,并且实现了Runnable接口——可以作为Thread类的target。
       实际上这里的FutureTask类实现了一个桥梁的作用,用于连接Callable对象和Thread对象。
      
       需要注意的是Callable接口有泛型限制,Callable接口里的泛型形参类型,与call()方法的返回值类型相同。


创建并启动有返回值的线程步骤与实现Runnable接口相似:
1.创建Callable接口的实现类,并实现call()方法
2.创建Callable实现类的实例,使用FutureTask类来包装Callable对象
3.使用FutrueTask对象作为Thread对象的target创建并启动新线程。
4.调用FutrueTask对象的get()方法来获得子线程执行结束后的返回值。

 

 

public class ThirdThread implements Callable<Integer> {
	public Integer call() {
		int i = 0;
		for (; i < 30; i++) {
			System.out.println(Thread.currentThread().getName() + "循环变量i的值是 : "
					+ i);
		}
		return i;
	}

	public static void main(String[] args) {

		ThirdThread rt = new ThirdThread(); // 创建Callable对象
		FutureTask<Integer> task = new FutureTask<Integer>(rt);// 使用FutrueTask来包装Callable对象
		for (int i = 0; i < 30; i++) {
			System.out
					.println(Thread.currentThread().getName() + "-------" + i);
			if (i == 4) {
				new Thread(task, "有返回值的线程").start(); // 实际上还是以Callable对象创建并启动线程
			}
		}
		try {
			System.out.println("子线程的返回值 :" + task.get());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

 

分析:当 i=4 时,主线程与子线程争夺资源 ,子线程名称更改为“有返回值的线程”

子线程 从 0 到 30 开始循环输出,直到i=30退出,将值返回给主线程,

主线程执行 task.get() 方法时,等待子线程计算完成,然后获取其结果。

 

 

输出结果:

main-------0
main-------1
main-------2
main-------3
main-------4
main-------5
main-------6
main-------7
main-------8
main-------9
main-------10
main-------11
main-------12
main-------13
main-------14
main-------15
main-------16
main-------17
有返回值的线程循环变量i的值是 : 0
main-------18
main-------19
main-------20
main-------21
main-------22
有返回值的线程循环变量i的值是 : 1
main-------23
有返回值的线程循环变量i的值是 : 2
main-------24
有返回值的线程循环变量i的值是 : 3
main-------25
有返回值的线程循环变量i的值是 : 4
main-------26
有返回值的线程循环变量i的值是 : 5
main-------27
有返回值的线程循环变量i的值是 : 6
main-------28
有返回值的线程循环变量i的值是 : 7
main-------29
有返回值的线程循环变量i的值是 : 8
有返回值的线程循环变量i的值是 : 9
有返回值的线程循环变量i的值是 : 10
有返回值的线程循环变量i的值是 : 11
有返回值的线程循环变量i的值是 : 12
有返回值的线程循环变量i的值是 : 13
有返回值的线程循环变量i的值是 : 14
有返回值的线程循环变量i的值是 : 15
有返回值的线程循环变量i的值是 : 16
有返回值的线程循环变量i的值是 : 17
有返回值的线程循环变量i的值是 : 18
有返回值的线程循环变量i的值是 : 19
有返回值的线程循环变量i的值是 : 20
有返回值的线程循环变量i的值是 : 21
有返回值的线程循环变量i的值是 : 22
有返回值的线程循环变量i的值是 : 23
有返回值的线程循环变量i的值是 : 24
有返回值的线程循环变量i的值是 : 25
有返回值的线程循环变量i的值是 : 26
有返回值的线程循环变量i的值是 : 27
有返回值的线程循环变量i的值是 : 28
有返回值的线程循环变量i的值是 : 29
子线程的返回值 :30

 

 

class MyCallable implements Callable<Object> {
	private String taskNum;

	MyCallable(String taskNum) {
		this.taskNum = taskNum;
	}

	public Object call() throws Exception {
		System.out.println(">>>" + taskNum + "任务启动");
		Thread.sleep(1000);
		System.out.println(">>>" + taskNum + "任务终止");
		return taskNum + "任务返回运行结果";
	}
}

 

 

public class Test {
	public static void main(String[] args) throws ExecutionException,
			InterruptedException {
		System.out.println("----程序开始运行----");

		int taskSize = 5;
		// 创建一个线程池
		ExecutorService pool = Executors.newFixedThreadPool(taskSize);
		// 创建多个有返回值的任务
		List<Future> list = new ArrayList<Future>();
		for (int i = 0; i < taskSize; i++) {
			Callable c = new MyCallable(i + " ");
			// 执行任务并获取Future对象
			Future f = pool.submit(c);
			list.add(f);
		}
		// 关闭线程池
		pool.shutdown();

		// 获取所有并发任务的运行结果
		for (Future f : list) {
			// 从Future对象上获取任务的返回值,并输出到控制台
			System.out.println(">>>" + f.get().toString());
		}

		System.out.println("----程序结束运行----");
	}
}

 

ExecutoreService提供了submit()方法,传递一个Callable,或Runnable,返回Future。如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成。

 

执行结果:

 ----程序开始运行----
>>>2 任务启动
>>>1 任务启动
>>>3 任务启动
>>>0 任务启动
>>>4 任务启动
>>>2 任务终止
>>>1 任务终止
>>>3 任务终止
>>>0 任务终止
>>>0 任务返回运行结果
>>>1 任务返回运行结果
>>>2 任务返回运行结果
>>>3 任务返回运行结果
>>>4 任务终止
>>>4 任务返回运行结果
----程序结束运行----

 

 

定时执行

class Task implements Callable<String> {

	public String call() throws Exception {
		System.out.println("task executing");
		return "success";
	}
}

 

客户端调用

public static void main(String[] args) throws InterruptedException,
			ExecutionException {

		ScheduledThreadPoolExecutor executor = (ScheduledThreadPoolExecutor) Executors
				.newScheduledThreadPool(10);
		ScheduledFuture<String> schedule = executor.schedule(new Task(), 1,
				TimeUnit.SECONDS);

		String retVal = schedule.get();
		System.out.println(retVal);
		executor.shutdown();
	}

 启动之后,1秒执行,输出结果:

task executing

success

 

在executor.shutdown()方法执行执行的时候,如果还有任务没有执行完成,可以执行

executor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);

“一盆凉水”将剩余的任务“浇灭”

 

再看下面的例子

class Task implements Callable<String> {
	private String taskNum;

	public Task(String taskNum) {
		this.taskNum = taskNum;
	}

	public Task() {
	}

	public String call() throws Exception {
		System.out.println("task " + taskNum + " executing");
		return "success";
	}
}

 

 返回第一个结果

   

// 创建一个线程池
  ExecutorService pool = Executors.newFixedThreadPool(5);

  List tasks = new ArrayList<Task>();

  tasks.add(new Task("1111"));
  tasks.add(new Task("2222"));

  String result = pool.invokeAny(tasks);
  System.out.println(result.toString());

  // 关闭线程池
  pool.shutdown();

 

 

返回所有结果

// 创建一个线程池
  ExecutorService pool = Executors.newFixedThreadPool(5);

  List tasks = new ArrayList<Task>();

  tasks.add(new Task("1111"));
  tasks.add(new Task("2222"));

  List<Future<MyCallable>> result = pool.invokeAll(tasks);
  
  for (Future<MyCallable> future : result) {
     System.out.println(future.get());
  }

  // 关闭线程池
  pool.shutdown();

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics