开发中遇到的问题:看到项目先前实现的代码,有一个逻辑处理,要完成两个界面的切换,而且这个两个界面的切换会共同设置系统的同一个资源的状态,且设置状态的操作(系统中间件已经做了互斥)比较耗时。先前的代码采用了新建线程来设置资源状态来保证主线程不被阻塞。这就出现了一个问题,如果频繁操作切换的话,就会出现系统资源的状态错乱。
应用调用:
Thread call state(true);
Thread call state(false);
Thread call state(true);
Thread call state(false);
Thread call state(true);
...
Thread call state(true);
实际执行结果:
Thread run state(true);
Thread run state(false);
Thread run state(true);
Thread run state(true);
Thread run state(false);
...
Thread run state(false); or Thread run state(true);
执行结果可以看出,在频繁调用的时候就出现与调用顺序不对应的问题,调用结束的结果可能会是错误的。
究其原因是因为只考虑了耗时操作采用线程来处理,没有对线程进行管理,造成一个操作结束后,其他等待的线程抢占运行,结果肯定会出现不对应调用的情况。
解决方法:
对于状态这种设置的处理,优先要保证状态的正确性,如果在执行true的时候,又调用设置false,再调用设置true。最终结果会是true。那么在执行第一次true的时候,来了两个操作,可以选择丢弃中间设置false的操作,来提高响应效率和保证结果正确。
另外线程的组织管理,没有比线程池好的。
频繁调用帮助类:
/**
* 如果你遇到这种场景:应用需要频繁的调用两个互斥的耗时操作,而且要保证效率与结果正确的话,这个帮助类对于你可能会有用。
* Created by leonhover on 13-12-11.
*/
public class FrequencyTaskHelper {
/**
* 线程池
*/
private ThreadPoolExecutor threadPool = null;
/**
* 任务队列,大小为1
*/
private ArrayBlockingQueue queue = new ArrayBlockingQueue<Runnable>(1);
public FrequencyTaskHelper() {
//创建线程池,只有一个线程在运行,采用DiscardOldestPolicy机制(采用其他也可以,因为用不到,我们手动清空任务队列)
threadPool = new ThreadPoolExecutor(1, 1, 50, TimeUnit.SECONDS,
queue,
new ThreadPoolExecutor.DiscardOldestPolicy());
}
/**
* 在线程池中执行操作,不一定被执行
*
* @param runnable 操作任务
*/
public void execute(Runnable runnable) {
//清空任务队列
queue.clear();
//在线程池中执行,如果没有任务运行则执行,要不就排队,排队后又可能被丢弃。
threadPool.execute(runnable);
}
}
测试类
/**
* 测试任务,任务延迟有20毫秒
*/
public static class TestOption implements Runnable {
private String name = null;
public TestOption(String str) {
name = str;
}
@Override
public void run() {
System.out.println("Task " + name + " start ");
System.out.println("Task " + name + " executed!");
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Task " + name + " end ");
}
}
//程序入口
public static final void main(String[] args) {
System.out.println("Start");
FrequencyTaskHelper helper = new FrequencyTaskHelper();
for (int i = 0; i < 100000; i++) {
TestOption opt = new TestOption("opt" + i);
helper.execute(opt);
}
}