社区网站模板,简单网站开发流程,网页设计教程孟宪宁,物联网网络架构由 和 组成所属专栏#xff1a;Java学习 1. 线程的开启
start和run的区别#xff1a;
run#xff1a;描述了线程要执行的任务#xff0c;也可以称为线程的入口
start#xff1a;调用系统函数#xff0c;真正的在系统内核中创建线程#xff08;创建PCB#xff0c;加入到链… 所属专栏Java学习 1. 线程的开启
start和run的区别
run描述了线程要执行的任务也可以称为线程的入口
start调用系统函数真正的在系统内核中创建线程创建PCB加入到链表中此处的start会根据不同的系统分别调用不同的api创建好之后的线程再单独去执行run所以说start的本质是调用系统api系统的api会在内核中创建线程
start执行的速度是比较快的一旦 start 执行完毕新线程就会开始执行调用start的线程main线程也会继续执行 在Java中一个Thread对象只能对应到一个系统中的线程在start中就会根据线程的状态来判断如果Thread对象没有start此时的状态就是一个new状态可以顺利调用start如果已经调用过start就会进入到其他状态只要不是new状态都会抛出异常 2. 线程的终止
当线程B正在运行时如果发生了特殊情况需要终止掉线程有两种实现方式 通过共享的标记来进行沟通调用interrupt()方法来通知 先来看使用自定义的isQuit作为标志位的例子
public class ThreadDemo4 {private static boolean isQuit false;public static void main(String[] args) throws InterruptedException {Thread thread new Thread(){Overridepublic void run() {while (!isQuit){System.out.println(thread);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println(thread线程终止了);}};thread.start();Thread.sleep(1000);System.out.println(main线程尝试终止thread线程...);isQuit true;}
} 这时就有一个问题需要注意
如果isQuit不用static修饰改为main方法里的局部变量可行不可行ans:肯定是不可行的 这就涉及到了lambda表达式变量捕获的问题了
变量捕获是lambda表达式/匿名内部类中的一个语法规则isQuit和lambda表达式定义在一个作用域中此时lambda内部是可以访问到外部和lambda同一个作用域中的变量的Java中的变量捕获是有一个特殊要求的要求捕获的变量必须是final或事实final虽然不是final修饰但是后面没有更改
就如上面的代码中isQuit后面是被修改了的所以就违反了语法规则
刚开始的形式为什么可以写在外面就是外部类的成员变量内部类本来就是可以访问外部类的
再来看interrupted的例子
Thread类里面有一个boolean类型的成员变量interrupted初始状态为false一旦外面有线程调用interrupt方法就会设置标志位为true
public class ThreadDemo5 {public static void main(String[] args) throws InterruptedException {Thread thread new Thread(()-{Thread currentThread Thread.currentThread();while (!currentThread.isInterrupted()){System.out.println(thread);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});thread.start();Thread.sleep(1000);//主线程中控制thread被终止设置标志位thread.interrupt();}
}
看起来是和自定义的标志位一样的但是运行之后就会发现出现了异常 由于在循环中判断和打印的操作太快了整个循环的时间都是花在sleep方法里的当main中调用interrupt时大概率线程thread是在sleep中的此时Interrupt就不仅能设置标志位还可以唤醒thread线程就会抛出InterruptedException异常catch中捕获到异常也是做了抛出处理就交给了JVM程序就异常终止那么把处理异常的方式更改一下试试 这时就会发现while循环中的代码一直在执行
这里的原因是当sleep等阻塞函数被唤醒之后就会清空刚刚设置的标志位这时interrupted就一直是初始状态也就导致了死循环如果需要结束循环可以把刚刚的异常处理直接改为break。
Java中终止线程的方式A线程希望B线程能够终止B线程收到这样的请求之后就可以自行决定是否是立即终止稍后终止还是直接无视 如果B线程想要无视Acatch中就什么也不做B线程就继续执行sleep清除标志位如果B线程想要立即结束就在catch中直接break或return如果B线程想要稍后再终止就可以在catch中添加其他的逻辑释放资源清理数据提交结果...这些完成之后再break/return. 3. 线程的等待
在之前提到过操作系统针对多个线程的执行是一个“随机执行抢占式调度”的过程哪条线程先执行和先结束是不确定的不过可以通过使用线程等待来决定哪条线程先结束也就是让最后结束的线程等待先结束的线程此时后结束的线程就进入了阻塞状态 例如在a线程中调用b.join()就是让a线程等待b线程先结束然后a再继续执行
public class ThreadDemo6 {public static void main(String[] args) {Thread thread1 new Thread(() - {for (int i 0; i 3; i) {System.out.println(thread1);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println(thread1结束了);});thread1.start();System.out.println(main线程开始等待);try {thread1.join();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(main线程结束等待);}
} 上面的代码是thread1线程先执行然后main线程开始等待进入阻塞状态如果说修改为先让thread1线程结束main线程再开始等待会如何呢 此时join并没有发生阻塞join方法就是确保被等待的线程能够先结束如果已经结束了就没有等待的必要了
此外任何线程都可以等待别的线程而且可以等待多个线程或者是多个线程之间互相等待
public class ThreadDemo7 {public static void main(String[] args) throws InterruptedException {Thread t1 new Thread(()-{for(int i 0;i 3;i){System.out.println(线程t1执行...);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println(线程t1结束);});Thread t2 new Thread(()-{for(int i 0;i 3;i){System.out.println(线程t2执行...);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println(线程t2结束);});t1.start();t2.start();System.out.println(main线程开始等待...);t1.join();t2.join();System.out.println(main线程结束等待);}
} 这就是main线程同时等待两个线程的例子关于两个join的顺序其实没有区别最终都是等待了4s 在main等待t1和t2同时t2也可以等待t1 这样就把原来t1和t2并发执行修改为了t1先执行
main线程也可以被其他线程等待不过写法不同的是需要先获取main线程的引用
public class ThreadDemo8 {public static void main(String[] args) throws InterruptedException {Thread mainThread Thread.currentThread();Thread thread new Thread(()-{System.out.println(thread等待main线程);try {mainThread.join();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(结束等待);});thread.start();Thread.sleep(1000);System.out.println(main线程结束);}
} 在上面使用的join方法中由于是没有传入参数的就表示被等待的线程只要没有执行完就会一直等待这种方式肯定是不好的如果被等待的线程出现问题了就会使这个等待操作一直进行所以就有了传参的版本但上面列举的第三个高精度的一般也用不到
4. 线程的休眠
之前一直用的Thread.sleep()这个操作就是让调用的线程阻塞等待一定时间的线程执行sleep之后就会使这个线程不参与CPU的调度把CPU的资源让出来给其他线程使用在开发时如果发现某个线程的CPU占用率过高就可以通过sleep来改善虽然说线程的优先级也可以影响但比较有限