`
liugang594
  • 浏览: 977476 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Java线程类二

 
阅读更多

一、java.util.concurrent.Exchanger

Java 5中新增加了一个Exchanger类,这个类可以用来在一对线程之间交换元素,并且这种交换是线程安全的,不需要同步,具体说来就是每个线程将它想交换的对象放到exchanger对象中去,然后从这个对象返回对方线程用来交换的对象。有一点要求就是这两个交换的对象类型必须相同。

例如要实现生产者、消费者应用,以前可能的一种作法就是用一个集合:一个线程往里写,另一个线程从里面读。现在,如果用Exchanger的方式,那可以用两个集合,一个用在生产端,一个用在消费端,然后不时的对他们进行交换,例如:

		Exchanger<List<String>> exchanger = new Exchanger<List<String>>();
		Producer producer = new Producer(exchanger);
		Consumer consumer = new Consumer(exchanger);
		producer.start();
		consumer.start();

首先创建一个Exchanger对象,并指定了它将交换的内容格式,然后分别创建Producer对象和Consumer对象,并将此Exchanger对象传入。Producer类实现如下:

class Producer extends Thread{
	private Exchanger<List<String>> exchanger;
	
	private List<String> storage = new ArrayList<String>();
	
	public Producer(Exchanger<List<String>> e){
		this.exchanger = e;
	}
	
	@Override
	public void run() {
		int i = 0;
		while(true){
			//store something into storage
			storage.add("One"+i);
			storage.add("two"+i);
			storage.add("three"+i);
			try {
				//show the storage before exchange
				System.out.println("Produced "+storage);
				//exchange with consumer, and get the exchanged from it
				storage = exchanger.exchange(storage);
				//show the exchanged result
				System.out.println("After exchanged on Producer: "+storage);
				//sleep for a while before continue
				sleep(2000);
				i++;
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
}

 它会不间断的生产,并且每次生产的内容都不一样,然后将生产的东西进行交换。它并不需要知道有谁在和它交换,只要将要交换的内容传给Exchanger,并从Exchanger取会交换的结果。Consumer的实现如下:

class Consumer extends Thread{
	private Exchanger<List<String>> exchanger;
	
	private List<String> storage = new ArrayList<String>();
	
	public Consumer(Exchanger<List<String>> e){
		this.exchanger = e;
	}
	
	@Override
	public void run() {
		while(true){
			try {
				/*
				 * do exchange, the storage is empty before exchanged
				 * and will be exchanged to producer
				 */
				storage = exchanger.exchange(storage);
				//show the exchanged result
				System.out.println("Consumed: "+storage);
				System.out.println("======================================");
				/*
				 * remember to reset storage before continue, else the values
				 * will be back to producer side
				 */
				storage.clear();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
}

 执行过程和Producer类似,不过需要注意的是:在每次使用完交换来的内容后,要记得清空,要不然结果又会被交换回Producer端。以上程序某次运行的结果可能如下:

Produced [One0, two0, three0]
After exchanged on Producer: []
Consumed: [One0, two0, three0]
======================================
Produced [One1, two1, three1]
After exchanged on Producer: []
Consumed: [One1, two1, three1]
======================================
Produced [One2, two2, three2]
After exchanged on Producer: []
Consumed: [One2, two2, three2]
======================================

 二、分支/合并(Fork/Join)框架

Java 7提供了一个分支/合并(Fork/Join)框架,它的作者是Doug Lea,实现来源于他于2000年发表的一篇论文:http://gee.cs.oswego.edu/dl/papers/fj.pdf 。简单的说就是:Fork -- 将大任务分解成一系列小任务分别执行,每个小任务由一个线程执行; Join -- 在每个小任务执行完成后,将它们的结果合并,得到最终的结果。(我不太清楚它和最近比较流行的Mapper/Reduce编程模式有多大的不同)

 

要使用Fork/Join框架,涉及到以下几个主要的类:

  1. ForkJoinPool:这是ForkJoin任务执行线程所在的线程池,由它创建一系统的线程去执行每个小任务。可以指定初始的并发数,如果不指定,由默认值为当前机器的内核数,这个可以从源码里得出:Runtime.getRuntime().availableProcessors()。
  2. ForkJoinTask这是ForkJoin任务的抽象实现,一般不直接继承它,而是使用它的以下两个抽象子类。
  3. RecursiveAction:ForkJoinTask的子类,一般用于没有返回值的情况
  4. RecursiveTask:ForkJoinTask的子类,一般用于有返回值的情况

 其中ForkJoinPool有三个用来提交或开始任务的方法:

  1. submit:异步的执行Task,并且在任务结束后,可以使用getRawResult()方法获取返回值;在获取返回值之前,可能需要使用如 is*() 方法判断当前任务的执行结果。
  2. execute:同submit类似,但是不带返回值
  3. invoke:同submit类似,但是是同步的,即方法会在任务结果里才返回。可以看它的源码实现,返回task.join()或者task.invoke()。
ForkJoinTask也有两个用来执行的方法:
  1. fork:类似于上面的submit
  2. invoke:类似于上面的invoke方法
下面用一个示例来介绍Fork/Join方法的用法。
我们都知道斐波那契数列由以下公式定义:
f(n) = f(n-1) + f(n-2); n-2>=0; f(0)=0; f(1)=1;
由以上对Fork/Join框架的介绍,可以试着用它来计算斐波那契数列的值:每一个f(n)都可以分解成对f(n-1)和f(n-2)的计算,然后把他们的结果合并,得到最终的值。
首先看一下Task的代码,如下:
	private static class FibonacciForkTask extends RecursiveTask<Long> { //因为需要有返回值,所以继承自RecursiveTask

		private static final long serialVersionUID = 1L;
		private int n;

		public FibonacciForkTask(int n) {
			this.n = n;
		}

		@Override
		protected Long compute() {
			if (n == 0) {
				return 0L;
			}
			if (n == 1) {
				return 1L;
			}
			FibonacciForkTask t1 = new FibonacciForkTask(n - 1);//子任务1
			FibonacciForkTask t2 = new FibonacciForkTask(n - 2);//子任务2
			invokeAll(t1, t2);//因为需要等待所有子任务的结果,所以用invokeAll()去调用
			try {
				Long long1 = t1.get();//取得结果1
				Long long2 = t2.get();//取得结果2
				return long1 + long2;//将两个子任务的结果求和,返回
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (ExecutionException e) {
				e.printStackTrace();
			}
			return null;
		}

	}
需要留意的两点:1.因为需要有返回值,所以子类继承RecursiveTask;因为结果需要把等待所有子任务完成然后求和,所以用invokeAll()调用。
然后我们看看怎么用这个子任务:
		ForkJoinPool forkJoinPool = new ForkJoinPool();
		ForkJoinTask<Long> submit = forkJoinPool.submit(new FibonacciForkTask(10));
		forkJoinPool.awaitTermination(0, TimeUnit.SECONDS);
		Long long1 = submit.get();
		System.out.println(long1);
		forkJoinPool.shutdown();
上面先创建一个ForkJoinPool对象,然后用submit()方法异常的调用之前我们定义的Task,然后等待任务结束,最后取得结果打印:
55
为了验证确实是有多线程在运行,并且进行了足够多次的任务划分,可以在compute()方法的任务划分之前加一个打印信息,例如(为了减少打印行数,我把上面的10改成了5):
protected Long compute() {
			if (n == 0) {
				return 0L;
			}
			if (n == 1) {
				return 1L;
			}
			System.out.println(Thread.currentThread().getName());
                        ...
}
在我机器上打印的结果如下:
ForkJoinPool-1-worker-1
ForkJoinPool-1-worker-1
ForkJoinPool-1-worker-1
ForkJoinPool-1-worker-2
ForkJoinPool-1-worker-2
ForkJoinPool-1-worker-1
ForkJoinPool-1-worker-3
5
 可以看到执行了7次划分(可以手工计划一下,确实是需要进行7次划分),这些划分是在3个线程中执行的。如果把打印语句放到方法的首行,则有很多的打印结果,例如:
ForkJoinPool-1-worker-1
ForkJoinPool-1-worker-1
ForkJoinPool-1-worker-1
ForkJoinPool-1-worker-1
ForkJoinPool-1-worker-1
ForkJoinPool-1-worker-2
ForkJoinPool-1-worker-3
ForkJoinPool-1-worker-3
ForkJoinPool-1-worker-2
ForkJoinPool-1-worker-2
ForkJoinPool-1-worker-4
ForkJoinPool-1-worker-1
ForkJoinPool-1-worker-3
ForkJoinPool-1-worker-2
ForkJoinPool-1-worker-4
5
 可以看到总共有4个线程在运行(这也差不多说明了我的机器至少是4核的)。
  • 大小: 22.9 KB
分享到:
评论

相关推荐

    线程 JAVA java线程 java线程第3版 java线程第2版第3版合集

    本书的新版本展示了如何利用Java线程工具的全部优势,并介绍了JDK 2线程接口中的最新变化。你将学习如何使用线程来提高效率,如何有效地使用它们,以及如何避免常见的错误。本书讨论了死锁、竞态条件以及饥饿等问题...

    Java线程讲解Java线程讲解

    Java线程讲解Java线程讲解Java线程讲解Java线程讲解Java线程讲解Java线程讲解Java线程讲解Java线程讲解Java线程讲解Java线程讲解

    JAVA 线程类应用

    JAVA 线程类应用JAVA 线程类应用JAVA 线程类应用JAVA 线程类应用JAVA 线程类应用JAVA 线程类应用JAVA 线程类应用JAVA 线程类应用JAVA 线程类应用

    Java线程详解大全

    Java线程Java线程Java线程Java线程Java线程Java线程Java线程Java线程Java线程Java线程Java线程Java线程Java线程Java线程Java线程

    java多线程编程总结

    Java线程:概念与原理 Java线程:创建与启动 Java线程:线程栈模型与线程的变量 Java线程:线程状态的转换 Java线程:线程的同步与锁 Java线程:线程的交互 Java线程:线程的调度-休眠 Java线程:线程的调度-优先级 ...

    Java线程Java线程Java线程Java线程

    Java线程Java线程Java线程Java线程Java线程Java线程

    java 线程java 线程

    java 线程java 线程java 线程java 线程java 线程java 线程java 线程java 线程java 线程

    Java多线程编程总结

    Java线程:概念与原理 Java线程:创建与启动 Java线程:线程栈模型与线程的变量 Java线程:线程状态的转换 Java线程:线程的同步与锁 Java线程:线程的交互 Java线程:线程的调度-休眠 Java线程:线程的调度-...

    java 线程 dump 分析工具 2.3.3

    java 线程Dump 分析工具: Java的TDA线程转储分析器是一个用于分析Sun Java VM生成的线程转储和堆信息的小型Swing GUI(目前用1.4测试)。它从提供的日志文件中解析线程转储和类直方图。它提供关于发现的线程转储的...

    java多线程笔记

    Java线程:概念与原理 2 一、操作系统中线程和进程的概念 2 二、Java中的线程 3 三、Java中关于线程的名词解释 3 四、线程的状态转换和生命周期 4 Java线程:创建与启动 7 Java线程:线程名称的设定及获取 10 Java...

    Java线程模块Java线程之秒表

    Java线程模块Java线程之秒表新手学习Java线程模块时,利用Java中设置线程的暂停间隔,做的简易秒表

    java线程同步java线程同步

    java线程同步java线程同步java线程同步

    java线程简单的封装类

    用反射实现了对java线程简单的封装,类似c#线程,使用此封装类勿需再次继承Thread类或实现Runnable接口,直接绑定需要使用线程的方法即可,另支持动态传参,如觉着好用请多多支持作者 QQ 359103820 希望能为使用线程...

    java线程 线程学习资料 java线程教程

    java线程 线程 教程 java线程教程 java线程学习资料 本教程有什么内容? 本教程研究了线程的基础知识— 线程是什么、线程为什么有用以及怎么开始编写使用线程的简单 程序。 我们还将研究更复杂的、使用线程的应用...

    Java的线程和Java AppletJava的线程和Java AppletJava的线程和Java Applet

    Java的线程和Java AppletJava的线程和Java AppletJava的线程和Java AppletJava的线程和Java AppletJava的线程和Java Applet

    java线程.pdf

    java线程.pdf java 学习java

    Java线程高清晰中文第二版

    在Java出现以前,似乎人人都在谈论线程,却很少有人使用它。用线程编程是技巧性很强的且不可移植。 而在Java中却完全不同。Java的线程工具易于使用,并且像Java中的其他东西一样可以在不同的平台之间移植。这是一件...

    Java线程Java线程

    java 线程 新手java 线程 新手java 线程 新手java 线程 新手

    java线程分析工具TDA

    分析java线程日志的工具,使用jstack把java线程日志dump下来,然后上传到该工具,就可以查看线程阻塞情况等信息。

    Java 模拟线程并发

    Java 模拟线程并发 Java, 模拟线程并发,线程,并发 Java, 模拟线程并发,线程,并发 Java, 模拟线程并发,线程,并发 Java, 模拟线程并发,线程,并发

Global site tag (gtag.js) - Google Analytics