`
liugang594
  • 浏览: 987539 次
  • 性别: 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 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具类 java 线程工具类java 线程工具...

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

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

    java 线程相关工具类

    java 线程相关工具类.java 线程相关工具类.java 线程相关工具类.java 线程相关工具类.java 线程相关工具类.java 线程相关工具类.java 线程相关工具类.java 线程相关工具类.java 线程相关工具类.java 线程相关工具类....

    java线程.pdf

    根据提供的信息,我们可以推断出这份文档主要关注的是Java线程的相关内容。下面将围绕“Java线程”这一主题展开详细的介绍与解释。 ### Java线程基础 在Java语言中,线程是程序执行流的基本单元。一个标准的Java...

    JAVA 线程类应用

    Java线程类应用是Java编程中的重要组成部分,它关乎到多任务处理和程序并发执行的能力。在Java中,线程是程序执行的最小单位,它允许一个程序中有多个执行流同时进行,使得程序能更高效地利用系统资源,特别是在处理...

    java多线程Demo

    Java线程有10个优先级(MIN_PRIORITY, NORM_PRIORITY, MAX_PRIORITY),默认优先级是NORM_PRIORITY。但是,线程优先级并不保证绝对的执行顺序,操作系统调度策略可能影响实际执行顺序。 7. join()方法: 一个线程...

    Java线程详解大全

    Java线程是并发编程的核心部分,它允许程序在同一时间执行多个独立的任务,从而提高系统效率和响应速度。本文将深入探讨Java线程的概念、生命周期、实现方式以及相关的同步机制。 首先,理解线程的基本概念至关重要...

    java 线程 dump 分析工具 2.3.3

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

    Java线程使用教程

    通过学习这个Java线程使用教程,你将能够熟练地在多线程环境中编写高效、安全的Java程序,理解线程同步、通信、线程池以及并发工具类的使用。阅读提供的"Java线程.pdf"和"说明.txt"文件将帮助你更深入地掌握这些知识...

    Java线程.ppt

    Java线程是Java编程中的重要概念,特别是在多核处理器和并发处理中不可或缺。Java线程允许程序在同一时间执行多个不同的任务,从而提高了程序的效率和响应性。在燕山大学信息学院计算机系的课程中,李峰教授讲解了...

    java线程实例 各种小Demo

    Java线程是多任务编程的重要概念,它允许程序同时执行多个独立的任务,从而提高系统效率和响应速度。在Java中,线程可以分为用户线程和守护线程,前者是程序运行的基础,而后者是在所有用户线程结束时才终止的后台...

    Java线程(第三版)

    线程优先级和守护线程也是Java线程中的一部分,`Thread`类提供了设置优先级的方法,高优先级的线程更有可能获得CPU执行时间。守护线程是一种特殊的线程,它不会阻止JVM的退出,除非所有的非守护线程都已结束。 书中...

    java线程深入解析

    创建Java线程有两种方式:继承`Thread`类并重写`run()`方法,或者实现`Runnable`接口并提供`run()`方法。当线程对象被创建并调用`start()`方法后,线程进入可运行态,由Java的线程调度器决定何时执行`run()`方法。 ...

    java线程文档大全

    2. **线程创建**:Java提供两种方式创建线程,一是直接继承Thread类并重写run()方法,二是实现Runnable接口并定义run()方法,然后通过Thread类的构造函数将Runnable对象传递进去。 3. **线程交互**:线程之间的通信...

    java线程.rar

    Java线程是Java编程语言中的一个核心概念,它允许程序同时执行多个任务,极大地提高了程序的效率和响应性。在Java中,线程是通过类`Thread`或实现`Runnable`接口来创建和管理的。Java线程模型是基于操作系统的原生...

    JAVA线程dump的分析

    JAVA线程dump的分析 JAVA线程dump是指在JAVA程序中,当前线程的状态和调用堆栈的快照,能够帮助开发者了解当前程序的执行情况,诊断问题和性能瓶颈。生成JAVA线程dump的方法在不同的操作系统下是不同的,在Windows...

    简单的java线程demo

    Java线程是多任务编程的重要概念,特别是在大型的、复杂的软件系统中,它允许程序同时执行多个不同的任务,提升程序的并发性和效率。本示例"简单的Java线程demo"旨在帮助初学者理解如何在Java中创建和管理线程。 在...

    Java线程高清晰中文第二版

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

    Java多线程知识点总结

    随着Java版本的更新,Java也引入了新的并发工具类,例如java.util.concurrent包下的并发集合、原子操作类以及各种并发控制类,这些工具类提供了比传统synchronized和volatile关键字更强大、更灵活的线程同步控制机制...

    Java线程培训资料

    #### 二、Java线程API 1. **线程控制方法** - `Thread.sleep(long millis)`: 让当前正在执行的线程暂停执行指定时间,让出CPU给其他线程使用。 - `Thread.yield()`: 提示当前线程让出CPU时间片,但不一定立即生效...

Global site tag (gtag.js) - Google Analytics