• 知道java可以使用java.util.concurrent包下的
CountDownLatch
ExecutorService
Future

Callable
实现并行编程,并在并行线程同步时,用起来十分简单的一种 。
实现原理:
1、CountDownLatch 统计并行线程完成数,并提供了await()方法,实现等待所有并行线程完成,或者指定最大等待时间。
2、ExecutorService提供了execute(Callable)执行线程方法,还提供了submit(Callable)提交线程。
3、Future接受实现Callable<V>接口的(可执行线程)返回值,接受Executors.submit(Callable<V>)返回值。而且Future<V>提供get()取回并行子线程返回的参数,还可以给get指定过期时间。

想到Concurrent,就能想到c#中,命名空间System.Collection,Concurrent,在该命名空间下提供了一些线程安全的集合类。

  • 代码示例:

MyTaskResult.java

package com.dx.testparallel;

public class MyTaskResult {
private String name; public MyTaskResult(String name){
this.name=name;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
}
}

TaskItem.java

package com.dx.testparallel;

public class TaskItem {
private int id;
private String name; public TaskItem(int id,String name){
this.id=id;
this.name=name;
} public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
} public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

MyTask.java

package com.dx.testparallel;

import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch; public class MyTask implements Callable<MyTaskResult> {
private final TaskItem taskItem;
private final CountDownLatch threadsSignal; public MyTask(CountDownLatch threadsSignal,TaskItem taskItem) {
this.threadsSignal= threadsSignal;
this.taskItem=taskItem;
} @Override
public MyTaskResult call() throws Exception {
MyTaskResult result=new MyTaskResult(this.taskItem.getName()); // 核心处理逻辑处理
Thread.sleep(2000); System.out.println("task id:" + taskItem.getId() +" >>>>等待結束");
System.out.println("task id:" + taskItem.getId() + " >>>>线程名称:" + Thread.currentThread().getName() + "结束. 还有" + threadsSignal.getCount() + " 个线程"); // 必须等核心处理逻辑处理完成后才可以减1
this.threadsSignal.countDown(); return result;
} }

Main.java

