探索flutter框架开发的app在移动应用市场的潜力与挑战
948
2022-10-25
线程中的常见方法
start方法和run方法
$start()$方法用来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到$cpu$时间片,就开始执行$run()$方法。而直接调用$run()$方法,仅仅只是调用了一个类里的方法,其本质上还是在当前线程中执行的,因此只有使用$start()$方法来调用$run()$方法才能实现真正的多线程。
示例代码
@Slf4j(topic = "c.Test4") public class Test4 { public static void main(String[] args) { Thread t1 = new Thread("t1"){ @Override public void run() { log.debug("running"); } }; t1.run(); } } //16:52:27.595 [main] DEBUG c.Test4 - running
上述代码是直接调用的$run()$方法。可以看到打印信息里,是$main$线程执行了这个方法。
@Slf4j(topic = "c.Test4") public class Test4 { public static void main(String[] args) { Thread t1 = new Thread("t1"){ @Override public void run() { log.debug("running"); } }; t1.start(); } } //16:58:22.740 [t1] DEBUG c.Test4 - running
而如果使用$start()$方法启动,才是真正的由$t1$线程执行的$run$方法。
注意
需要注意的是,当$Thread$对象调用了$start()$方法后,就会进入就绪状态,处于就绪状态时无法再调用$start()$方法,否则就会抛出$IllegalThreadStateException$异常,如下代码所示
@Slf4j(topic = "c.Test4") public class Test4 { public static void main(String[] args) { Thread t1 = new Thread("t1"){ @Override public void run() { log.debug("running"); } }; t1.start(); t1.start(); } }
sleep方法与yield方法
sleep
调用$sleep()$方法会让当前线程从$Running$状态变成$Time Waiting$状态(阻塞) 其它线程可以使用$interrupt$方法打断正在睡眠的线程,此时$sleep$方法会抛出InterruptedException 睡眠结束后的线程未必会立刻得到执行 建议用$TimeUnit$的$sleep$代替$Thread$的$sleep$来获得更好的可读性
示例代码
@Slf4j(topic = "c.Test5") public class Test5 { public static void main(String[] args) { Thread t1 = new Thread("t1"){ @Override public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } }; t1.start(); log.debug("t1 state {}", t1.getState()); //让主线程休眠500ms try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } log.debug("t1 state {}", t1.getState()); } } //17:13:21.729 [main] DEBUG c.Test5 - t1 state RUNNABLE //17:13:22.245 [main] DEBUG c.Test5 - t1 state TIMED_WAITING
上述代码中,首先启动$t1$线程,此时打印线程的状态应该是处于$RUNNABLE$状态,而让主线程休眠是防止主线程先执行打印,但是还未进入到$sleep()$状态。当执行到$run()$里边的$sleep$方法时,线程进入$TIMED WAITING$状态
@Slf4j(topic = "c.Test6") public class Thread6 { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread("t1") { @Override public void run() { try { log.debug("enter sleep"); Thread.sleep(2000); } catch (InterruptedException e) { log.debug("wake up"); e.printStackTrace(); } } }; t1.start(); Thread.sleep(1000); log.debug("interrupt t1"); //被唤醒 t1.interrupt(); } }
TimeUnit.SECONDS.sleep(1);//该语句作用是睡眠一秒
yield
调用$yield$会让当前进程从$Running$进入到$Runnable$就绪状态,然后调度执行其他线程 具体的实现依赖于操作系统的任务调度器,(即当任务调度器中没有其他任务时,即使让出$cpu$,也会继续执行该线程) $sleep$执行后是进入阻塞状态,此时睡眠时间不结束,就不会分配$cpu$给该线程,但是$yield$是进入就绪状态,即如果没有其他线程需要执行,那么还会给该线程分配时间片,这是$sleep$和$yield$的最大区别
线程优先级
线程优先级会提示调度器优先调度该线程,但它仅仅是一个提示,调度器可以忽略他如果$cpu$比较忙,那么优先级高的会获得更多的时间片,可$cpu$空闲时,优先级几乎没有
sleep的应用-防止cpu占用100%
在没有利用$cpu$来计算时,不要让$while(true)$空转浪费$cpu$,这时可以可以使用$yield$或者$sleep$来让$cpu$的使用权交给其他程序
while (true) { try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } }
可以使用$wait$或者条件变量达到类似的效果不同的是后两者都需要加锁,并且需要相应的唤醒操作,一般适用于要进行同步的场景$sleep$适用于无需锁同步的场景
join方法
以下程序的打印结果
@Slf4j(topic = "c.Test6") public class Test6 { static int r = 0; public static void main(String[] args) { test(); } private static void test() { log.debug("开始"); Thread t = new Thread("t1") { @Override public void run() { log.debug("开始"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } log.debug("结束"); r = 10; } }; t.start(); log.debug("r的值是{}", r); log.debug("结束"); } }
有实效的等待
$join(毫秒)$方法里可以有一个参数是传入等待的时间,如果线程执行时间大于等待时间,则等待时间到了之后,就会停止等待。如果线程执行时间小于等待时间,则线程执行完毕之后,等待也会跟着结束。不会把设置的等待时间过完。
interrupt方法
打断$sleep, wait, join$的线程,即打断阻塞状态的线程打断$sleep$的线程,会清空打断状态
@Slf4j(topic = "c.Test7") public class Test7 { public static void main(String[] args) throws InterruptedException { Thread t = new Thread("t1"){ @Override public void run() { log.debug("sleep..."); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } } }; t.start(); Thread.sleep(1000); log.debug("interrupt"); t.interrupt(); log.debug("打断标记: {}", t.isInterrupted()); } }
打断正常运行的线程,不会清空打断状态
因此我们可以在线程中判断打断标记,来决定是否被打断,以及执行被打断之前的收尾工作。
@Slf4j(topic = "c.Test8") public class Test8 { public static void main(String[] args) throws InterruptedException { Thread t = new Thread("t1"){ @Override public void run() { while (true) { if (Thread.currentThread().isInterrupted()) { log.debug("线程被打断了"); break; } } } }; t.start(); Thread.sleep(1000); log.debug("interrupt"); t.interrupt(); } }
守护线程
默认情况下,$java$需要等待所有线程都运行结束,才会结束。有一种特殊的线程叫做守护线程,只要其他非守护线程运行结束了,即使守护线程的代码没有执行完毕,也会强制结束。
@Slf4j(topic = "c.Test10") public class Test10 { public static void main(String[] args) throws InterruptedException { Thread t = new Thread("t1") { @Override public void run() { while (true) { } } }; //设置线程为守护线程 t.setDaemon(true); t.start(); Thread.sleep(1000); log.debug("主线程结束"); } }
如果不把$t$设置为守护线程,则因为线程内部的死循环,导致程序不会结束运行。
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~