转载:http://hi.baidu.com/tianyadoudou/item/4deeb71dd6fbe7e55f53b18c
也许有人会问 “既然用了多线程,为什么还要同步?还要顺序执行呢?”。这个看似脑残的问题其实并非我们想象的那么简单。
假设(这里只是一个假设,类似下面的情形有很多,这里不一一阐述)当你执行定时任务的时候,你需要执行ScheduledExecutorService的一个scheduleAtFixedRate方法的时候,那么你需要给这个方法传入一个线程A的实例。如果这个线程A是一个大的业务,这个大业务里边分多个步骤。假设第一个步骤需要用到多线程,而且业务需求是必须执行完第一步才能执行下面的操作,那么惨了。因为多线程不等第一步运行完毕就有可能执行第二步的操作,那我们该怎么办?
我们可以把每个步骤看做是一个线程,执行完一个,再执行下一个,那么该如何让这些线程同步并且顺序地执行呢?
(在网上找了好多资料,有说用join的,但是我没弄出来。于是就自己硬搞了,希望有高手能用join解决一下,谢过!)
第一步是让线程同步,第二部是让线程有顺序。
同步:我们可以用synchronized来解决。
java线程同步原理: java会为每个object对象分配一个monitor,当某个对象的同步方法(synchronized methods )被多个线程调用时,该对象的monitor将负责处理这些访问的并发独占要求。
当一个线程调用一个对象的同步方法时,JVM会检查该对象的monitor。如果monitor没有被占用,那么这个线程就得到了monitor的占有权,可以继续执行该对象的同步方法;如果monitor被其他线程所占用,那么该线程将被挂起,直到monitor被释放。
当线程退出同步方法调用时,该线程会释放monitor,这将允许其他等待的线程获得monitor以使对同步方法的调用执行下去。就像下面这样:
public void test() {
synchronized (this) {
//做一些事
//这里只会有一个线程来调用该方法,因为只有一个this对象作为资源分配给该线程
}
}
顺序:我们可以用List来解决,因为它是有序的。我们只需要将要执行的线程放入到List中不就好解决了吗
上代码:
/**
* 让多个线程同步顺序执行的线程管理器
* @author bianrx
* @date 2012.1.18
* SynchronizedRunMultiThread
*/
public class SyncManager {
/**
* 待执行的线程集合,注意这里必须是Runnable接口类型,下面会解释
*/
private List<Runnable> runnableList;
public SyncManager(){}
public SyncManager(List<Runnable> runnableList) {
this.runnableList = runnableList;
}
public void setRunnable(List<Runnable> runnableList) {
this.runnableList = runnableList;
}
public void run() {
//遍历代执行的线程集合
for(Runnable runnable: runnableList) {
runThread(runnable);
}
}
/**
* 按顺序同步执行多个线程
* @author bianrx
* @date 2012.1.18
* @param runnable
*/
private void runThread(Runnable runnable) {
synchronized (this) {
runnable.run();//这里需要注意的是:必须调用run方法,因为如果你调用了start方法,线程只会向JVM请求资源,但是未必就执行其中的run。
//这个方法是同步的,所以当前只有一个线程占用了this对象。
}
}
}
测试代码:有两个要执行的线程
public class Thread1 extends Thread {
@Override
public void run() {
System.out.println("运行线程1");
}
}
public class Thread2 extends Thread {
@Override
public void run() {
System.out.println("运行线程2");
}
}
//主函数测试
public class MainTest {
/**
* @param args
* @throws ParseException
* @throws InterruptedException
*/
public static void main(String[] args) throws ParseException, InterruptedException {
List<Runnable> runnableList = new ArrayList<Runnable>();
runnableList.add(new Thread1());
runnableList.add(new Thread2());
while(true) {
Thread.sleep(1000);
new SyncManager(runnableList).run();
System.out.println("---------------");
}
}
在这里补充一下:我做错了。情况是这样的,我要定时执行一个任务,而我查到的java定时任务的api是这样的:
executorService.scheduleAtFixedRate(srtm, initialDelay, period, unit);
第一个参数是一个runnable接口。这个任务分两个步骤,我为了方便维护程序,就分别写到了两个线程里,实际上算不上并发。但是我的第一步里确实是要有并发的(多线程),我上面写的方法只能保证在主线程里不嵌套子线程的情况下才好用,但是如果有嵌套该怎么办呢?这些是我遇到的情况,大家不必关注。也就是说想做到在主线程中等待多线程都执行完毕了再执行后续代码该怎么办呢?
答案是用join。join方法大家可以查下api,它的意思是等待当前线程执行完后执行完毕才执行其他线程。也就是说如果一个类中有这样一个代码段:
thread1.start();
thread2.start();
thread1.join();
thread2.join();
do something 1;
do something 2;
那么这段代码会等待两个线程执行完毕后再执行 do something 1 和 do something 2,注意:必须先启动所有线程,再join。如果启动一个就join一个,结果是什么?对,那就会是等待thread1执行完再执行thread2,再执行后续代码。
贴上自己测试代码:
package com.yonyou.mulitthread;
/**
* Created by lvyufan on 14-3-8.
*/
public class JoinTest {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread() {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread 1....");
}
};
Thread t2 = new Thread() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread 2....");
}
};
// 下面表示,t1线程执行完之后,才能执行t2线程,t2,线程执行完之后才能执行main线程
// t1.start();
// t1.join();
// t2.start();
// t2.join();
// t1,t2都start之后再分别join,表示t1,t2两个线程都执行完之后,主线程main中得打印
// 语句才能执行。
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("main thread run end ...");
}
}