假如现在有两个数,B和C。如果要计算(B+C)*B/2,那么这个运算过程就是无法并行的。原因是,如果B+C没有执行完成,则永远算不出(B+C)*B,这就是数据相关性。
可以借鉴日常生产中的流水线思想,首先将计算过程拆分为三个步骤:
P1:A=B+C
P2:D=AxB
P3:D=D/2
上述步骤中P1、P2和P3均在单独的线程中计算,并且每个线程只负责自己的工作。此时,P3的计算结果就是最终需要的答案。
简单实现
载体
为了实现这个功能,我们需要定义一个在线程间携带结果进行信息交换的载体
public class Msg {
public double i;
public double j;
public String orgStr=null;
}
P1计算的加法
public class Plus implements Runnable{
public static BlockingQueue<Msg> bg=new LinkedBlockingQueue<Msg>();
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
try {
Msg msg=bg.take();
msg.j=msg.i+msg.j;
Multiply.bg.add(msg);
} catch (Exception e) {
}
}
}
}
可以看出,P1从共享管道BlockingQueue取出Msg,然后使用Msg封装的i和j进行求和操作,然后通过把结果添加到P2的共享管道BlockingQueue,传递给P2
P2计算的乘法
public class Multiply implements Runnable {
public static BlockingQueue<Msg> bg=new LinkedBlockingQueue<Msg>();
@Override
public void run() {
while(true){
try {
Msg msg=bg.take();
msg.i=msg.i*msg.j;
Div.bg.add(msg);
} catch (Exception e) {
// TODO: handle exception
}
}
}
}
P3计算的除法
public class Div implements Runnable {
public static BlockingQueue<Msg> bg = new LinkedBlockingQueue<Msg>();
@Override
public void run() {
while (true) {
try {
Msg msg=bg.take();
msg.i=msg.i/2;
System.out.println(msg.orgStr+"="+msg.i);
} catch (Exception e) {
}
}
}
}
这个可以得到最终结果,所以我们就输出最终结果,msg.orgStr是计算格式的字符串
测试
public class PStreamMain {
public static void main(String[] args) {
new Thread(new Plus()).start();
new Thread(new Multiply()).start();
new Thread(new Div()).start();
for(int i=0;i<=1000;i++){
for(int j=0;j<=1000;j++){
Msg msg=new Msg();
msg.i=i;
msg.j=j;
msg.orgStr="(("+i+"+"+j+")*"+i+")/2";
Plus.bg.add(msg);
}
}
}
}
总结
你会发现并发流水线是通过BlockingQueue这个共享管道,通过依赖关系串联起来,把操作分配在不同线程中进行计算,尽可能利用多核优势
参考:《Java高并发程序设计》