当前位置: 首页 > news >正文

县网站建设检查情况汇报31省份新增40例本土确诊

县网站建设检查情况汇报,31省份新增40例本土确诊,做网站学哪方面知识,浙江手机网站建设文章目录 一、第一个多线程程序1.Jconsole观察线程2.线程休眠-sleep 二、创建线程三、Thread类及常见方法1. Thread 的常见构造方法2. Thread 的几个常见属性3. 启动线程 - start4. 中断线程5. 等待一个线程 四、线程状态五、线程安全问题(synchronized)#xff08;重点#… 文章目录 一、第一个多线程程序1.Jconsole观察线程2.线程休眠-sleep 二、创建线程三、Thread类及常见方法1. Thread 的常见构造方法2. Thread 的几个常见属性3. 启动线程 - start4. 中断线程5. 等待一个线程 四、线程状态五、线程安全问题(synchronized)重点1. 观察线程不安全问题2.线程安全问题分析3.线程安全问题的原因4.解决线程不安全问题5.synchronized 关键字6.总结 六、内存可见性问题(volatile)1.观察内存不可见问题2.问题分析3.volatile关键字4.总结 七、wait 和 notify1.wait 和 sleep 之间的区别 前言平时我们敲的代码当点击运行程序的时候就会先创建出一个java进程。这个进程中就包含了至少一个线程。这个线程也叫做主线程。也就是负责执行main方法的线程. 一、第一个多线程程序 Java中为了实现多线程提供了thread类。 创建一个类来继承thread重写thread中的run方法这里的run方法就相当于线程的入口当程序运行后此线程要做什么事情都是通过run方法来实现的。 创建完后我们要在main中来调用这个myThread线程这里通过start方法来启动线程。start会调用系统api在系统内核中把线程对应的pcb给创建出来并管理好由此新的线程就会参与调度了 为什么不用 myThread.run() ? run只是上面的入口方法(普通的方法)。并没有调用系统 api也没有创建出真正的线程来.不会执行并发操作只是按顺序执行代码。 class myThread extends Thread{Overridepublic void run() {while (true) {System.out.println(hello Thread);}} } public class Main {public static void main(String[] args) {Thread myThread new myThread();myThread.start();//myThread.run();while (true) {System.out.println(Hello world!);}} }主线程和新线程是并发执行的关系. 操作系统怎么调度? 每个线程,都是一个独立的执行流.每个线程都可以执行一段代码.多个线程之间是并发的关系~~ 1.Jconsole观察线程 当创建出线程之后,也是可以通过一些方式,直观的观察到的~~ idea 的调试器 jconsole 此为官方在 jdk 中给程序猿提供的一个调试工具。 我们可以按照之前 jdk 下载的路径在 bin 目录下找到 jconsole.exe 先运行java程序然后点击 jconsole.exe ,就会发现我们用 java 写的多线程正在运行。 这里就列出了当前进程中所有的线程不仅仅是主线程和自己创建的新线程. 剩下的线程都是JVM里自带的负责完成一些其他方面的任务。 2.线程休眠-sleep 这里先介绍Thread类中的一个sleep方法顾名思义就是让线程暂时睡一会、暂时停滞 不进行工作。 在sleep()中我们可以设置休眠多长时间其单位是ms。 但是 sleep 本身也是存在一些误差的. 设置 sleep(1000) 不一定是精确的就休眠 1000ms会存在误差原因是线程的调度也是需要时间的。 冷知识sleep(0) 是让当前线程放弃 CPU 重新去队列中排队准备下一轮的调度。由于这个操作看起来比较抽象因此java有封装了一个方法yield和sleep(0)功能一样。 二、创建线程 创建线程的方式还有很多包括 1.创建一个类,继承自 Thread.重写run方法. 已介绍 2.创建一个类,实现Runnable.重写run方法. 3.继承 Thread ,重写run,基于匿名内部类. 4.实现 Runnable ,重写run,基于匿名内部类. 5.使用 lambda表达式,表示run方法的内容.(推荐常用) 6.基于Callable 7.基于线程池 上述第一种方法已介绍接着介绍第二种方法。 创建一个类实现 Runnable.重写 run 方法. Runnable这里,则是分开了把要完成的工作放到Runnable 中,再让Runnable和Thread 配合. 这里是把要完成的工作放到 Runnable 中再让 Runnable 和 Thread 配合. class myRunnable implements Runnable {Overridepublic void run() {while (true) {System.out.println(hello world);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}} } public class Demo2 {public static void main(String[] args) throws InterruptedException {myRunnable myRunnable new myRunnable();Thread i new Thread(myRunnable);i.start();while (true) {System.out.println(hello main);Thread.sleep(1000);}} }继承 Thread ,重写run,基于匿名内部类 1.创建了一个子类,这个子类继承自Thread. 但是,这个子类,是没有名字的!!(匿名)另一方面,这个类的创建,是在Demo3这个类里面. 2.在子类中,重写了run方法. 3创建了该子类的实例.并且使用t这个引用来指向. public class Demo3 {public static void main(String[] args) throws InterruptedException {Thread t new Thread() {Overridepublic void run() {while (true) {System.out.println(hello world);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}};t.start();while (true) {System.out.println(hello main);Thread.sleep(1000);}} }实现 Runnable ,重写run,基于匿名内部类. 1.创建了一个Runnable的子类(类,实现 Runnable) 2.重写了run方法 3.把子类,创建出实例,把这个实例传给Thread的构造方法. public class Demo4 {public static void main(String[] args) throws InterruptedException {Thread t new Thread(new Runnable() {Overridepublic void run() {while (true) {System.out.println(hello world);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}});t.start();while (true) {System.out.println(hello main);Thread.sleep(1000);}} } 使用 lambda表达式,表示run方法的内容.(推荐常用) lambda表达式,本质上就是一个匿名函数”。这样的匿名函数,主要就可以用来作为回调函数来使用. 经常会用到回调函数的场景: 服务器开发:服务器收到一个请求,触发一个对应的回调函数.图形界面开发:用户的某个操作,触发一个对应的回调. 类似于lambda这样的写法,本质上并没有新增新的语言特性,而是把以往能实现的功能,换了一种更简洁的方式来编写.(新瓶装旧酒-语法糖) public class Demo5 {public static void main(String[] args) throws InterruptedException {Thread t new Thread(() - {while (true) {System.out.println(hello world);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.start();while (true) {System.out.println(hello main);Thread.sleep(1000);}} }三、Thread类及常见方法 1. Thread 的常见构造方法 我们可以给创建的线程进行命名 public class Demo5 {public static void main(String[] args) throws InterruptedException {Thread t new Thread(() - {while (true) {System.out.println(hello a);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}},THREAD);t.start(); /* while (true) {System.out.println(hello b);Thread.sleep(1000);}*/} }把主线程中的循环注释掉当程序运行时查看 jconsole发现只剩HTREAD线程main线程没有了。因为main已经执行完了 2. Thread 的几个常见属性 isDaemon是否后台线程 JVM会在一个进程的所有非后台线程结束后才会结束运行。 创建的线程默认是前台线程。可以通过setDaemontrue显式的设置成后台。 islive是否存活 Thread对象的生命周期,并不是和系统中的线程完全一致的!! 一般,都是Thread对象,先创建好然后手动调用start内核才真正创建出线程。 消亡的时候,可能是thread对象先结束了生命周期(没有引用指向这个对象)。也可能是 thread对象还在,内核中的线程把run执行完了就结束了。 3. 启动线程 - start start 方法是系统中真正创建出线程。此方法是调用系统中的 api 完成线程的创建 如何创建的呢 在内核中完成创建pcb并把pcb加入到对应的链表中。 start方法本身的执行是一瞬间就完成的.只是告诉系统你要创建个小线程出来。调用start完毕之后代码就会立即继续执行 start 后续的逻辑。 4. 中断线程 在线程执行 run 方法的时候不完成是不会结束的。但有时候因为特殊原因需要终止一个正在执行的程序该如何操作嘞 常见的方式有以下两种 程序猿手动设定标志位调用 interrupt() 方法来通知 —设定标志位 public class Demo8 {public static boolean isQuit false;public static void main(String[] args) throws InterruptedException {//boolean isQuit false;Thread t new Thread(()-{while (!isQuit) {System.out.println(hello world);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.start();//主线程执行一些其他逻辑后要让 t 线程结束.Thread.sleep(3000);//这个代码就是在修改前面设置的标志位.isQuit true;System.out.println(把 t 线程中断);} }以上代码就是通过设定标志位来终止线程的。 思考 如果我们现在把 isQuit 定义在 main 内代码就会开始报错这是为什么呢 是因为 lambda 所触发的“变量捕获”机制。变量捕获这里有个限制要求捕获的变量得是final (至少是看起来是final)我们都知道被final修饰后面是不可以修改的。 如果这个变量想要进行修改,就不能进行变量捕获了~~因此上述代码就会进行报错。 什么是变量捕获lambda内部看起来是在直接访问外部的变量,其实本质上是把外部的变量给复制了一份到 lambda里面. 为啥java这么设定?? java是通过复制的方式来实现变量捕获如果外面的代码要对这个变量进行修改,就会出现一个情况:外面的变量变了里面的没变~~代码更容易出现歧义. —使用 interrupt()方法 使用 Thread.interrupted() 或者 Thread.currentThread().isInterrupted() 代替自定义标志位. public class Demo9 {public static void main(String[] args) throws InterruptedException {Thread t new Thread(()-{//Thread.currentThread()其实就是 t//这里不能用t是因为lambda表达式还没构造完t因此看到不到。while (!Thread.currentThread().isInterrupted()) {System.out.println(hello Thread);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});t.start();Thread.sleep(3000);//把上述的标志位设置为truet.interrupt();} }执行程序后并没有让我们的程序结束而是出现了一个异常。 我们可以理解成sleep被唤醒。 一个程序可能处于正常运行状态也可能处于Sleep状态也可以称为阻塞状态意思就是代码暂时不执行了。 重点线程在阻塞过程中如果其他线程调用interrupt方法就会立即唤醒一个正在被阻塞的程序。但是sleep在被唤醒的同时也会自动清除前面设置的标志位!! 唤醒后会给程序猿留下更多的操作空间. 此时如果想添加其他的操作就可以在 catch 中编写新代码。如果想直接终止掉程序只需要在 catch 中屏蔽掉异常另加一个 break 即可。 这几种处理方式都是比较温和的方式。另一个线程提出请求本线程自己决定是否要终止。更激进的做法是这边提出请求那边立即就结束线程根本来不及反应。完全不考虑本线程的实际情况,就可能会造成一些负面的影响~ 5. 等待一个线程 多个线程是并发执行的.具体的执行过程,都是由操作系统负责调度的!!!操作系统调度线程的过程,是随机的。无法确定线程执行的先后顺序。因此等待线程就是一种规划 线程结束顺序 的手段。 回过头来再解释一下阻塞状态顾名思义就是代码暂时不继续执行了该线程暂时不去CPU上参与调度 join 的阻塞则是“死等” - 不见不散的那种。例如t.join()表示t程序如果没执行完则阻塞t.join所在的程序。 在计算机中更推荐有时间限制的版本 join(long milis)留有余地。只要时间到了不管来没来都不等了。 join能否被interrupt唤醒?? 答案是可以的!! sleep, join, wait…产生阻塞之后,都是可能被interrupt方法唤醒的这几个方法都会在被唤醒之后自动清除标志位(和sleep类似的) public class Demo10 {public static void main(String[] args) {//线程bThread b new Thread(()-{for (int i 0; i 5; i) {System.out.println(hello b);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(b 结束了);});//线程aThread a new Thread(() -{for (int i 0; i 3; i) {System.out.println(hello a);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}try {//这里运用 b.join 来堵塞a程序//如果 b 此时还没执行完毕b.join 就会产生阻塞的情况。//这里的join也会产生受查异常需要try-catchb.join(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(a 结束了);});b.start();a.start();} }四、线程状态 之前谈到过线程的两个状态一个是阻塞状态另一个是就绪状态。这两个状态都是系统所设定的两个状态。在java中把上述状态又进一步的细分出了6个状态。 NEW: 安排了工作, 还未开始行动RUNNABLE: 可工作的. 又可以分成正在工作中和即将开始工作. 正在工作中线程正在 CPU 上运行即将开始工作线程正在排队随时可以去 CPU 上执行 BLOCKED: 这几个都表示排队等着其他事情因锁产生的阻塞后文后讲到WAITING: 这几个都表示排队等着其他事情因调用wait产生阻塞后文会讲到TIMED_WAITING: 这几个都表示排队等着其他事情(用 sleep(millis) 和 join(millis) 带时间参数的版本都会触发)TERMINATED: 工作完成了. public class Demo1 {public static void main(String[] args) throws InterruptedException {Thread t new Thread(() - {try {Thread.sleep(10000);} catch (InterruptedException e) {e.printStackTrace();}//System.out.println(执行完毕!!);});//安排了线程但还未工作System.out.println(状态1 t.getState());t.start();//开始工作正在执行中System.out.println(状态2 t.getState());Thread.sleep(1000);//排队等待中 System.out.println(状态3 t.getState());t.join();//线程结束工作完成了System.out.println(状态4 t.getState());} } /*输出状态1NEW状态2RUNNABLE状态3TIMED_WAITING状态4TERMINATED */五、线程安全问题(synchronized)重点 1. 观察线程不安全问题 观察下列代码 static class Counter {public int count 0;public void increase() {count;} } public static void main(String[] args) throws InterruptedException {final Counter counter new Counter();Thread t1 new Thread(() - {for (int i 0; i 50000; i) {counter.increase();}});Thread t2 new Thread(() - {for (int i 0; i 50000; i) {counter.increase();}});t1.start();t2.start();t1.join(); //等待线程t1结束t2.join(); //等待线程t2结束System.out.println(counter.count); } /*输出64821 //输出任意数小于10W */我们发现如果按照正常逻辑来两个线程针对同一个变量进行循环自增各自增 5w 次预期最终结果应该是 10w但实际上并不是 说明我们的代码有 bug 这里的 bug 是一个非常广义的概念只要是实际运行效果和预期效果(需求效果)不一致就可以称之是一个 bug. 在多线程下发现由于多线程执行导致的 bug统称为“线程安全问题”如果某个代码在单线程下执行没有问题多个线程下执行也没问题则称为“线程安全”反之也可以称为“线程不安全”。 2.线程安全问题分析 那为啥会出现上述的 bug 呢 问题出现在这里count 看上去是一行代码实际上在CPU角度上来说是执行了三步操作。 把内存中的数据加载到CPU的寄存器中(load)把寄存器中的数据进行1 (add)把寄存器中的数据写回到内存中(save) 如果上述的操作,在两个线程,或者更多个线程并发执行的情况下就可能会出现问题!! 接下来我们可以通过时间轴具体看一下问题出现在哪。 预期情况下t1、t2 线程串行执行t1完事后 t2 才开始。执行结果为正确。 若通常情况下CPU针对这些线程的调度是按照抢占式的方式进行调度的因此这些命令的执行顺序可能会存在很多中方式。 因此这两组执行操作的相对顺序会存在很大差异 取其中的一个执行方法为例虽然是自增两次但是由于两个线程并发执行就可能在一定的执行顺序下导致运算的中间结果就被覆盖了。 在这5w次的循环过程中有多少次这俩线程执行是串行的”有多少次会出现覆盖结果的不确定线程的调度是随机的抢占式执行的过程。 此处这里的结果就会出现问题而且得到的这个错误值一定是小于10w。 因此很多代码都会涉及到线程安全问题不仅仅只是 count. 3.线程安全问题的原因 1.[根本原因]多个线程之间的调度顺序是“随机的”操作系统使用抢占式执行的策略来调度线程。 和单线程不同的是多线程下代码的执行顺序产生了更多的变化。 以往只需要考虑代码在一个固定的顺序下执行执行正确即可。现在则要考虑多线程下N种执行顺序下代码执行结果都得正确。 2.多个线程同时修改同一个变量.容易产生线程安全问题. 代码的结构 3.进行的修改不是“原子的”。 此为切入线程安全问题最主要的手段。 如果修改操作能够按照原子的方式来完成此时也不会有线程安全问题。 例如上述例子中count 操作不是原子的。需要考虑到CPU 中的三步操作。 4.内存可见性引起的线程安全问题。后文讲解 5.指令重排序引起的线程安全问题。后文讲解 4.解决线程不安全问题 为了解决线程不安全问题我们引入加锁这不操作。 其原理就相当于把一组操作打包成一个“原子的操作”。但是与事务的那个原子不同。事务原子性主要体现在“回滚”的操作。而这里的原子则是通过锁进行“互斥”相当于我这个线程工作的时候其他线程无法工作。 通过这个锁就限制了同一时刻只有一个线程能使用当前资源。 此时当t1线程进行访问时就会对increase方法加锁。若果在t1加完锁后t2又来试图访问加锁t2就会阻塞等待这个阻塞一直会持续到t1把锁解放后t2才能够加锁成功。 按照上述加锁方法就相当于把 increase 方法中的 count 操作“打包成一个原子”。 因此就实现了把“穿插执行”变成了串行执行。 这里提出个问题通过加锁使并发执行变为串行化执行此时多线程还有存在的意义吗 必然是有的我们要知道串行化执行针对的是 count 操作也就是线程中的 counter.increase() 方法但是线程中不仅仅包含了这一句代码还有 for 循环因此线程之间还是存在并发执行的操作也就是说多线程还是有意义的。 5.synchronized 关键字 java 给我们提供的加锁的方式关键字是搭配 代码块 来完成的~进入代码块就加锁出了代码块就解锁。 synchronized 进行加锁 解锁其实是以对象为维度进行展开的!!! 以下是 synchronized 锁的两种用法的一种是第二种的简化直接修饰方法就相当于对 this 加锁。 这里非常关键**只要两个线程针对同一个对象进行加锁就会出现 锁竞争/锁冲突一个线程加锁成功另一个线程阻塞等待。 **这里的锁对象是任意对象都可以。锁对象和要访问的对象没有必然关联 反之两个线程针对不同对象进行加锁就不会出现锁竞争。会出现“穿插执行”的线程不安全问题 线程安全案例 线程不安全案例 这里面锁对象是不同的此时就不会出现有阻塞等待也不会有两个线程按照串行的方式执行。 6.总结 利用synchronized 锁的时候代码执行流程如下。 六、内存可见性问题(volatile) 1.观察内存不可见问题 观察下面代码 public class Demo1 {public static int isQuite 0;public static void main(String[] args) {Thread t1 new Thread(() - {while (isQuite 0) {;}System.out.println(程序t1执行结束);});Thread t2 new Thread(() - {Scanner scanner new Scanner(System.in);isQuite scanner.nextInt();});t1.start();t2.start();} }我们所期望的代码逻辑t1始终在进行while循环t2则是要让用户通过控制台输入一个整数作为isQuit的值。当用户输入的仍然是0的时候,t1线程继续执行。如果用户输入的非0则t1线程就应该循环结束。 而实际上即使t2线程修改了isQuite值代码也不会结束而是陷入无限循环状态。 问题出现了当输入非0值的时候已经修改了isQuit的值了。但是t1线程仍然在继续执行。这就是不符合预期的也是bug。 2.问题分析 为什么会出现上述 bug 呢 其根本原因就是 java 编译器的优化机制。 当我们写出来的代码程序运行时java编译器和 jvm可能会对代码做出一些“优化”。 编译器优化本质上是靠代码智能的对你写的代码进行分析判断进行调整。这个调整过程大部分情况下都是ok都能保证逻辑不变但是如果遇到多线程了此时的优化可能就会出现差错!!! 会使用程序中原有的逻辑发生改变。 对于上述代码中的 isQuite 0 本质上其实是两步指令 第一步加载load读取到内存中的数据。 -读内存操作速度非常慢第二步放在寄存器中操作与0进行比较是否相等 -寄存器操作速度极快 此时编译器/JVM就发现这个逻辑中代码要反复的快速的读取同一个内存的值。并且这个内存的值每次读出来还是一样的~~ 因此编译器就做出一个大胆的决策直接把 load 操作优化掉了只是第一次执行load 。后续都不再执行load直接拿寄存器中的数据进行比较了。 但是万万没想到程序猿有点不讲武德搞偷袭在另一个线程 t2 中把内存中的 isQuite 给改了! 另一个线程中并没有重复读取isQuit的值而是只读寄存器中的值。因此 t1线程就无法感知到 t2 的修改。因此也就出现了上述内存不可见问题。 3.volatile关键字 编译器优化在上述代码中好心办坏事算是编译器的 bug 吧。为了弥补这样的 bug volatile就由此诞生喽。 把volatile用来修饰一个变量之后编译器就明白这个变量是易变的就不能按照上述方式把读操作优化到读寄存器中.(编译器就会禁止上述优化)于是就能保证t1在循环过程中始终都能读取内存中的数据 volatile本质上是保证变量的内存可见性.(禁止该变量的读操作被优化到读寄存器中). 4.总结 编译器优化后的 java 线程只能从寄存器中读数据 加上 volatile 后就可以保证内存的可见性非原子性。从而线程就可以正常从内存中读数据。 编译器优化其实是一个玄学问题。啥时候进行优化啥时候不优化咱们作为外行有些摸不到规律~~ 代码稍微改动一下可能就不会触发上述优化~~ 比如说在while内加上个sleep就不会触发优化机制。这里不给演示了 七、wait 和 notify wait 和 notify 也是多线程编程中的重要工具。多线程调度是随机的有时候希望多个线程能够按照咱们规定的顺序来执行完成线程间的配合工作。由此wait 和 notify就闪亮登场了。wait 和 notify 通常都是搭配成对使用。 wait等待. notify通知. 我们可以按照字面意思来理解。 wait 和 notify 都是由Object所提供的方法。因此随便找个对象都可以使用 wait 和 notify. 在尝试使用 wait 的时候编译器出现提示 大概意思就是在 wait 运行阻塞时可能被 interrupted 给唤醒需要捕获异常。 当我们添加完 try-catch 运行后编译器报错非法监视器状态这里的监视器是指 synchronized 可以称为监视器锁 这里为啥会报错呢 wait 在执行的时候会做三件事 解锁。 object.wait 会尝试针对object 对象解锁。阻塞等待。当被其他线程唤醒之后就会尝试重新加锁。加锁成功 wait 执行完毕继续往下执行其他逻辑。 在锁中无非就两种状态要么加锁要么解锁。 这里 wait 操作要解锁的前提那就是把 wait 加锁。 核心思路先加锁然后在synchronized里头再进行wait 在运行过程中我们通过 t1.getState() 观察线程状态发现此线程正在 WAITING阻塞等待中。 这里的 wait 就是一直阻塞到其他线程进行 notify 了。 notify 使用方法和 wait 差不多。直接上代码。 public class Demo2 {//public static Object locker;public static void main(String[] args) throws InterruptedException {Object locker new Object();Thread t1 new Thread(() - {synchronized (locker) {System.out.println(t1 wait 开始);try {locker.wait();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(t1 wait 结束);}});t1.start();Thread.sleep(1000);System.out.println(t1.getState());Thread t2 new Thread(() - {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (locker) {System.out.println(t2 notify 开始);locker.notify();System.out.println(t2 notify 结束);}});t2.start();} }几个注意事项: 要想让 notify 能够顺利唤醒 wait就需要确保 wait 和 notify 都是使用同一个对象调用的. wait 和 notify 都需要放到synchronized之内的。虽然 notify 不涉及解锁操作但是 java 也强制要求 notify 要放到 synchronized 中(系统的原生api中,没有这个要求) 如果进行 notify 的时候另一个线程并没有处于wait状态。此时, notify 相当于空打一炮不会有任何副作用 小技巧: 如果就想唤醒某个指定的线程。就可以让不同的线程使用不同的对象来进行wait 。想唤醒谁就可以使用对应的对象来 notify。 1.wait 和 sleep 之间的区别 sleep是有一个明确的时间的。到达时间自然就会被唤醒。也能提前唤醒使用interrupt就可以。 wait 默认是一个死等一直等到有其他线程notify。wait 也能够被 interrupt 提前唤醒。 notify 的唤醒是顺理成章的唤醒。唤醒之后该线程还需要继续工作后续还会进入到 wait 状态。 interrupt 的唤醒就相当于告知线程要结束了。接下来线程就要进入到收尾工作了。 因此协调多个线程之间的执行顺序当然还是优先考虑使用 wait notify 而不是 sleep 。
http://www.sczhlp.com/news/186697/

