励精图治---Concurrency---GUI设计
2015-09-21 17:46
381 查看
为什么只是点击一个button, 可以看到按键变化很快? 添加了按键事件listener,并在里头dosomething的时候有时会有无响应的感觉?
因为特么GUI工具包都被实现为单线程.---------->因为单线程能保证时间有序进行.多线程不稳定的多,开发难度大.现在都是单线程了.
此文不必较真,有个别类是基于swing写的。就看那么个意思。有些许GUI了解就达到目的了
EDT: 事件分发线程 Event Dispatch Thread
EDT负责GUI drawing, 事件分发处理. 因此,当onClickListener中有一个长时间运行的任务,那么会导致界面无响应.
那么对长时间运行的任务,必须要将这个任务委托给分线程,让分线程去处理这个任务, 这可以迅速释放控制器给EDT。从而保证GUI的迅速响应。
用单线程实现线程中的任务调度
范例
public class SingleThread {
private static final ExecutorService exec = Executors.newSingleThreadExecutor(new SingleThreadFactory());
private static volatile Thread theSingleThread;
private static class SingleThreadFactory implements ThreadFactory {
public Thread newThread(Runnable r) {
theSingleThread = new Thread(r);
return theSingleThread;
}
}
public static boolean isEventDispatchThread () {
return Thread.currentThread() == theSingleThread;
}
public static void invokeLater(Runnable task) {
exec.execute(task);
}
public static void invokeAndWait(Runnable task) throws InterruptedException, InvocationTargetException {
Future f = exec.submit(task);
try {
f.get();
} catch (ExecutionException e) {
throw new InvocationTargetException(e);
}
}
}
用newSingleThreadExecutor来实现任务的提交。还是比较靠谱的。起码是单线程。安全。
适用:任务时间短,不需要被并发执行,
GUI事件流程
![](https://img-blog.csdn.net/20150921143626904?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
在鼠标点击后,事件的处理流程是这样的。。。这里只要这里doSomething访问的是线程安全的或者GUI对象,而且,时间短的话,那么这里跟线程相关的问题都可以忽略。
面对长时间任务
使用线程接力的方法是比较可取的。
直接看范例。
mSearchView.setOnClickListener(
new OnClickListener() {
@Override
public void onClick(View v) {
mSearchView.setText("start");//#--------------->第一部分在GUIExecutor中
backgroundExec.execute(new Runnable() {
public void run() {
try {
doSomethingBig();//#--------------->第二部分在backgroundExec中
} finally {
GuiExecutor.instance().execute(new Runnable() {
public void run() {
mSearchView.setText("end");//#--------------->第三部分在GUIExecutor中
}
});
}
}
});
}
});
这种做法就叫线程接力。如果这里还需要用Future,那么,用get就可以在得到结果后继续操作。
利用Future取消线程
1. 设置mayInterruptIfRuning为true
2. 调用cancel
Future<?> runningTask;
mSubMitBT.setOnClickListener(
new OnClickListener() {
@Override
public void onClick(View v) {
if(runningTask == null){
runningTask = backgroundExec.submit(new Runnable() {
public void run() {
while(hasWork()){
if(Thread.currentThread().isInterrupted()){
cleanup();
break;
}
doSomething();
}
}
});
}
}
});
mCancelBT.setOnClickListener(
new OnClickListener() {
@Override
public void onClick(View v) {
if(runningTask != null) runningTask.cancel();
}
});
利用Future更新完成状态和进度
先看一段FutureTask的源码
public class FutureTask<V> implements RunnableFuture<V> {
......
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;//#这里可以传入callable
this.state = NEW; // ensure visibility of callable
}
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
public boolean isCancelled() {//#通过isCanceled检查是否中止
return state >= CANCELLED;
}
public boolean isDone() {//#通过isCanceled检查是否结束
return state != NEW;
}
......
protected void done() { }
......
}
FutureTask实现了RunnableFuture,RunnableFuture继承了Future
public abstract class BackgroundTask<V> implements Runnable, Future<V> {
private final FutureTask<V> computation = new Computation();
private class Computation extends FutureTask<V> {
public Computation() {
super(new Callable<V>() {
public V call() throws Exception {
return BackgroundTask.this.compute();//#这里的Callable会传入给callable,在run的时候会被调用。
}
});
}
protected final void done() {
GuiExecutor.instance().execute(new Runnable() {
public void run() {
V value = null;
Throwable thrown = null;
boolean cancelled = false;
try {
value = get();//#阻塞式获取结果
} catch (ExceptionException e) {
thrown = e.getCause();
} catch (CancellationException e) {
cancelled = true;
} finally {
onCompletion(value, thrown, cancelled);
}
}
});//# GuiExecutor这个类是自定义的。是针对swing写。写这个代码的意义在于意会下那么个意思。不要较真
}
protected void setProgress(final int cur, final int max) {
GuiExecutor.instance().execute(new Runnable() {
public void run() {
onProgress(cur, max);
}
});
}
protected abstract V computer() throws Exceptions;//#这里是要被实现的,在实现的过程中可以调用setProgress来更新进度
protected void onCompletion(V result, Throwable exe, boolean cancelled) {}
protected void onProgress(final int cur, final int max) {}
}
}
总结
1. GUI设计大部分都是单线程的。也可以自己写。要够严谨才行。
2. 长时间运行的任务,一定要放在分线程里头
3. 线程接力就是把一个人任务拆开执行。UI的归一个线程,逻辑的归一个线程。这是为了避免计算造成阻塞。影响响应
4. EDT中的处理的任务要迅速
5. 创建一个自己的类去处理。Future和newSingleThreadExecutor搭配使用有奇效哦。
6. 库的访问,System.loadLibrary什么的,都要放在同一个线程中执行。android里头一般就是static。方便快捷
因为特么GUI工具包都被实现为单线程.---------->因为单线程能保证时间有序进行.多线程不稳定的多,开发难度大.现在都是单线程了.
此文不必较真,有个别类是基于swing写的。就看那么个意思。有些许GUI了解就达到目的了
EDT: 事件分发线程 Event Dispatch Thread
EDT负责GUI drawing, 事件分发处理. 因此,当onClickListener中有一个长时间运行的任务,那么会导致界面无响应.
那么对长时间运行的任务,必须要将这个任务委托给分线程,让分线程去处理这个任务, 这可以迅速释放控制器给EDT。从而保证GUI的迅速响应。
用单线程实现线程中的任务调度
范例
public class SingleThread {
private static final ExecutorService exec = Executors.newSingleThreadExecutor(new SingleThreadFactory());
private static volatile Thread theSingleThread;
private static class SingleThreadFactory implements ThreadFactory {
public Thread newThread(Runnable r) {
theSingleThread = new Thread(r);
return theSingleThread;
}
}
public static boolean isEventDispatchThread () {
return Thread.currentThread() == theSingleThread;
}
public static void invokeLater(Runnable task) {
exec.execute(task);
}
public static void invokeAndWait(Runnable task) throws InterruptedException, InvocationTargetException {
Future f = exec.submit(task);
try {
f.get();
} catch (ExecutionException e) {
throw new InvocationTargetException(e);
}
}
}
用newSingleThreadExecutor来实现任务的提交。还是比较靠谱的。起码是单线程。安全。
适用:任务时间短,不需要被并发执行,
GUI事件流程
在鼠标点击后,事件的处理流程是这样的。。。这里只要这里doSomething访问的是线程安全的或者GUI对象,而且,时间短的话,那么这里跟线程相关的问题都可以忽略。
面对长时间任务
使用线程接力的方法是比较可取的。
直接看范例。
mSearchView.setOnClickListener(
new OnClickListener() {
@Override
public void onClick(View v) {
mSearchView.setText("start");//#--------------->第一部分在GUIExecutor中
backgroundExec.execute(new Runnable() {
public void run() {
try {
doSomethingBig();//#--------------->第二部分在backgroundExec中
} finally {
GuiExecutor.instance().execute(new Runnable() {
public void run() {
mSearchView.setText("end");//#--------------->第三部分在GUIExecutor中
}
});
}
}
});
}
});
这种做法就叫线程接力。如果这里还需要用Future,那么,用get就可以在得到结果后继续操作。
利用Future取消线程
1. 设置mayInterruptIfRuning为true
2. 调用cancel
Future<?> runningTask;
mSubMitBT.setOnClickListener(
new OnClickListener() {
@Override
public void onClick(View v) {
if(runningTask == null){
runningTask = backgroundExec.submit(new Runnable() {
public void run() {
while(hasWork()){
if(Thread.currentThread().isInterrupted()){
cleanup();
break;
}
doSomething();
}
}
});
}
}
});
mCancelBT.setOnClickListener(
new OnClickListener() {
@Override
public void onClick(View v) {
if(runningTask != null) runningTask.cancel();
}
});
利用Future更新完成状态和进度
先看一段FutureTask的源码
public class FutureTask<V> implements RunnableFuture<V> {
......
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;//#这里可以传入callable
this.state = NEW; // ensure visibility of callable
}
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
public boolean isCancelled() {//#通过isCanceled检查是否中止
return state >= CANCELLED;
}
public boolean isDone() {//#通过isCanceled检查是否结束
return state != NEW;
}
......
protected void done() { }
......
}
FutureTask实现了RunnableFuture,RunnableFuture继承了Future
public abstract class BackgroundTask<V> implements Runnable, Future<V> {
private final FutureTask<V> computation = new Computation();
private class Computation extends FutureTask<V> {
public Computation() {
super(new Callable<V>() {
public V call() throws Exception {
return BackgroundTask.this.compute();//#这里的Callable会传入给callable,在run的时候会被调用。
}
});
}
protected final void done() {
GuiExecutor.instance().execute(new Runnable() {
public void run() {
V value = null;
Throwable thrown = null;
boolean cancelled = false;
try {
value = get();//#阻塞式获取结果
} catch (ExceptionException e) {
thrown = e.getCause();
} catch (CancellationException e) {
cancelled = true;
} finally {
onCompletion(value, thrown, cancelled);
}
}
});//# GuiExecutor这个类是自定义的。是针对swing写。写这个代码的意义在于意会下那么个意思。不要较真
}
protected void setProgress(final int cur, final int max) {
GuiExecutor.instance().execute(new Runnable() {
public void run() {
onProgress(cur, max);
}
});
}
protected abstract V computer() throws Exceptions;//#这里是要被实现的,在实现的过程中可以调用setProgress来更新进度
protected void onCompletion(V result, Throwable exe, boolean cancelled) {}
protected void onProgress(final int cur, final int max) {}
}
}
总结
1. GUI设计大部分都是单线程的。也可以自己写。要够严谨才行。
2. 长时间运行的任务,一定要放在分线程里头
3. 线程接力就是把一个人任务拆开执行。UI的归一个线程,逻辑的归一个线程。这是为了避免计算造成阻塞。影响响应
4. EDT中的处理的任务要迅速
5. 创建一个自己的类去处理。Future和newSingleThreadExecutor搭配使用有奇效哦。
6. 库的访问,System.loadLibrary什么的,都要放在同一个线程中执行。android里头一般就是static。方便快捷
相关文章推荐
- UItableView一些小方法
- poj3581 Sequence(后缀数组sa的运用+离散化)
- ThreadPoolExecutor使用和思考-线程池大小设置与BlockingQueue的三种实现区别
- Opencv3.0+opencv_contrib_lib +VS2013(编译)+CMake-gui(最近的版本都可以)
- 判断 Fragment 的 UI 是否可见
- UML建模之时序图(Sequence Diagram)
- 【UIFramework】前端UI框架—导航条
- java中queue的使用
- requestWindowFeature()的应用
- android NotificationCompat.Builder 使用
- 15.Android 异步更新UI 技巧
- DBCP针对不同数据库的validationQuery
- 递归-快速排序quickSort
- 字符串 intValue、floatValue、doubleValue、longLongValue 方法可以正确转换的位数或者大小
- iOS经典讲解之UICollectionView
- A new session could not be created. (Original error: Requested a new session but one was in progress) )错误解决办法
- POJ 2533 Longest Ordered Subsequence (DP动态规划)
- PAT研究生入学考试2015.03第四题Build A Binary Search Tree (30) 题解
- List<data> 数据转为easyui-datagrid json数据格式
- UILabel自适应高、宽