C語言中文網 目錄

Java暫停/掛起線程(suspend())和恢復線程(resume())

暫停線程意味著此線程還可以恢復運行。Java 多線程中,可以使用 suspend() 方法暫停線程,使用 resume() 方法恢復線程的執行。

suspend() 與 resume() 方法

本節通過一個案例來介紹 suspend() 與 resume() 方法的用法。首先來看一下案例中使用到的 MyThread21 線程,代碼如下所示。
package ch14;
public class MyThread21 extends Thread
{
    private long i=0;
    public long getI()
    {
        return i;
    }
    public void setI(long i)
    {
        this.i=i;
    }
    @Override
    public void run()
    {
        while(true)
        {
            i++;
        }
    }
}

MyThread21 線程中有一個成員 i,其中 setI() 方法和 getI() 方法分別用于設置和獲取 i 的值,run() 方法則是一個從i開始遞增的死循環。

下面編寫主線程的代碼,具體如下所示。
package ch14;
public class Test25
{
    public static void main(String[] args)
    {
        try
        {
            MyThread21 thread=new MyThread21();
            thread.start();
            Thread.sleep(5000);
            //A段
            thread.suspend();
            System.out.println("A= "+System.currentTimeMillis()+" i= "+thread.getI());
            Thread.sleep(5000);
            System.out.println("A= "+System.currentTimeMillis()+" i= "+thread.getI());
            //B段
            thread.resume();
            Thread.sleep(5000);
            //C段
            thread.suspend();
            System.out.println("B= "+System.currentTimeMillis()+" i= "+thread.getI());
            Thread.sleep(5000);
            System.out.println("B= "+System.currentTimeMillis()+" i= "+thread.getI());
        }
        catch(InterruptedException e)
        {
            e.printStackTrace();
        }
    }
}

最終運行結果如下所示。
A= 1540978346179 i= 2680986095
A= 1540978351179 i= 2680986095
B= 1540978356179 i= 5348657508
B= 1540978361179 i= 5348657508

從輸出結果的時間來看,調用 suspend() 方法確實可以暫停線程,而在調用 resume() 方法后線程恢復運行狀態。

獨占問題

在使用 suspend() 方法與 resume() 方法時,如果使用不當極易造成公共的同步對象被獨占,從而使得其他線程無法訪問公共同步對象。

例 2

下面通過一個案例來演示這種情況。如下所示是案例中使用的公共對象的代碼。
package ch14;
public class SynchronizedObject1
{
    synchronized public void printString()
    {
        System.out.println("begin");
        if (Thread.currentThread().getName().equals("a"))
        {
            System.out.println("a線程永遠 suspend了!");
            Thread.currentThread().suspend();
        }
        System.out.println("end");
    }
}

SynchronizedObject1 類在 printString() 方法開始時輸出“begin”,在該方法結束時輸出“end”,方法體中的if判斷如果當前線程的名稱是 a 則調用 suspend() 方法暫停線程。

接下來編寫主線程代碼,在主線程中創建一個 SynchronizedObject1 類對象并在線程中調用 printString() 方法,具體代碼如下所示。
package ch14;
public class Test26
{
    public static void main(String[] args)
    {
        try
        {
            final SynchronizedObject1 object=new SynchronizedObject1();
            Thread thread1=new Thread()
            {
                @Override
                public void run()
                {
                    object.printString();
                }
            };
            thread1.setName("a");
            thread1.start();
            Thread.sleep(1000);
            Thread thread2=new Thread()
            {
                @Override
                public void run()
                {
                    System.out.println("thread2啟動了,但進入不了printString()方法!所以只會打印1個begin!");
                    System.out.println("因為printString()方法被a線程鎖定并且永遠暫停了!");
                    object.printString();
                }
            };
            thread2.start();
        }
        catch(InterruptedException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

上述代碼比較簡單這里就不再解釋,運行后的結果如下所示。從中可以看到由于線程被永久暫停,所以只會輸出一個 begin。
begin
a線程永遠 suspend了!
thread2啟動了,但進入不了printString()方法!所以只會打印1個begin!
因為printString()方法被a線程鎖定并且永遠暫停了!

例 3

還有另外一種獨占鎖的情況也要格外注意,稍有不慎就會掉進“坑”里。創建測試用的 MyThread22 線程,具體代碼如下:
package ch14;
public class MyThread22 extends Thread
{
    private long i=0;
    @Override
    public void run()
    {
        while (true)
        {
            i++;
        }
    }
}

再來看主線程的代碼,如下所示。
package ch14;
public class Test27
{
    public static void main(String[] args)
    {
        try
        {
            MyThread22 thread=new MyThread22();
            thread.start();
            Thread.sleep(1000);
            thread.suspend();
            System.out.println("main end!");
        }
        catch(InterruptedException e)
        {
            e.printStackTrace();
        }
    }
}

程序執行后將看到預料不到的結果,如下所示。
main end!

如果將 MyThread22 線程類的代碼更改如下:
package ch14;
public class MyThread22 extends Thread
{   
    private long i=0; 
    @Override 
    public void run()
    { 
        while(true)
        { 
            i++; 
            System.out.println(i); 
        } 
    }
}

再次運行程序,控制臺將不打印 main end,運行結果如下所示。
......
130862
130863
130864
130865
130866
130867

出現這種情況的原因是,當程序運行到 println() 方法內部停止時,同步鎖未被釋放。這導致當前 PrintStream 對象的 println() 方法一直呈“暫停”狀態,并且“鎖未釋放”,而 main() 方法中的代碼“System.out. println(Mmain end!'1);”遲遲不能執行打印。

提示:雖然 suspend() 方法是過期作廢的方法,但還是有必要研究它過期作廢的原因,這是很有意義的。

不同步問題

在使用 suspend() 方法與 resume() 方法時也容易出現因為線程的暫停而導致數據不同步的情況。

例 4

下面通過一個案例來演示這種情況。如下所示是案例中使用的公共對象的代碼。
package ch14;
public class MyObject
{
    private String username="1";
    private String password="11";
    public void setValue(String u,String p)
    {
        this.username=u;
        if(Thread.currentThread().getName().equals("a"))
        {
            System.out.println("停止a線程!");
            Thread.currentThread().suspend();
        }
        this.password=p;
    }
    public void printUsernamePassword()
    {
        System.out.println(username+" "+password);
    }
}

如上述代碼所示,MyObject 類的 setValue() 方法會在線程名稱是 a 時執行停止線程操作。 如下所示是主線程代碼。
package ch14;
public class Test28
{
    public static void main(String[] args) throws InterruptedException
    {
        final MyObject myobject=new MyObject();
        Thread thread1=new Thread()
        {
            public void run()
            {
                myobject.setValue("a","aa");
            };
        };
        thread1.setName("a");
        thread1.start();
        Thread.sleep(500);
        Thread thread2=new Thread()
        {
            public void run()
            {
                myobject.printUsernamePassword();
            };
        };
        thread2.start();
    }
}

程序運行結果如下所示。
停止a線程!
a 11

從程序運行的結果可以看到,出現了值不同步的情況,所以在程序中使用 suspend() 方法要格外注意。

精美而實用的網站,提供C語言C++STLLinuxShellJavaGo語言等教程,以及socketGCCviSwing設計模式JSP等專題。

Copyright ?2011-2018 biancheng.net, 陜ICP備15000209號

底部Logo