疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

Java多线程深入Thread.sleep


 

 
 
 
实战分析
 
一直都说,Threed.sleep是不会释放锁,而wait是释放锁的(对象锁),现理论上来分析一下啊。
 
由于CPU分配的每个线程的时间片极为短暂(一般为几十毫秒),所以,CPU通过不停地切换线程执行,这样就给程序员一种错觉,以为多个线程是在同时执行。sleep就是正在执行的线程主动让出CPU,CPU去执行其他线程,在sleep指定的时间过后,CPU才会回到这个线程上继续往下执行,如果当前线程进入了同步锁,sleep方法并不会释放锁,即使当前线程使用sleep方法让出了CPU,但其他被同步锁挡住了的线程也无法得到执行。
 
package thread.concurrent;
 
public class DeepenSleep implements Runnable {
     private int number = 10;
 
     public void firstMethod() throws Exception {
            synchronized (this) {
                System. out.println("in first method");
                 number += 100;
                System. out.println("+100=" + number);
           }
     }
 
     public void secondMethod() throws Exception {
            synchronized (this) {
                System. out.println("in second method, prepare sleep");
                 /**
                 * (休息2S,阻塞线程) 以验证当前线程对象的机锁被占用时, 是否被可以访问其他同步代码块
                 */
                Thread. sleep(2000);
                System. out.println("wake up!!");
                 // this.wait(2000);
                 number *= 200;
                System. out.println("*200=" + number);
           }
     }
 
     @Override
     public void run() {
            try {
                System. out.println("run thread...");
                firstMethod();
           } catch (Exception e) {
                e.printStackTrace();
           }
     }
 
     public static void main(String[] args) throws Exception {
           DeepenSleep dt = new DeepenSleep();
           Thread thread = new Thread(dt);
           thread.start();
           System. out.println("prepare run second method");
           dt.secondMethod();
     }
}
 
输出如下:
 
执行结果
 
分析:主线程启动起来,因为创建线程等的资源消耗,所以主线程会先执行 dt.secondMethod(),因此会先输出prepare run second method,其后执行secondMehtod方法(注意该方法是要先闹到锁对象),而该方法直接将线程睡眠2s(注意此处对象锁DeepenSleep的实例对象并没有释放),然后执行线程dt的run方法,该方刚发执行dt的firstMethod,然而,该方法也是需要获取锁对象的,而此时他没先不能获取到,因为secondMehtod没有释放锁(准确点讲,主线程没有释放锁);然后等了2s,主线程睡眠时间已过,他warkup之后,因为还拥有锁,因此直接run secondMethod的剩下的方法,先输出”wake up”,然后执行 number*200,执行完,主线程释放掉锁,而dt线程拿到锁,执行run方法,拿到锁,执行run方法的synchronized的剩余方法:先输出”in first method”,然后执行加100的操作。
 
我们来变一下将firstMethod的同步去掉,看输出是什么样子
package thread.concurrent;
 
public class DeepenSleep implements Runnable {
     private int number = 10;
 
     public void firstMethod() throws Exception {
//         synchronized (this) {
                System. out.println( "in first method");
                 number += 100;
                System. out.println( "+100=" + number);
//         }
     }
 
     public void secondMethod() throws Exception {
            synchronized ( this) {
                System. out.println( "in second method, prepare sleep");
                 /**
                 * (休息2S,阻塞线程) 以验证当前线程对象的机锁被占用时, 是否被可以访问其他同步代码块
                 */
                Thread. sleep(2000);
                System. out.println( "wake up!!");
                 // this.wait(2000);
                 number *= 200;
                System. out.println( "*200=" + number);
           }
     }
 
     @Override
     public void run() {
            try {
                System. out.println( "run thread...");
                firstMethod();
           } catch (Exception e) {
                e.printStackTrace();
           }
     }
 
     public static void main(String[] args) throws Exception {
           DeepenSleep dt = new DeepenSleep();
           Thread thread = new Thread(dt);
           thread.start();
           System. out.println( "prepare run second method");
           dt.secondMethod();
     }
}
 
输出如下:
 
执行结果
 
分析:不同点在于,主线程睡眠之后,没有释放锁,dt线程执行firstMethod并不需要锁,因此先run firstMethod中的逻辑,先加100,然今,主线程睡醒之后,再执行剩下的逻辑,乘以200。
 
Thread.sleep(1000),1000ms后是否立即执行?
 
不一定,在未来的1000毫秒内,线程不想再参与到CPU竞争。那么1000毫秒过去之后,这时候也许另外一个线程正在使用CPU,那么这时候操作系统是不会重新分配CPU的,直到那个线程挂起或结束;况且,即使这个时候恰巧轮到操作系统进行CPU 分配,那么当前线程也不一定就是总优先级最高的那个,CPU还是可能被其他线程抢占去。
 
Thread.sleep(0),是否有用?
 
boss:“给你睡0小时”。
 
coder:“你TM逗我啊”。
休眠0ms,这样的休眠有何意义?Thread.Sleep(0)的作用,就是“触发操作系统立刻重新进行一次CPU竞争,重新计算优先级”。竞争的结果也许是当前线程仍然获得CPU控制权,也许会换成别的线程获得CPU控制权。这也是我们在大循环里面经常会写一句Thread.sleep(0) ,因为这样就给了其他线程比如Paint线程获得CPU控制权的权力,这样界面就不会假死在那里。