线程间同步工具
当一个任务被分解成多个任务并行执行完成时,各个任务间难免有相互依赖关系,比如,任务A为界面创建并更新的工作,任务B为网络获取数据,当任务A执行时,B可以同时去获取数据,A进行页面刷新时,可能会依赖B获取到的数据进行界面显示,这时,A需要等待B的完成,才能进行后续工作。
基于类似场景的解决方案很多,本文编写的工具类,个人感觉使用上可能稍微便捷一些。当然有得必有失,同仁们取长补短吧。
本文所写工具类命名为ConditionUtil, 基本原理很简单,主要是封装了Object.java的wait/notify机制。ConditionUtil内部会全局保存需要等待的condition在字典中, condition可以用字符串索引,condition被解除等待后,会从字典中移除。使用上的简便之处在于,等待和解除等待的操作都是针对某一个字符串的操作,也就是以一个字串唯一命名一个等待条件,对于解耦有一定的帮助。
举例如下:
在某个时间点对condition进行创建:
1.
ConditionUtil.getInstance().createCondition(“condition_util_test”);
//首先创建一个等待条件,针对某一个condition, 创建操作要先于等待操作,否则不会对该 condition进行等待
2.
A任务:
ConditionUtil.getInstance().wait(“condition_util_test”);
//对condition进行等待,该A任务线程被block, 等待B任务对该condition进行signal解锁
或者使用:ConditionUtil.getInstance().wait(“condition_util_test”, WAIT_TIMEOUT)
3.
B任务:
ConditionUtil.getInstance().signal(“condition_util_test”);
//B任务完成后,调用signal对该condition解锁,A任务线程继续执行
4.
在某个时间点对condition进行移除:
ConditionUtil.getInstance().removeCondition(“condition_util_test”);
总结:
1.signal操作先于wait操作之前被触发
如上所述,对condition要先进行创建,signal和removeCondition操作均表示此次与该condition相关的操作均满足解锁条件,不需要再等待. signal先于wait触发,wait操作不会再block线程,线程直接继续执行.
2.signal操作一直没有被触发
采用timeout机制,timeout后,线程继续执行,如果设置了timeout回调,该回调函数被
调用.
3.ConditionUtil中保存Condition的存储释放
import java.util.HashMap;
public class ConditionUtil {
private static ConditionUtil self;
private HashMap<String, ConditionInUse> map;
private interface ConditionTimeoutCallback {
void onTimeout();
}
private ConditionUtil() {
map = new HashMap<>();
}
public synchronized static ConditionUtil getInstance() {
if (self == null) {
self = new ConditionUtil();
}
return self;
}
private class ConditionInUse {
private volatile boolean mCondition;
private ConditionTimeoutCallback timeoutCallback;
private int waitCount = 0;
public ConditionInUse() {
mCondition = false;
}
public int getWaitCount() {
return waitCount;
}
private void open() {
synchronized (this) {
boolean old = mCondition;
mCondition = true;
if (!old) {
this.notifyAll();
}
}
}
private void close() {
synchronized (this) {
mCondition = false;
}
}
public void block() {
waitCount++;
synchronized (this) {
while (!mCondition) {
try {
this.wait();
}
catch (InterruptedException e) {
waitCount--;
}
}
}
waitCount--;
}
public boolean block(long timeout) {
waitCount++;
if (timeout > 0) {
synchronized (this) {
long now = System.currentTimeMillis();
long end = now + timeout;
while (!mCondition && now < end) {
try {
this.wait(end-now);
}
catch (InterruptedException e) {
waitCount--;
}
now = System.currentTimeMillis();
}
if (!mCondition && end < now && timeoutCallback != null) {
timeoutCallback.onTimeout();
}
waitCount--;
return mCondition;
}
} else {
this.block();
waitCount--;
return true;
}
}
public void setTimeoutCallback(ConditionTimeoutCallback callback) {
timeoutCallback = callback;
}
}
public void createCondition(String str) {
if (str == null) {
return;
}
synchronized (map) {
ConditionInUse condition = map.get(str);
if (condition == null) {
condition = new ConditionInUse();
map.put(str, condition);
}
}
}
public void removeCondition(String str) {
if (str == null) {
return;
}
synchronized (map) {
ConditionInUse condition = map.get(str);
if (condition != null) {
condition.close();
condition.open();
map.remove(str);
}
}
}
public void wait(final String str, long time) {
if (str == null) {
return;
}
final ConditionInUse condition = map.get(str);
if (condition != null) {
condition.setTimeoutCallback(new ConditionTimeoutCallback() {
@Override
public void onTimeout() {
//do something here to handle timeout
if (condition.getWaitCount() == 0) {
//map.remove(str); 取消移除操作, 使用removeCondition进行手动移除
}
}
});
condition.block(time);
}
}
public void wait(final String str) {
if (str == null) {
return;
}
final ConditionInUse condition = map.get(str);
if (condition != null) {
condition.block();
}
}
public void signal(final String str) {
removeCondition(str);
}
}