java多线程之六种状态

一、java线程的六种状态

其中,RUNNABLE状态包括 【运行中】 和 【就绪】;
BLOCKED(阻塞态)状态只有在【等待进入synchronized方法(块)】和 【其他Thread调用notify()或notifyAll(),但是还未获得锁】才会进入;

二、sleep() 、yield()、join()与 wait()/notify()的区别

sleep() 、yield()、join()是Thread的方法,只放弃cpu,但是不放弃锁
1、Thread.sleep(long millis),一定是当前线程调用此方法,当前线程进入TIMED_WAITING状态,但不释放对象锁,millis后线程自动苏醒进入就绪状态。作用:给其它线程执行机会的最佳方式。

2、Thread.yield(),一定是当前线程调用此方法,当前线程放弃获取的CPU时间片,但不释放锁资源,由运行状态变为就绪状态,让OS再次选择线程。作用:让相同优先级的线程轮流执行,但并不保证一定会轮流执行。实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。Thread.yield()不会导致阻塞。该方法与sleep()类似,只是不能由用户指定暂停多长时间。

3、t.join()/t.join(long millis),当前线程里调用其它线程t的join方法,当前线程进入WAITING/TIMED_WAITING状态,当前线程不会释放已经持有的对象锁。线程t执行完毕或者millis时间到,当前线程进入就绪状态。

wait()是Object的方法,放弃cpu,也放弃锁

4、obj.wait(),当前线程调用对象的wait()方法,当前线程释放对象锁,进入等待队列。依靠notify()/notifyAll()唤醒或者wait(long timeout) timeout时间到自动唤醒。
5、obj.notify()唤醒在此对象监视器上等待的单个线程,选择是任意性的。notifyAll()唤醒在此对象监视器上等待的所有线程。

public class Test {
     public static void main(String[] args) {

         new Thread1().start();         
         new Thread2().start();
     }
     
     public static class Thread1 extends Thread{
    	 
    	    @Override
    	    public void run() {
    		   
    	    	    synchronized (Test.class){
    	    	    	   System.out.println("Thread1 start");
    	    	    	   
    	    	    	   try {
                    /**
                     * 1、wait()和notify()是Object锁的方法
                     * 2、wait()会让出锁    	    	    		    
                     */
					Test.class.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
    	    	    	   
    	    	    	   System.out.println("Thread1 go on ");  
    	    	    } 	
    	    }
     }
     
     public static class Thread2 extends Thread{
    	 
 	    @Override
 	    public void run() {
 		   
 	    	    synchronized (Test.class){
 	    	    	   System.out.println("Thread2 start");
 	    	    	   
 	    	    	  /**
 	                    * 1、notify()调用后,该线程会等待该同步块执行完毕才释放锁  	    	    		    
 	                    */
 	    	    	   Test.class.notifyAll();
 	    	    	   
 	    	    	   try {
 	    	    		  /**
 	    	 	            * 1、sleep()是Thread的方法
 	    	 	            * 2、sleep()不让出锁,只让出cpu    	    	    		    
 	    	 	            */
					Thread2.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
 	    	    	   
 	    	    	   System.out.println("Thread2 go on");
 	    	    }
 	    }
  }
}

三、LockSupport中的park() 和 unpark()

总结一下,LockSupport比Object的wait/notify有两大优势:

①LockSupport不需要在同步代码块里 ,所以线程间也不需要维护一个共享的同步对象了,实现了线程间的解耦;
而wait/notify必须在同步块或同步方法中才能调用。

②unpark函数可以先于park调用,所以不需要担心线程间的执行的先后顺序,而wait必须先于notify。

1、为什么LockSupport不需要在同步代码块里而wait()需要?

线程A执行一段业务逻辑后调用wait阻塞住自己。主线程调用notify方法唤醒线程A,线程A然后打印自己执行的结果:

public class TestObjWait {

    public static void main(String[] args)throws Exception {
        final Object obj = new Object();
        Thread A = new Thread(new Runnable() {
            @Override
            public void run() {
                int sum = 0;
                for(int i=0;i<10;i++){
                    sum+=i;
                }
                try {
                    synchronized (obj){
                        obj.wait();
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }
                System.out.println(sum);
            }
        });
        A.start();
        //睡眠一秒钟,保证线程A已经计算完成,阻塞在wait方法
        Thread.sleep(1000);
        synchronized (obj){
            obj.notify();
        }
    }
}

使用LockSupport实现:

public class TestObjWait {

    public static void main(String[] args)throws Exception {
    
        Thread A = new Thread(new Runnable() {
            @Override
            public void run() {
                int sum = 0;
                for(int i=0;i<10;i++){
                    sum+=i;
                }
                LockSupport.park();
                System.out.println(sum);
            }
        });
        A.start();
        //睡眠一秒钟,保证线程A已经计算完成
        Thread.sleep(1000);
        LockSupport.unpark(A);
    }
}

2、为什么LockSupport不需要担心unpark函数和park调用顺序,而Object的wait/notify需要关心?

如果我们将上面代码的这一句去掉:

//睡眠一秒钟,保证线程A已经计算完成
        Thread.sleep(1000);

那么,使用wait()和notify()的就会出题,可能A会永远被挂起,因为主线程的notify()先于wait()调用了;
但是LockSupport的代码还是正确的执行,因为
LockSupport和每个使用它的线程都与一个许可(permit)关联。permit相当于1,0的开关,默认是0;
调用unpark就将permit赋值1;
调用park时,会判断permit如果为1,就会将permit赋值0,并且立即返回,如果permit为0,会阻塞在这里,直到permit变为1

猜你喜欢

转载自blog.csdn.net/shinecjj/article/details/82995309