package com.dx.testparallel;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future; public class Main {
public static void main(String[] args) throws InterruptedException {
List<TaskItem> taskItems=new ArrayList<TaskItem>();
for(int i=0;i<20;i++){
taskItems.add(new TaskItem(i, "task "+i));
} CountDownLatch threadsSignal = new CountDownLatch(taskItems.size());
ExecutorService executor = Executors.newFixedThreadPool(taskItems.size());
List<Future<MyTaskResult>> resultLazyItems=new ArrayList<Future<MyTaskResult>>();
System.out.println("主線程開始進入並行任務提交");
for (TaskItem taskItem : taskItems) {
// 使用future存储子线程执行后返回结果,必须在所有子线程都完成后才可以使用get();
// 如果在这里使用get(),会造成等待同步。
Future<MyTaskResult> future = executor.submit(new MyTask(threadsSignal,taskItem));
resultLazyItems.add(future);
}
System.out.println("主線程開始走出並行任務提交");
System.out.println("主線程進入等待階段(等待所有并行子线程任务完成)。。。。。");
// 等待所有并行子线程任务完成。
threadsSignal.await();
// 并不是终止线程的运行,而是禁止在这个Executor中添加新的任务
executor.shutdown();
System.out.println("主線程走出等待階段(等待所有并行子线程任务完成)。。。。。"); for(Future<MyTaskResult> future :resultLazyItems){
try {
MyTaskResult result = future.get();
System.out.println(result.getName());
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} }

运行结果:

主線程開始進入並行任務提交
主線程開始走出並行任務提交
主線程進入等待階段(等待所有并行子线程任务完成)。。。。。
task id:0 >>>>等待結束
task id:6 >>>>等待結束
task id:8 >>>>等待結束
task id:3 >>>>等待結束
task id:4 >>>>等待結束
task id:2 >>>>等待結束
task id:2 >>>>线程名称:pool-1-thread-3结束. 还有20 个线程
task id:5 >>>>等待結束
task id:1 >>>>等待結束
task id:7 >>>>等待結束
task id:1 >>>>线程名称:pool-1-thread-2结束. 还有19 个线程
task id:5 >>>>线程名称:pool-1-thread-6结束. 还有19 个线程
task id:4 >>>>线程名称:pool-1-thread-5结束. 还有20 个线程
task id:19 >>>>等待結束
task id:10 >>>>等待結束
task id:18 >>>>等待結束
task id:18 >>>>线程名称:pool-1-thread-19结束. 还有16 个线程
task id:3 >>>>线程名称:pool-1-thread-4结束. 还有20 个线程
task id:8 >>>>线程名称:pool-1-thread-9结束. 还有20 个线程
task id:6 >>>>线程名称:pool-1-thread-7结束. 还有20 个线程
task id:0 >>>>线程名称:pool-1-thread-1结束. 还有20 个线程
task id:10 >>>>线程名称:pool-1-thread-11结束. 还有16 个线程
task id:19 >>>>线程名称:pool-1-thread-20结束. 还有16 个线程
task id:12 >>>>等待結束
task id:17 >>>>等待結束
task id:17 >>>>线程名称:pool-1-thread-18结束. 还有9 个线程
task id:11 >>>>等待結束
task id:11 >>>>线程名称:pool-1-thread-12结束. 还有8 个线程
task id:14 >>>>等待結束
task id:14 >>>>线程名称:pool-1-thread-15结束. 还有7 个线程
task id:16 >>>>等待結束
task id:16 >>>>线程名称:pool-1-thread-17结束. 还有6 个线程
task id:15 >>>>等待結束
task id:9 >>>>等待結束
task id:13 >>>>等待結束
task id:7 >>>>线程名称:pool-1-thread-8结束. 还有19 个线程
task id:13 >>>>线程名称:pool-1-thread-14结束. 还有5 个线程
task id:9 >>>>线程名称:pool-1-thread-10结束. 还有5 个线程
task id:15 >>>>线程名称:pool-1-thread-16结束. 还有5 个线程
task id:12 >>>>线程名称:pool-1-thread-13结束. 还有9 个线程
主線程走出等待階段(等待所有并行子线程任务完成)。。。。。
task 0
task 1
task 2
task 3
task 4
task 5
task 6
task 7
task 8
task 9
task 10
task 11
task 12
task 13
task 14
task 15
task 16
task 17
task 18
task 19

  注释以下代码:

    // Thread.sleep(2000);
// System.out.println("task id:" + taskItem.getId() +" >>>>等待結束");

之后运行结果:

主線程開始進入並行任務提交
task id:1 >>>>线程名称:pool-1-thread-2结束. 还有20 个线程
task id:2 >>>>线程名称:pool-1-thread-3结束. 还有20 个线程
task id:3 >>>>线程名称:pool-1-thread-4结束. 还有20 个线程
task id:4 >>>>线程名称:pool-1-thread-5结束. 还有19 个线程
task id:5 >>>>线程名称:pool-1-thread-6结束. 还有19 个线程
task id:8 >>>>线程名称:pool-1-thread-9结束. 还有15 个线程
task id:0 >>>>线程名称:pool-1-thread-1结束. 还有14 个线程
task id:9 >>>>线程名称:pool-1-thread-10结束. 还有13 个线程
task id:7 >>>>线程名称:pool-1-thread-8结束. 还有12 个线程
task id:6 >>>>线程名称:pool-1-thread-7结束. 还有14 个线程
task id:10 >>>>线程名称:pool-1-thread-11结束. 还有10 个线程
task id:11 >>>>线程名称:pool-1-thread-12结束. 还有9 个线程
task id:12 >>>>线程名称:pool-1-thread-13结束. 还有8 个线程
task id:13 >>>>线程名称:pool-1-thread-14结束. 还有7 个线程
task id:14 >>>>线程名称:pool-1-thread-15结束. 还有6 个线程
task id:15 >>>>线程名称:pool-1-thread-16结束. 还有5 个线程
task id:16 >>>>线程名称:pool-1-thread-17结束. 还有4 个线程
task id:17 >>>>线程名称:pool-1-thread-18结束. 还有3 个线程
task id:18 >>>>线程名称:pool-1-thread-19结束. 还有2 个线程
主線程開始走出並行任務提交
主線程進入等待階段(等待所有并行子线程任务完成)。。。。。
task id:19 >>>>线程名称:pool-1-thread-20结束. 还有1 个线程
主線程走出等待階段(等待所有并行子线程任务完成)。。。。。
task 0
task 1
task 2
task 3
task 4
task 5
task 6
task 7
task 8
task 9
task 10
task 11
task 12
task 13
task 14
task 15
task 16
task 17
task 18
task 19
  • 项目中应用:

定义可执行线程类:

public class UploadFileToTask implements Callable<UploadFileToTaskResult> {
private final Task_UploadFileToTaskItem taskItem;
private final Log log = LogHelper.getInstance(ImportMain.class);
private final CountDownLatch threadsSignal;
private final HDFSUtil hdfsUtil = new HDFSUtil();
private final static String HADOOP_HDFS_PATH = HdfsConfiguration.getHdfsUrl(); public UploadFileToTask(CountDownLatch threadsSignal ,Task_UploadFileToTaskItem taskItem){
this.taskItem=taskItem;
this.threadsSignal=threadsSignal;
} @Override
public UploadFileToTaskResult call() throws Exception {
String area = taskItem.getArea();
String fileGenerateDate = taskItem.getFileGenerateDate();
String manufacturer = taskItem.getManufacturer();
String eNodeBId = taskItem.geteNodeBId();
String filePath = taskItem.getFilePath();
FileType fileType = taskItem.getFileType(); TaskStatus taskStatus= TaskStatus.Success; // 不确定该FileSystem是否是线程安全的,故在每一个thread初始化一次。
Configuration conf = new Configuration();
Path dstPath = new Path(HADOOP_HDFS_PATH);
FileSystem hdfs = dstPath.getFileSystem(conf); // 核心代码。。。
// 上传MR文件
// 上传Signal文件 // 如果文件路径不为空,就开始上传文件到hdfs
if(uploadFilePath.length()>0){
if (!hdfsUtil.uploadFileToHdfs(hdfs, filePath, uploadFilePath)) {
taskStatus= TaskStatus.Fail;
}
} TaskGroupInfo taskGroupInfo = new TaskGroupInfo();
taskGroupInfo.setArea(area);
taskGroupInfo.setManufacturer(manufacturer);
taskGroupInfo.setFileGenerateDate(fileGenerateDate);
taskGroupInfo.setFileType(fileType); String key = String.format("%s,%s,%s,%s", taskGroupInfo.getArea(), taskGroupInfo.getManufacturer(), taskGroupInfo.getFileGenerateDate(), String.valueOf(taskGroupInfo.getFileType().getValue())); UploadFileToTaskResult result=new UploadFileToTaskResult(); // 填充返回值
result.setStatus(taskStatus);
result.setTaskGroupInfo(taskGroupInfo);
result.setTaskGroupkey(key);
result.setTaskOID(taskItem.getOid()); System.out.println("task id:" + taskItem.getOid() + " >>>>线程名称:" + Thread.currentThread().getName() + "结束. 还有" + threadsSignal.getCount() + " 个线程"); // 必须等核心处理逻辑处理完成后才可以减1
this.threadsSignal.countDown(); return result;
}
}

实现并行线程同步核心代码:

            // 获取当前节点带执行任务
ArrayList<Task_UploadFileToTaskItem> taskItems = uploadFileToTaskItemDao.getTopNTodoTaskItems(this.computeNode.getId(),Configuration.getTaskCount());
// 批量修改任务状态为正在处理状态(doing)。
log.info("Start:>>>>>>batch modify task status(doing)>>>>>>");
log.info("Over:>>>>>>batch modify task status(doing)>>>>>>"); // 批量处理上传任务(上传文件到)
log.info("Start:>>>>>>each process task(upload to)>>>>>>");
CountDownLatch threadsSignal = new CountDownLatch(taskItems.size());
ExecutorService executor = Executors.newFixedThreadPool(taskItems.size());
List<Future<UploadFileToTaskResult>> resultLazyItems=new ArrayList<Future<UploadFileToTaskResult>>();
for (Task_UploadFileToTaskItem taskItem : taskItems) {
// 使用future存储子线程执行后返回结果,必须在所有子线程都完成后才可以使用get();
// 如果在这里使用get(),会造成等待同步。
Future<UploadFileToTaskResult> future = executor.submit(new UploadFileToTask(threadsSignal,taskItem));
resultLazyItems.add(future);
}
// 等待所有并行子线程任务完成。
threadsSignal.await();
executor.shutdown();//并不是终止线程的运行,而是禁止在这个Executor中添加新的任务 
log.info("Over:>>>>>>each process task(upload to)>>>>>>"); // 批量修改任务处理状态
Map<String, TaskGroupInfo> taskGroupItems=new HashMap<String, TaskGroupInfo>();
Map<Integer, TaskStatus> successTaskItems = new HashMap<Integer, TaskStatus>();
Map<Integer, TaskStatus> failTaskItems = new HashMap<Integer, TaskStatus>(); for(Future<UploadFileToTaskResult> future :resultLazyItems){
UploadFileToTaskResult result= future.get();
if(!taskGroupItems.containsKey(result.getTaskGroupkey())){
taskGroupItems.put(result.getTaskGroupkey(),result.getTaskGroupInfo());
}
if(result.getStatus()== TaskStatus.Success){
successTaskItems.put(result.getTaskOID(),result.getStatus());
}else{
failTaskItems.put(result.getTaskOID(),result.getStatus());
}
}
  • 参考资料:

http://blog.csdn.net/wangmuming/article/details/19832865

http://www.importnew.com/21312.html

Java:并行编程及同步使用方法的更多相关文章

  1. Java并发编程:同步容器

    Java并发编程:同步容器 为了方便编写出线程安全的程序,Java里面提供了一些线程安全类和并发工具,比如:同步容器.并发容器.阻塞队列.Synchronizer(比如CountDownLatch). ...

  2. C#并行编程-线程同步原语

    菜鸟学习并行编程,参考<C#并行编程高级教程.PDF>,如有错误,欢迎指正. 目录 C#并行编程-相关概念 C#并行编程-Parallel C#并行编程-Task C#并行编程-并发集合 ...

  3. 【转】Java并发编程:同步容器

    为了方便编写出线程安全的程序,Java里面提供了一些线程安全类和并发工具,比如:同步容器.并发容器.阻塞队列.Synchronizer(比如CountDownLatch).今天我们就来讨论下同步容器. ...

  4. 转载 三、并行编程 - Task同步机制。TreadLocal类、Lock、Interlocked、Synchronization、ConcurrentQueue以及Barrier等

    随笔 - 353, 文章 - 1, 评论 - 5, 引用 - 0 三.并行编程 - Task同步机制.TreadLocal类.Lock.Interlocked.Synchronization.Conc ...

  5. 三、并行编程 - Task同步机制。TreadLocal类、Lock、Interlocked、Synchronization、ConcurrentQueue以及Barrier等

    在并行计算中,不可避免的会碰到多个任务共享变量,实例,集合.虽然task自带了两个方法:task.ContinueWith()和Task.Factory.ContinueWhenAll()来实现任务串 ...

  6. 8、Java并发编程:同步容器

    Java并发编程:同步容器 为了方便编写出线程安全的程序,Java里面提供了一些线程安全类和并发工具,比如:同步容器.并发容器.阻塞队列.Synchronizer(比如CountDownLatch). ...

  7. Java 并行编程!

    多核处理器现在已广泛应用于服务器.台式机和便携机硬件.它们还扩展到到更小的设备,如智能电话和平板电脑.由于进程的线程可以在多个内核上并行执行,因此多核处理器为并发编程打开了一扇扇新的大门.为实现应用程 ...

  8. Java并发编程之同步

    1.synchronized 关键字 synchronized 锁什么?锁对象. 可能锁对象包括: this, 临界资源对象,Class 类对象. 1.1 同步方法 synchronized T me ...

  9. Java并发编程基础--基本线程方法详解

    什么是线程 线程是操作系统调度的最小单位,一个进程中可以有多个线程,这些线程可以各自的计数器,栈,局部变量,并且能够访问共享的内存变量.多线程的优势是可以提高响应时间和吞吐量. 使用多线程 一个进程正 ...

随机推荐

  1. 【BZOJ】2237: [NCPC2009]Flight Planning

    题意 \(n(1 \le n \le 2500)\)个点的树,求删掉一条边再加上一条边使得还是一棵树,且任意两点最大距离最小. 分析 考虑枚举删掉每一条边,我们只需要考虑如何加边容易求得新树的最大距离 ...

  2. 【bzoj2631】tree link-cut-tree

    2016-06-01 08:50:36 题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2631 注意加和乘的标记下传问题. 还有就是split后 ...

  3. C语言(3)

    C语言(3)----数据输入 输入时的关键字为scanf.如我们要从键盘上输入一个数,放在变量a里面,则可以写成scanf("%d",&a); "&&qu ...

  4. PHP 进行数据庫对比工具

    <?php /** * author jackluo * net.webjoy@gmail.com */ class IMysqlDiff { private $master,$slave; p ...

  5. 获取系统开机的时间(Windows、Linux)

    获取系统启动的时间.Windows系统和Linux系统 1.Windows系统 1)代码如下 #include <stdio.h> #include <time.h> #inc ...

  6. oracle免安装客户端设置

    对oracle不是很熟悉,就是使用层面的,开发时往往需要连接oracle,又不想单独安装,一般都用个免安装的客户端罢了,再次记录一下自用 1.下载oracle免安装的客户端 下载地址:http://w ...

  7. [LintCode] Scramble String 爬行字符串

    Given a string s1, we may represent it as a binary tree by partitioning it to two non-empty substrin ...

  8. Python 3.x下消除print()自动换行

    Python 2.x下的print语句在输出字符串之后会默认换行,如果不希望换行,只要在语句最后加一个“,”即可.但是在Python 3.x下,print()变成内置函数,加“,”的老方法就行不通了. ...

  9. 关闭CentOS不必要的开机启动项

    命令行: for i in `chkconfig --list |grep 3:on|awk '{print $1}' |grep -Ev "network|sshd|sysstat|ude ...

  10. 在Apache中使用mod_rewrite模块重写URL

    如果有使用第三方框架做项目时,url路径是可以同过框架给的方法来设定的(如thinkphp),但如果使用原生php写的项目又想重写url,则可通过apache的一些设置来达到想要的效果. 在更改apa ...