-
2021-03-13 00:18:31
ThreadSwitch
一个方便的主线程和子线程之间的线程切换框架
使用说明
提交主线程任务
当你在子线程完成了任务,比如查询数据库等,需要切换到主线程,在主线程中操作
WorkerCenter.getInstance().submitMainThreadTask(new ThreadTask() {
@Override
public void onWork() {
//主线程任务
}
});
//主线程延时任务
WorkerCenter.getInstance().submitMainThreadTaskDelay(new ThreadTask() {
@Override
public void onWork() {
//主线程延迟任务
}
},10000);
提交子线程队列任务
子线程执行队列任务,根据提交的先后顺序执行,无任何方式的结果通知
WorkerCenter.getInstance().submitQueneTask(new ThreadTask() {
@Override
public void onWork() {
//子线程队列任务
}
});
//子线程队列延时任务
WorkerCenter.getInstance().submitQueneTaskDelay(new ThreadTask() {
@Override
public void onWork() {
//子线程队列延时任务
}
},10000);
提交子线程任务,同时执行多个子任务
线程池处理提交任务,同时执行。可配置 执行的结果 是在子线程中还是在主线程 执行
WorkerCenter.getInstance().submitNormalTask(new WorkerTask("workTask",true) {
@Override
protected String execute() {
//子线程中执行任务
return "this is a test task for woker";
}
@Override
protected void notifyResult(String result) {
//处理结果,可配置在子线程还是主线程
if(!TextUtils.isEmpty(result)){
Log.e("woker","execute's result is " + result + " 当前执行线程是否为主线程 : " + (Looper.myLooper() == Looper.getMainLooper()) );
}
}
});
提交子线程阻塞任务
提交任务 ,阻塞执行,直到子线程任务执行完成
WorkerCenter.getInstance().submitBlockTask(new ThreadTask() {
@Override
public void onWork() {
//提交线程池 阻塞任务
}
});
更多相关内容 -
FXThreads:在 JavaFX 中切换主线程和后台线程变得容易
2021-06-07 02:48:24在 JavaFX 中切换主线程和后台线程变得容易。 该项目由单个类组成,因此集成它的最简单方法是将其复制到您的项目中,并可能重命名包。 示例 1 // get some string from a remote server: Future< String> ... -
Java线程切换(一)
2020-12-19 21:47:00我们往往做一些数据处理是耗时操作,必须要在子线程中进行,然后再将处理后的数据切换到主线程去更新UI,这便是线程切换。线程切换的本质是“数据的切换”,即将数据从一个线程传递到另一个线程。二 案例描述老风格...(本文由言念小文原创,转载请注明出处)
一 前言
有Android开发经验的同学都清楚,UI的更新必须在主线程中进行,且主线程不能被阻塞,否则系统ANR异常。我们往往做一些数据处理是耗时操作,必须要在子线程中进行,然后再将处理后的数据切换到主线程去更新UI,这便是线程切换。
线程切换的本质是“数据的切换”,即将数据从一个线程传递到另一个线程。
二 案例描述
老风格,先给出场景案例,然后通过线程切换实现该案例。
案例:在线程A中调用api发送一段数据data,data最终通过线程切换由线程B执行数据处理动作。
三 线程切换基本原理
所谓线程切换,本质就是“数据在线程间切换”,即将一个线程A中的数据,传递到另一个线程B执行数据处理操作。基于以上认知,比较自然的实现逻就是:将线程A中的数据进行拷贝,线程B获取到拷贝数据,然后进行处理,如下图所示。
四 案例实现代码
首先创建一个DataHandler类,该类中定义了数据拷贝方法和数据处理线程。在DataHandler中定义了一个用于数据副本存放的成员变量mData;外部模块可通过sendData()将数据拷贝到DataHandler中;在DataHandler构造中初始化数据处理线程(线程B);该线程重写的run()方法中用一个while循环不停查询数据副本mData的值,当检测到mData有数据时,则执行数据处理动作,数据处理完成后,将数副本mData清空。这样便实现了将其他外部线程中的数据切换到线程B中处理。
public classDataHandler {//数据副本
privateString mData;publicDataHandler() {
initSwithThread();
}/*** 发送数据,供外部调用
*@paramdata*/
public voidsendData(String data) {//拷贝数据
mData =data;
}private voidinitSwithThread() {new Thread(newRunnable() {
@Overridepublic voidrun() {while(true) {if(null != mData && mData.length() > 0) {
System.out.println("process data, run in thread : "
+ Thread.currentThread().getName() + ", data = " +mData);//数据发送后,清除数据副本mData
mData = null;
}//为了能够清晰看清结果这里让thread休眠1s
try{
Thread.sleep(1000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
},"ThreadB").start();
}
}
测试类ThreadTest中创建线程A,然后在A中调用DataHandler实例的数据拷贝方法sendData()将数据切换到线程B中处理。
public classThreadTest {public staticDataHandler mHandler;public static voidmain(String[] args) {
mHandler= newDataHandler();
Thread threadA= new Thread(newRunnable() {
@Overridepublic voidrun() {//线程A中发送数据
String data = "hello code 9527";
System.out.println("send data, run in thread : "
+ Thread.currentThread().getName() + ", data = " +data);//通过DataHandler将数据拷贝到线程B中处理
mHandler.sendData(data);
}
},"threadA");
threadA.start();
}
}
运行结果:
send data, run in thread : threadA, data = hello code 9527
process data, run in thread : ThreadB, data = hello code 9527
从结果可以看出数据“hello code 9527”在线程A中被DataHandler拷贝出来,副本存放到DataHandler中,然后通过线程B中进行处理。这样就实现了数据在线程间的切换。实现切换的“灵魂操作”便是“数据拷贝”和“目标线程对拷贝副本的轮询”。实现原理是不是很简单?其实Android的Handler机制也是基于这种原理实现的,有兴趣的同学可以深入分析一下Handler源码。
-
Java线程的6种状态及切换教程
2021-01-12 06:44:04Java中线程的状态分为6种。1. 初始(NEW) :新创建了一个线程对象,但还没有调用start()方法。2.运行(RUNNABLE) :Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。线程对象创建后,其他线程...Java中线程的状态分为6种。
1. 初始(NEW) :新创建了一个线程对象,但还没有调用start()方法。
2.运行(RUNNABLE) :Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。
3. 阻塞(BLOCKED) :表示线程阻塞于锁。
4. 等待(WAITING) :进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
5. 超时等待(TIMED_WAITING) :该状态不同于WAITING,它可以在指定的时间后自行返回。
6.终止(TERMINATED) :表示该线程已经执行完毕。
这6种状态定义在Thread类的State枚举中,可查看源码进行一一对应。
一、线程的状态图
二、状态详细说明
2.1. 初始状态(NEW)
实现Runnable接口和继承Thread可以得到一个线程类,new一个实例出来,线程就进入了初始状态。
2.2. 就绪状态(RUNNABLE之READY)
就绪状态只是说你资格运行,调度程序( Cpu )没有挑选到你,你就永远是就绪状态。
调用线程的start()方法,此线程进入就绪状态。
当前线程sleep()方法结束,其他线程join()结束,等待用户输入完毕,某个线程拿到对象锁,这些线程也将进入就绪状态。
当前线程时间片用完了,调用当前线程的yield()方法,当前线程进入就绪状态。
锁池里的线程拿到对象锁后,进入就绪状态。
2.3. 运行中状态(RUNNABLE之RUNNING)
线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一的一种方式。
3. 阻塞状态(BLOCKED)
阻塞状态是线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态。
4. 等待(WAITING)
处于这种状态的线程不会被分配CPU执行时间,它们要等待被显式地唤醒,否则会处于无限期等待的状态。
5. 超时等待(TIMED_WAITING)
处于这种状态的线程不会被分配CPU执行时间,不过无须无限期等待被其他线程显示地唤醒,在达到一定时间后它们会自动唤醒。
6. 终止状态(TERMINATED)
当线程的run()方法完成时,或者主线程的main()方法完成时,我们就认为它终止了。这个线程对象也许是活的,但是它已经不是一个单独执行的线程。线程一旦终止了,就不能复生。
在一个终止的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。
三、等待队列
调用obj的wait(), notify()方法前,必须获得obj锁,也就是必须写在synchronized(obj) 代码段内。
与等待队列相关的步骤和图
线程1获取对象A的锁,正在使用对象A。
线程1调用对象A的wait()方法。
线程1释放对象A的锁,并马上进入等待队列。
锁池里面的对象争抢对象A的锁。
线程5获得对象A的锁,进入synchronized块,使用对象A。
线程5调用对象A的notifyAll()方法,唤醒所有线程,所有线程进入同步队列。若线程5调用对象A的notify()方法,则唤醒一个线程,不知道会唤醒谁,被唤醒的那个线程进入同步队列。
notifyAll()方法所在synchronized结束,线程5释放对象A的锁。
同步队列的线程争抢对象锁,但线程1什么时候能抢到就不知道了。
四、同步队列状态
当前线程想调用对象A的同步方法时,发现对象A的锁被别的线程占有,此时当前线程进入同步队列。简言之,同步队列里面放的都是想争夺对象锁的线程。
当一个线程1被另外一个线程2唤醒时,1线程进入同步队列,去争夺对象锁。
同步队列是在同步的环境下才有的概念,一个对象对应一个同步队列。
线程等待时间到了或被notify/notifyAll唤醒后,会进入同步队列竞争锁,如果获得锁,进入RUNNABLE状态,否则进入BLOCKED状态等待获取锁。
五、几个方法的比较
Thread.sleep(long millis),一定是当前线程调用此方法,当前线程进入TIMED_WAITING状态,但不释放对象锁,millis后线程自动苏醒进入就绪状态。作用:给其它线程执行机会的最佳方式。
Thread.yield(),一定是当前线程调用此方法,当前线程放弃获取的CPU时间片,但不释放锁资源,由运行状态变为就绪状态,让OS再次选择线程。 作用:让相同优先级的线程轮流执行,但并不保证一定会轮流执行。实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。Thread.yield()不会导致阻塞。该方法与sleep()类似,只是不能由用户指定暂停多长时间。
thread.join()/thread.join(long millis),当前线程里调用其它线程t的join方法,当前线程进入WAITING/TIMED_WAITING状态,当前线程不会释放已经持有的对象锁。线程t执行完毕或者millis时间到,当前线程一般情况下进入RUNNABLE状态,也有可能进入BLOCKED状态(因为join是基于wait实现的)。
Object.wait(),当前线程调用对象的wait()方法,当前线程释放对象锁,进入等待队列。依靠notify()/notifyAll()唤醒或者wait(long timeout) timeout时间到自动唤醒。
Object.notify()唤醒在此对象监视器上等待的单个线程,选择是任意性的。notifyAll()唤醒在此对象监视器上等待的所有线程。
LockSupport.park()/LockSupport.parkNanos(long nanos),LockSupport.parkUntil(long deadlines), 当前线程进入WAITING/TIMED_WAITING状态。对比wait方法,不需要获得锁就可以让线程进入WAITING/TIMED_WAITING状态,需要通过LockSupport.unpark(Thread thread)唤醒。
六 创建线程的几种方式
Thread
线程运行的过程会产生很多信息,这些信息都保存在Thread类中的成员变量里面,常见的有:
a.线程的ID是唯一标识getId()
b.线程的名称:getName(),如果不设置线程名称默认为“Thread-xx”
c.线程的优先级:getPriority,线程优先级从1-10,其中数字越大表示优先级别越高,同时获得JVM调度执行的可能性越大,JDK内置了三种常见的状态:
`//最小优先级
public final static int MIN_PRIORITY = 1;
//一般优先级
public final static int NORM_PRIORITY = 5;
//最大优先级
public final static int MAX_PRIORITY = 10;`
一般不推荐设置线程的优先级,如果进行设置了非法的优先级程序就会出现IllegalArgumentException异常。
创建线程方式一:
继承Thread类。
步骤:
1,定义一个类继承Thread类。
2,覆盖Thread类中的run方法。
3,直接创建Thread的子类对象创建线程。
4,调用start方法开启线程并调用线程的任务run方法执行。
可以通过Thread的getName获取线程的名称 Thread-编号(从0开始)
主线程的名字就是main。
class Demo extends Thread
{
/**
*线程名称
*/
private String name;
Demo(String name)
{
//父类构造函数,改线程的名称
super(name);
//this.name = name;
}
//***run方法中定义就是线程要运行的任务代码。***
public void run()
{
for(int x=0; x<10; x++)
{
//for(int y=-9999999; y<999999999; y++){}
System.out.println(name+"....x="+x+".....name="+Thread.currentThread().getName());
}
}
}
class ThreadDemo2
{
public static void main(String[] args)
{
Demo d1 = new Demo("旺财");
Demo d2 = new Demo("xiaoqiang");
d1.start();//开启线程,调用run方法。
d2.start();
System.out.println("over...."+Thread.currentThread().getName());
}
}
创建线程方式二
当该类有自己父类的时候,通过实现Runnable接口,覆盖run方法。(常用)
步骤:
1,定义类实现Runnable接口。
2,覆盖接口中的run方法,将线程的任务代码封装到run方法中。
3,通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。
为什么?因为线程的任务都封装在Runnable接口子类对象的run方法中。
所以要在线程对象创建时就必须明确要运行的任务。
思想:将线程的任务通过Runnable接口封装成了对象。
4,调用线程对象的start方法开启线程。
实现Runnable接口的好处:
1,将线程的任务从线程的子类中分离出来,进行了单独的封装,按照面向对象的思想将任务封装成对象。
2,避免了java单继承的局限性。
//extends Fu
//准备扩展Demo类的功能,让其中的内容可以作为线程的任务执行。
//通过接口的形式完成。
class Demo implements Runnable{
public void run()
{
show();
}
public void show()
{
for(int x=0; x<20; x++)
{
System.out.println(Thread.currentThread().getName()+"....."+x);
}
}
}
class ThreadDemo
{
public static void main(String[] args)
{
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
t2.start();
}
}
创建线程方式三
实现Callable接口
与使用Runnable相比, Callable功能更强大些
1 相比run()方法,可以有返回值
2 方法可以抛出异常
3 支持泛型的返回值
4 需要借助FutureTask类,比如获取返回结果
Future接口
1 可以对具体Runnable、Callable任务的执行结果进行取消、查询是
否完成、获取结果等。
2 FutrueTask是Futrue接口的唯一的实现类
3 FutureTask 同时实现了Runnable, Future接口。它既可以作为 Runnable被线程执行,又可以作为Future得到Callable的返回值
//1.创建一个实现Callable的实现类
class Stu implements Callable {
//2.实现call方法,将此线程需要执行的操作生命call()中
@Override
public Object call() throws Exception {
int sum=0;
for (int i = 1; i <=100; i++) {
if(i % 2 == 0){
System.out.println(i);
sum += i;
}
}
return sum;
}
}
public class Bank {
public static void main(String[] args) {
//3.创建Callable接口实现类的对象
Stu stu = new Stu();
//4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
FutureTask futureTask = new FutureTask(stu);
//5.FutureTask的对象作为参数传递到Thread类的构造器中创建Thread,并调用start()
new Thread(futureTask).start();
try {
Object sum = futureTask.get();
System.out.println("总和为"+sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
创建线程方式四
使用线程池
背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程, 对性能影响很大。
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完 放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交 通工具。
好处:
1提高响应速度(减少了创建新线程的时间);
2降低资源消耗(重复利用线程池中线程,不需要每次都创建);
3便于线程管理;
corePoolSize:核心池的大小
maximumPoolSize:最大线程数
keepAliveTime:线程没有任务时最多保持多长时间后会终止
//创建并使用多线程的第四种方法:使用线程池
class MyThread implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
if(i % 2 ==0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
public class ThreadPool {
public static void main(String[] args) {
// 1.提供指定线程的数量
ExecutorService service = Executors.newFixedThreadPool(10);
//设置线程的属性
ThreadPoolExecutor service1= (ThreadPoolExecutor) service;
//service1.setMaximumPoolSize(15);
//service1.setCorePoolSize();*/
// 2.将Runnable实现类的对象作为形参传递给ExecutorService的submit()方法中,开启线程
// 并执行相关的run()
service.execute(new MyThread());//适用于Runnable
//service.submit();适用于Callable
// 3.结束线程的使用
service.shutdown();
}
}
总结
到此这篇关于Java线程的6种状态及切换的文章就介绍到这了,更多相关Java线程状态及切换内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
-
[Android] 任意时刻从子线程切换到主线程的实现
2021-03-13 00:18:32这里我梳理一下思路: 还是使用Handler进行线程切换 在子线程中能通过简单的调用就切换到主线程进行工作 在子线程切换到主线程时,子线程进入阻塞直到主线程执行完成(知道为什么有这样的需求么?) 一定要保证其...========================================================
作者:qiujuer
博客:blog.csdn.net/qiujuer
网站:www.qiujuer.net
开源库:Genius-Android
转载请注明出处:http://blog.csdn.net/qiujuer/article/details/41599383
========================================================
引入
在Android开发中常常会遇到网络请求,数据库数据准备等一些耗时的操作;而这些操作是不允许在主线程中进行的。因为这样会堵塞主线程导致程序出现未响应情况。
所以只能另起一个子线程进行这些耗时的操作,完成后再显示到界面。众所周知,界面等控件操作只能在主线程中完成;所以不可避免的需要从子线程切换到主线程。
方法
对于这样的情况在Android 中比较常见的是使用AsynTask类或者 Handler来进行线程切换;而其中AsynTask是官方封装的类,较为简单,效率也比较可以,但是并不适合所有的情况,至少我使用了一两次后就再也没有使用了。使用 Handler可以说是最万能的方式,其原理是消息循环,在主线程中建立Handler变量时,就会启动Handler消息循环,一个个的处理消息队列中的任务。但是其也有棘手的时候;其棘手的地方就是麻烦。
每次都需要去建立一个 Handler 类,然后使用voidhandleMessage(Messagemsg) 方法把消息取出来进行界面操作,而其中还要遇到参数的传递等问题,说起来真的是挺麻烦的。
想法
既然有着这么多的问题,但是又有其的优势,我们何不自行封装一次呢?
这里我梳理一下思路:
还是使用Handler进行线程切换
在子线程中能通过简单的调用就切换到主线程进行工作
在子线程切换到主线程时,子线程进入阻塞直到主线程执行完成(知道为什么有这样的需求么?)
一定要保证其效率
主线程的执行要有时间限制,不能执行太长时间导致主线程阻塞
我能想到的就是这些;观众老爷们咋样?可否还有需求?
说干就干,梳理一下实现方法
使用Handler实现,既然这样那么主方法当然就是采用继承Handler来实现
而要简单同时又要能随时进入方法 那么对外采用静态方法是个不错的选择
而要保证效率的话,那就不能让Handler的消息队列过于太多,但是又要满足能随时调用,那么采用外部Queue
更具情况有阻塞与不阻塞子线程两种情况,那么采用两个Queue吧,分开来好一点
要保证不能长时间在主线程执行那么对于队列的执行一定要有时间限制加一个时间变量吧
当然最后考虑了一下,既然要简单那么传入参数采用Runnable 是很爽的
万事俱备,只欠东风了;好了进入下一环节。
CodeTime
首先我们建立一个ToolKit类:
public class ToolKit {
/**
* Asynchronously
*
* @param runnable Runnable Interface
*/
public static void runOnMainThreadAsync(Runnable runnable) {
}
/**
* Synchronously
*
* @param runnable Runnable Interface
*/
public static void runOnMainThreadSync(Runnable runnable) {
}
}
两个对外的方法简单来说就是这样了;但是其功能实现就需要使用继承Handler了。
建立类HandlerPoster,继承自Handler:
final class HandlerPoster extends Handler {
private final int ASYNC = 0x1;
private final int SYNC = 0x2;
private final Queue asyncPool;
private final Queue syncPool;
private final int maxMillisInsideHandleMessage;
private boolean asyncActive;
private boolean syncActive;
HandlerPoster(Looper looper, int maxMillisInsideHandleMessage) {
super(looper);
this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
asyncPool = new LinkedList<>();
syncPool = new LinkedList<>();
}
void dispose() {
this.removeCallbacksAndMessages(null);
this.asyncPool.clear();
this.syncPool.clear();
}
void async(Runnable runnable) {
synchronized (asyncPool) {
asyncPool.offer(runnable);
if (!asyncActive) {
asyncActive = true;
if (!sendMessage(obtainMessage(ASYNC))) {
throw new GeniusException("Could not send handler message");
}
}
}
}
void sync(SyncPost post) {
synchronized (syncPool) {
syncPool.offer(post);
if (!syncActive) {
syncActive = true;
if (!sendMessage(obtainMessage(SYNC))) {
throw new GeniusException("Could not send handler message");
}
}
}
}
@Override
public void handleMessage(Message msg) {
if (msg.what == ASYNC) {
boolean rescheduled = false;
try {
long started = SystemClock.uptimeMillis();
while (true) {
Runnable runnable = asyncPool.poll();
if (runnable == null) {
synchronized (asyncPool) {
// Check again, this time in synchronized
runnable = asyncPool.poll();
if (runnable == null) {
asyncActive = false;
return;
}
}
}
runnable.run();
long timeInMethod = SystemClock.uptimeMillis() - started;
if (timeInMethod >= maxMillisInsideHandleMessage) {
if (!sendMessage(obtainMessage(ASYNC))) {
throw new GeniusException("Could not send handler message");
}
rescheduled = true;
return;
}
}
} finally {
asyncActive = rescheduled;
}
} else if (msg.what == SYNC) {
boolean rescheduled = false;
try {
long started = SystemClock.uptimeMillis();
while (true) {
SyncPost post = syncPool.poll();
if (post == null) {
synchronized (syncPool) {
// Check again, this time in synchronized
post = syncPool.poll();
if (post == null) {
syncActive = false;
return;
}
}
}
post.run();
long timeInMethod = SystemClock.uptimeMillis() - started;
if (timeInMethod >= maxMillisInsideHandleMessage) {
if (!sendMessage(obtainMessage(SYNC))) {
throw new GeniusException("Could not send handler message");
}
rescheduled = true;
return;
}
}
} finally {
syncActive = rescheduled;
}
} else super.handleMessage(msg);
}
}
下面来说说这个我花了很大时间弄出来的类。
类的变量部分:
两个标识,两个队列,两个执行状态,一个时间限制;很好理解吧?标识为了区别分别是处理那个队列使用;队列当然是装着任务了;执行状态是为了避免重复发送消息导致消息队列过多;时间限制这个最好理解了。
下面来说说方法部分:
构造函数HandlerPoster(Looper_looper,int_maxMillisInsideHandleMessage):
传入两个参数,分别是 Looper,用于初始化到主线程,后面的是时间限制;然后初始化了两个队列。
销毁函数void_dispose():首先去除掉没有处理的消息,然后清空队列。
添加异步执行方法void_async(Runnable_runnable):
void async(Runnable runnable) {
synchronized (asyncPool) {
asyncPool.offer(runnable);
if (!asyncActive) {
asyncActive = true;
if (!sendMessage(obtainMessage(ASYNC))) {
throw new GeniusException("Could not send handler message");
}
}
}
}
可以看见进入方法后第一件事儿就是进入同步状态,然后调用asyncPool.offer(runnable);把任务写入到队列。
之后判断当前是否处于异步任务执行中,如果不是:立刻改变状态,然后发送一个消息给当前Handler,当然不要忘记了传入标识。
当然为了效率其消息的构造也是通过obtainMessage(ASYNC)方法来完成,为的就是不过多建立新的Message,尽量使用当前队列中空闲的消息。
添加同步执行方法void_sync(SyncPost_post):
void sync(SyncPost post) {
synchronized (syncPool) {
syncPool.offer(post);
if (!syncActive) {
syncActive = true;
if (!sendMessage(obtainMessage(SYNC))) {
throw new GeniusException("Could not send handler message");
}
}
}
}
可以看到,这里传入的并不是Runnable而是SyncPost这是为了同步而对Runnable进行了一次封装后的类;后面介绍。
同样是进入同步,添加,判断,发送消息。
任务执行者@Override_void_handleMessage(Message_msg):
这里是复写的Handler的消息处理方法,当当前Handler消息队列中有消息的时候将会按照顺序一个个的调用该方法。
分段来看:
if (msg.what == ASYNC) {
boolean rescheduled = false;
try {
long started = SystemClock.uptimeMillis();
while (true) {
Runnable runnable = asyncPool.poll();
if (runnable == null) {
synchronized (asyncPool) {
// Check again, this time in synchronized
runnable = asyncPool.poll();
if (runnable == null) {
asyncActive = false;
return;
}
}
}
runnable.run();
long timeInMethod = SystemClock.uptimeMillis() - started;
if (timeInMethod >= maxMillisInsideHandleMessage) {
if (!sendMessage(obtainMessage(ASYNC))) {
throw new GeniusException("Could not send handler message");
}
rescheduled = true;
return;
}
}
} finally {
asyncActive = rescheduled;
}
}
进入后首先判断是否是进行异步处理的消息,如果是那么进入该位置。
进入后我们进行了try_finally有一个变量long_started用于标识开始时间。
当执行一个任务后就判断一次如果超过了每次占用主线程的时间限制,那么不管队列中的任务是否执行完成都退出,同时发起一个新的消息到Handler循环队列。
在while部分,我们从队列取出一个任务,采用Poll方法;判断是否为空,如果为空进入队列同步块;然后再取一次,再次判断。
如果恰巧在进入同步队列之前有新的任务来了,那么第二次取到的当然就不是 NULL也就会继续执行下去。反之,如果还是为空;那么重置当前队列的状态为false同时跳出循环。
下面来看第二部分:
else if (msg.what == SYNC) {
boolean rescheduled = false;
try {
long started = SystemClock.uptimeMillis();
while (true) {
SyncPost post = syncPool.poll();
if (post == null) {
synchronized (syncPool) {
// Check again, this time in synchronized
post = syncPool.poll();
if (post == null) {
syncActive = false;
return;
}
}
}
post.run();
long timeInMethod = SystemClock.uptimeMillis() - started;
if (timeInMethod >= maxMillisInsideHandleMessage) {
if (!sendMessage(obtainMessage(SYNC))) {
throw new GeniusException("Could not send handler message");
}
rescheduled = true;
return;
}
}
} finally {
syncActive = rescheduled;
}
} else super.handleMessage(msg);
首先还是判断,如果是同步任务消息就进入,如果还是不是 那么只有调用super.handleMessage(msg);了。
从上面的处理部分可以看出来其处理的过程与第一部分可以说是完全一样的。
只不过是从不同队列取出不同的类SyncPost,然后判断执行,以及发送不同标识的消息;可以说如果懂了第一部分,这部分是毫无营养的。
这里就有问题了,既然方法操作流程一样,那么同步与异步是在哪里进行区分的?
这里就要看看SyncPost了:
final class SyncPost {
boolean end = false;
Runnable runnable;
SyncPost(Runnable runnable) {
this.runnable = runnable;
}
public void run() {
synchronized (this) {
runnable.run();
end = true;
try {
this.notifyAll();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void waitRun() {
if (!end) {
synchronized (this) {
if (!end) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
首先看看SyncPost的构造函数:
是不是传入一个Runnable接口?所以说是对Runnable的简单封装。
可以看见其public_void_run()方法:
在该方法中我们进入了同步块,然后调用Runnable接口的run方法。同时在执行完成后将其中的一个状态变量进行了改变boolean_end=true;
然后调用this.notifyAll();通知等待的部分可以继续了,当然有这样的情况;假如在进入该同步块的时候子线程还未执行到this.wait();部分呢?所以我们为此准备了end和try。
然后看看public_void_waitRun()方法:
在这个中,我们首先判断状态,如果状态已经变了,那么证明子线程执行到此处时,主线程以及执行了void_run()。
所以也就不用进入同步块进行等待了,不然那还不等死啊?反之就进入进行等待直到主线程调用this.notifyAll();
激情部分
马上进入到完成部分了,组建都完善了那么该进行最后的组装了。
回到类classToolKit
public class ToolKit {
private static HandlerPoster mainPoster = null;
private static HandlerPoster getMainPoster() {
if (mainPoster == null) {
synchronized (ToolKit.class) {
if (mainPoster == null) {
mainPoster = new HandlerPoster(Looper.getMainLooper(), 20);
}
}
}
return mainPoster;
}
/**
* Asynchronously
* The child thread asynchronous run relative to the main thread,
* not blocking the child thread
*
* @param runnable Runnable Interface
*/
public static void runOnMainThreadAsync(Runnable runnable) {
if (Looper.myLooper() == Looper.getMainLooper()) {
runnable.run();
return;
}
getMainPoster().async(runnable);
}
/**
* Synchronously
* The child thread relative thread synchronization operation,
* blocking the child thread,
* thread for the main thread to complete
*
* @param runnable Runnable Interface
*/
public static void runOnMainThreadSync(Runnable runnable) {
if (Looper.myLooper() == Looper.getMainLooper()) {
runnable.run();
return;
}
SyncPost poster = new SyncPost(runnable);
getMainPoster().sync(poster);
poster.waitRun();
}
public static void dispose() {
if (mainPoster != null) {
mainPoster.dispose();
mainPoster = null;
}
}
}
其中就一个静态变量HandlerPoster
然后一个初始化部分HandlerPoster_getMainPoster()这里采用同步的方式进行初始化,用于适应多线程同时调用情况;当然在初始化的时候我们传入了
mainPoster=newHandlerPoster(Looper.getMainLooper(),20);这里就决定了是在主线程执行的HandlerPoster,同时指定主线程单次运行时间为20毫秒。
在方法void_runOnMainThreadAsync(Runnable_runnable)中:
首先判断调用该方法的是否是主线程,如果是那还弄到队列中执行干嘛?直接执行啊;如果是子线程就调用getMainPoster().async(runnable);追加到队列中执行。
而在方法void_runOnMainThreadSync(Runnable_runnable)中:
public static void runOnMainThreadSync(Runnable runnable) {
if (Looper.myLooper() == Looper.getMainLooper()) {
runnable.run();
return;
}
SyncPost poster = new SyncPost(runnable);
getMainPoster().sync(poster);
poster.waitRun();
}
同样是线程判断,然后进行封装,然后丢进队列中等待执行,而在该方法中调用poster.waitRun();进行等待;直到主线程执行了SyncPost类的run方法。最后当然留下了一个销毁方法;妈妈说要学会清理不留垃圾:void_dispose()
OK,完成了
// "Runnable" 类实现其中 "run()" 方法
// "run()" 运行在主线程中,可在其中进行界面操作
// 同步进入主线程,等待主线程处理完成后继续执行子线程
ToolKit.runOnMainThreadSync(Runnable runnable);
// 异步进入主线程,无需等待
ToolKit.runOnMainThreadAsync(Runnable runnable);
对外就是这么两个方法,简单便捷啊;大伙试试吧;一个字爽!
代码:
开源项目:
-
RxJava2线程切换原理分析
2021-02-26 18:24:22通过对本节的学习你会发现,RxJava2线程切换是如此的简单,仅仅是通过两个操作符就能完成从子线程到主线程,或者主线程到子线程,再或者从子线程到子线程的切换。对应的操作符为:observerOn:指定观察者运行的线程... -
Android切换主线程更新UI方法总结
2019-02-14 10:09:29Android切换主线程更新UI方法总结 一、归纳总结 1. view.post(Runnable action) 2. activity.runOnUiThread(Runnable action) 3. Handler机制 4. 使用AsyncTask 二、详细介绍及示例 方法一: view.... -
Java Swing - invokeLater子线程修改主线程UI
2021-09-08 19:36:33问题: 1.我在这里是想解决如何使用SwingUtilities来进行线程通信。 2.扩展一下多线程与子线程的知识,一直对这一块儿比较迷糊 学习: ...1.就先来学习一下,Swing线程机制 ... 大多数SwingAPI是非线程安全的,不能在... -
【小家java】Java中主线程(父线程)与子线程的通信和联系
2018-11-05 18:47:22因此本文不做讨论 主线程与子线程之间的关系 1、最常见的情况,主线程中开启了一个子线程,开启之后,主线程与子线程互不影响各自的生命周期,即主线程结束,子线程还可以继续执行;子线程结束,主线程也能继续执行... -
Java平台原生Proxy代理把MVP中普通函数接口切换调度到Android UI主线程
2019-06-16 23:11:43Java平台原生Proxy代理把MVP中普通函数接口切换调度到Android UI主线程 在Android开发者,这种线程切换的需求场景很常见,比如在后台普通的Java线程展开了一项耗时的操作(比如下载一个大文件),下载时候需要实时... -
【Android 异步操作】Android 线程切换 ( 判定当前线程是否是主线程 | 子线程中执行主线程方法 | 主线程中...
2021-09-28 19:59:18一、判定当前线程是否是主线程、 二、子线程中执行主线程方法、 三、主线程中执行子线程方法、 -
rxjava线程切换主线程_Android中的rxjava多线程
2020-08-24 02:36:25rxjava线程切换主线程 重点 (Top highlight)RxJava has been in the development market for a long time but while interacting with some of my colleagues, I found out that many people haven’t started using... -
多线程切换的几种方法,你知道几种?
2020-12-19 21:46:57原标题:多线程切换的几种方法,你知道几种?刘望舒”,马上关注,每天早上8:42准时推送作者:蓝灰_qhttps://...我们先回顾一下Java多线程的几个基础内容,然后再分析总结一些经典代码中对于线程切换的实... -
Android子线程切换到UI线程方法总结
2021-03-13 00:18:09线程切换通过消息发送(发布)和接收(订阅)的方式切换的。1 .Handler子线程(非UI线程)调用handler对象sendMessage(msg)方法,将消息发送给关联Looper,Looper将消息存储在MessageQueue消息队列里面。然后轮巡取出... -
[Android] 任意时刻从子线程切换到主线程的实现原理及加强版
2014-12-12 23:56:01在 Android 的使用中经常会遇到从子线程切换到主线程进行界面更改的情况的;如果在一个 Activity 中进行倒好说一个 Handler 即可解决问题;但是假如很多个界面呢?每个界面都建立一个 Handler 么?太浪费了吧?咱们... -
通过源码来理解RxJava是切换到主线程的?
2020-04-07 14:21:40通过源码来理解RxJava是切换到主线程的? Observable.create(new ObservableOnSubscribe<String>() { @Override public void subscribe(ObservableEmitter<String> e) throws ... -
RxJava 是如何实现线程切换的(上)
2021-03-22 18:00:02前言通过前一篇的从观察者模式出发,聊聊RxJava,我们大致理解了RxJava的实现原理,在RxJava中可以非常方便的实现不同线程间的切换。subscribeOn 用于指定上游线程,observeOn 用于指定下游线程,多次用 subscribeOn ... -
Java子线程和主线程交替输出(一个简单的实例)
2017-05-20 16:42:19实现主线程和子线程的交替输出打印,首先创建一个实现线程方法的Service public class Service { boolean flag = false; //这是一个控制sub和mian的开关 public synchronized void sub(){ while(flag) { //... -
java基本教程之线程休眠 java多线程教程
2021-02-13 00:59:50sleep()介绍sleep() 定义在Thread.java中。sleep() 的作用是让当前线程休眠,即当前线程会从“运行状态”进入到“休眠(阻塞)状态”。sleep()会指定休眠时间,线程休眠的时间会大于/等于该休眠时间;在线程重新被... -
Android任意时刻从子线程切换到主线程的实现
2014-12-02 10:05:28引入 在Android开发中常常会遇到网络请求,数据库数据准备等一些耗时的操作;而这些操作是不允许在主线程...所以不可避免的需要从子线程切换到主线程。 方法 对于这样的情况在Android 中比较常见的是使用Asy -
Android 在JNI主线程调用Java方法
2019-01-01 16:58:051.1 C++主线程调用Java方法 在 Android C++多线程-创建子线程中演示了在 Java 层调用 native 层的方法,但是如何在 native 层去调用 Java 层的方法呢? 下面这个类,我们想在调用 callJavaMethodOnCPPMainThread ... -
Rxjava 子线程 主线程 切换 简单实现
2018-09-06 17:26:25//主线程 public MObservable observeOn (){ MObservableObserveOn on = new MObservableObserveOn( new MObservableOnSubscribe() { @Override public void subscribe (MObserver observer) ... -
Android之——任意时刻从子线程切换到主线程的实现(插曲)
2015-05-24 13:04:53一、引入 在Android开发中常常会遇到网络请求,数据库数据准备等一些耗时的操作;而这些操作是不允许在主线程中进行的。...所以不可避免的需要从子线程切换到主线程。 二、方法 对于这样的情况在Android 中 -
Java 主线程和子线程的转换
2017-08-23 15:56:59开发过程中,常常需要更新UI界面,需要在主线程做相关操作。主线程和IO线程的相互转换很频繁。特意整理了两个方法,轻松红/** * 主线程运行 * * @param mainThreadHandler 主线程处理器 */ public static void ... -
从源码处理一理Retrofit的异步网络请求如何把结果切换到主线程
2019-01-28 19:05:08场景:某日面试的时候被问道,Retrofit异步网络请求是怎么把结果返回给主线程的? 答曰:具体原理不是很清楚,最后应该是通过handler把结果发送到主线程的吧。。。 问:你确定吗?真是handler吗? 这一问把我问懵... -
Android 主线程与子线程区分和实践
2022-02-17 15:31:20对于学过java或者Android的都知道,在代码中主线程不能进行耗时操作,子线程不能更新UI,比如在自定义view时,想要让View重绘,需要先判断当前线程到底是不是主线程,然后根据判断结果来决定到底是调用 invalidate() ... -
获取主线程的handler
2018-03-30 17:02:03如果通过handler的形式切换线程就需要获取到主线程的handler,这时候以前都是通过在activity中创建实例后传进来,但是这样子不太美观,所以应然而生在其他地方获取到主线程的handler是尤为重要。 具体代码如下:... -
一分钟入门kotiln协程,线程切换
2020-05-25 07:03:51) { //上下文切换到IO主线程 GlobalScope.launch(Dispatchers.IO) { Log.i(TG, "Dispatchers.IO isMainThread ${isMain()}")//输出false //上下文切换到主线程 GlobalScope.launch(Dispatchers.Main){ Log.i(TG, ...