本章重点介绍的是基于 Java并行程序开发以及优化的方法,对于多核的 CPU,传统的串行程序已经很好的发回了 CPU性能,此时如果想进一步提高程序的性能,就应该使用多线程并行的方式挖掘 CPU的潜能。本章知识点:
常用的多线程设计模式,比如 Future模式,Master-Worker 模式,Guarded Suspeionsion模式、不变模式、生产者消费者模式等等。
JDK 内置的多线程框架和各种线程池技术;
JDK 内置的并发数据结构;
JDK 并发控制方式,比如内部锁,重入锁,读写锁, ThreadLocal变量,信号量等等
有关锁的一些优化方法;
使用无锁的方式提高程序性能;
使用轻量级的协称获得更高的并行度。
1. 并行程序的设计模式
并行程序的设计模式也是并行程序优化的一部分,他是对一些常用的多线程结构的总结和抽象。和串行程序相比的话,并行程序的结构更为复杂,因此合理的好似用并行模式在多线程的开发中是有助于程序的优化。经常使用的是 Future模式, Master-Worker模式, Guarded Suspeionsion模式,不变模式,生产者 -消费者模式。
1.1Future 模式
有点类似的商品的订单,网上下订单,之后交给商家来进行处理,我们只是等待结果,在这个等待的过程中,我们可以处理其他的事物。类比到程序中,我们在程序中提交了一个请求的话,可能是互联网请求获得其他 HTTP Web等等,传统的单线程环境下面,调用的函数是同步的,也就是说必须等到结果返回之后,才能够进行其他的处理。在 Future模式中,采用的是异步的方式,返回主调函数中,如果可以继续处理其他事物的话,就会继续处理其他的事物。这样的话,就不会出现所谓的等待时间,提高系统的响应速度。
Future 模式的主要参与者:
Main 系统启动,调用 Client发出请求;
Client 返回Data对象,立即返回 FutureData,并且开启ClientThread 线程装配 RealData;
Data 返回数据的接口;
FutureData Future 数据,构造很快,但是是一个虚拟的数据,需要装配 Real数据;
RealData 真实数据,其构造过程比较耗时;
TestMain.java
public class TestMain {
/*
* main 函数的实现 主要负责是调用Client吗,发起请求,使用返回的数据
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Client client = new Client();
Data data = client.request( "name");
System. out.println("request finished" );
try {
Thread. sleep(2000);//期间可以处理其他的事务
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System. out.println("request data:" + data.getResult());
}
}
Client.java
public class Client {
public Data request( final String queryStr){
final FutureData future = new FutureData();
new Thread(){
public void run(){
RealData realData = new RealData(queryStr);
future.setRealData(realData);
}
}.start();
return future;
}
}
Data.java
public interface Data {
public String getResult();
}
FutureData.java
public class FutureData implements Data{
protected RealData realData = null;
protected boolean isReady = false;
public synchronized void setRealData(RealData realData){
if(isReady ){
return;
}
this.realData = realData;
this.isReady = true;
this.notifyAll();
}
@Override
public synchronized String getResult() {
// TODO Auto-generated method stub
while(!isReady ){
try{
this.wait();
} catch(InterruptedException ex){
ex.printStackTrace();
}
}
return realData .result;
}
}
RealData.java
public class RealData implements Data{
protected final String result ;
public RealData(String queryStr) {
// TODO Auto-generated constructor stub
StringBuffer sb = new StringBuffer();
for(int i = 0; i< 10; i++){
sb.append(i);
try {
Thread. sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
result = sb.toString();
}
@Override
public String getResult() {
// TODO Auto-generated method stub
return this .result ;
}
}
以上就是整个的 Future模式
JDK 中对于Future是有内部实现的 ,在JDK 中已经内置了一种 future模式实现。 JDK的实现是相当的复杂的,并且提供了更为丰富的多线程控制功能。其中的模块包括:
Runnable Future RunnableFuture FutureTask Sync Callable
最为重要的模块式 FutureTask类,他实现了 Runnable接口,作为单独的线程运行。其中 rn方法中通过 Sync内部类,调用 Callable接口返回对象。当使用 FutureTask.get方法的时候,将返回 Callable接口的返回对象。
Callable 接口是一个用户自己定义的实现,在应用程序中实现 Callable中的 call方法,指定 FutureTask中实际的工作内容和返回对象。
public class TestMain {
public static void main(String[] args) throws InterruptedException, ExecutionException {
// TODO Auto-generated method stub
FutureTask<String> future = new FutureTask<String>(new RealData("name"));
ExecutorService executor = Executors. newFixedThreadPool(1);
executor.submit(future);
System. out.println("request finished" );
try {
Thread. sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System. out.println("Data = " + future.get());
}
}
public class RealData implements Callable<String>{
protected final String para ;
public RealData(String queryStr) {
// TODO Auto-generated constructor stub
this.para = queryStr;
}
@Override
public String call() throws Exception {
// TODO Auto-generated method stub
StringBuffer sb = new StringBuffer();
for(int i = 0; i< 10; i++){
sb.append(i);
try {
Thread. sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return sb.toString();
}
}
这里使用了线程池技术,从线程翅中获取一个线程,使用他,执行 Callable中的 call函数,返回结果。
其中ExecutorService executor = Executors.newFixedThreadPool(int);建立一个线程池,初始化线程,然后将任务提交到线程池中使用的是 exector.submit(future);其中future 是实现了Callable的 call接口,并且具有指定的返回值类型,使用 future.get()获得返回值。
还就是 FutureTask<ReturnType> future = new FutureTask<ReturnType> (new CallableClass());
当然是可以将 future传递给 Thread的,使用 thread执行 call中的内容,初始化 Callable和 FutureTask对象,创建一个线程将 futuretask作为线程构造函数的参数,然后是启动刚刚创建的新的线程
public class FutureTaskDemo {
public static void main(String[] args) {
// 初始化一个Callable对象和 FutureTask对象
Callable pAccount = new PrivateAccount();
FutureTask futureTask = new FutureTask(pAccount);
// 使用futureTask 创建一个线程
Thread pAccountThread = new Thread(futureTask);
System. out.println("futureTask 线程现在开始启动,启动时间为: " + System.nanoTime());
pAccountThread.start();
System. out.println(" 主线程开始执行其他任务 ");
// 从其他账户获取总金额
int totalMoney = new Random().nextInt(100000);
System. out.println(" 现在你在其他账户中的总金额为 " + totalMoney);
System. out.println(" 等待私有账户总金额统计完毕 ...");
// 测试后台的计算线程是否完成,如果未完成则等待
while (!futureTask.isDone()) {
try {
Thread. sleep(500);
System. out.println(" 私有账户计算未完成继续等待 ...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System. out.println("futureTask 线程计算完毕,此时时间为 " + System.nanoTime());
Integer privateAccountMoney = null;
try {
privateAccountMoney = (Integer) futureTask.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System. out.println(" 您现在的总金额为: " + totalMoney + privateAccountMoney.intValue());
}
}
@SuppressWarnings("all" )
class PrivateAccount implements Callable {
Integer totalMoney;
@Override
public Object call() throws Exception {
Thread. sleep(5000);
totalMoney = new Integer(new Random().nextInt(10000));
System. out.println(" 您当前有 " + totalMoney + " 在您的私有账户中 ");
return totalMoney ;
}
}
TengfeiYang
于广州中山大学图书馆
20131114