相关文章:

  • 电脑网站首页设计用html5做的网站代码
  • 网站外链推广工具自己做网站的软件下载
  • 小说网站建设采集浙江大成建设集团有限公司网站
  • 钢材贸易网站建设网站优化排名如何做
  • 制作网站的公司还能赚钱吗做任务转比特币的网站
  • 一个专门做海鲜的网站南京公司网站建设
  • 信誉好的河南网站建设网站建设实例教程
  • 佛山行业网站建设做外贸的网站平台有哪些
  • 常用的sql语句
  • SQL常用语句分类及示例
  • Docker安装MQTT
  • Ubuntu Linux双网卡实现在校园网环境下的网络共享
  • 做企业网站和邮箱建设社区网站有什么借鉴之处
  • 哪些网站设计的好仿站酷网站模板
  • 网站外包反钓鱼网站联盟
  • seo整站优化网站建设国外wordpress主题风险
  • 有个性的个人网站一步步教你为网站开发android客户端
  • 站长工具网站备案查询建设银行互联网站
  • 如何自己做自己的网站wordpress怎么启用sll证书
  • 天元建设集团有限公司网站厦门移动网站建设哪家专业
  • 北京市做网站工业产品设计图片欣赏
  • 六安网站制作人才招聘沧州品牌网站建设
  • 鄂北局网站建设者风采织梦五彩婚纱源码网_婚庆策划网站php源码
  • 企业网站实名认证怎么做长沙美容网站建设
  • 南昌网站排名优化费用工程建设部
  • 中国软件开发公司排行seo外包公司报价
  • 网站建设营销解决方案宁波网站建设地址
  • 制作网站商城做一个网站的预算
  • 建设网站功能seo如何做网站建设
  • 颜色搭配的网站wordpress 3.2 漏洞