天天看點

Java多線程——線程操作(方法)

Java多線程中我們有一些常用的操作線程的方法,如start(),sleep(),join(),yield(),wait(),notify()和interrupt()等方法。就是這些方法我們才能啟動線程使線程完成我們想要完成的任務。我寫過一篇建立線程的部落格,其中就介紹了線程的三種狀态:就緒狀态,運作狀态和阻塞狀态。而以上一些方法的調用就是線程狀态變化的原因之一。

1.有關線程名稱的方法

我們首先來看看Thread類的一個構造方法:

public Thread(Runnable target, String name)
           

這個構造方法可以傳入一個Runnable類型對象和String對象。我們知道傳入的Runnable類型的對象實際上就是我們賦予這個線程的任務,而這個String對象實際上就是我們為這個線程賦予的名稱。

我們來試一試:

public class NameTest1 {

    public static void main(String[] args) {

        Thread thread1 = new Thread(new Runnable() {
            public void run() {
                System.out.println("這是線程");
            }
        },"Thread-user1");
        
    }

}
           

那麼我們這個線程的名稱到底是不是我們所賦的名稱呢?我們就要用到檢視線程的方法了。

public final String getName()
           

這個方法會傳回調用這個方法的線程的線程名稱。

我們使用這個方法來檢視上面的的代碼是否賦名成功:

public class NameTest1 {

    public static void main(String[] args) {

        Thread thread1 = new Thread(new Runnable() {
            public void run() {
                System.out.println("這是線程");
            }
        },"Thread-user1");

        System.out.println("thread1線程的名稱為:" + thread1.getName());

    }

}
           

結果:

Java多線程——線程操作(方法)

可以看到,我們的線程名稱與我們所賦的名稱是相同的。

那麼如果我們想要更改一個線程的名稱怎麼辦呢?就要用到這個方法:

public final synchronized void setName(String name)
           

這個方法會修改調用這個方法的線程的線程名稱。

我們來試一試:

public class NameTest1 {

    public static void main(String[] args) {

        Thread thread1 = new Thread(new Runnable() {
            public void run() {
                System.out.println("這是線程");
            }
        },"Thread-user1");

        System.out.println("thread1線程的名稱為:" + thread1.getName());

        thread1.setName("Thread-newName");

        System.out.println("thread1線程的名稱為:" + thread1.getName());

    }

}
           

結果:

Java多線程——線程操作(方法)

2.sleep()方法

public static native void sleep(long millis)
           

這個方法Thread類的靜态方法,需要傳入一個long型資料(millis表示機關為毫秒)。這個方法會使調用它的線程(調用這個方法所在的線程)休眠,休眠時間為傳入的參數大小乘以1毫秒。

!!!注意:線程使用這個方法進入休眠時會釋放CPU資源,但不會釋放鎖。

我們來使用一下這個方法:

public class SleepTest {

    public static void main(String[] args) throws InterruptedException {

        Runnable runnable = new Runnable() {

            private volatile int i = 0;

            public void run() {
                for(;;){
                    Thread thread = Thread.currentThread();
                    synchronized (this){
                        i++;
                        if(i<6){
                            System.out.println(thread.getName() + "線程。。。" + i);
                        }else {
                            while(true){
                                try {
                                    Thread.sleep(1000);
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }
                }
            }
        };

        Thread thread1 = new Thread(runnable,"Thread-test1");
        Thread thread2 = new Thread(runnable,"Thread-test2");
        thread1.start();
        thread2.start();

    }

}
           

結果如圖:

Java多線程——線程操作(方法)

可以看到,此時程式無法結束,這是因為線程1休眠無法終止。而線程1休眠後,線程2卻仍然不能執行被同步的代碼塊。

2.yield()方法

public static native void yield()
           

這個方法是Thread類的靜态方法,這個方法會暫停調用這個方法的線程(調用這個方法所在的線程),釋放CPU資源,但不會釋放鎖。看起來這個方法和Sleep有些相似,但是它們也有差別:1.這個方法暫停時間是沒法控制的;2.暫停結束後直接進入就緒态,不會造成阻塞;3.線程因這個方法釋放的CPU資源隻能被與線程同優先級的線程獲得。

不使用這個方法:

public class YieldTest {

    public static void main(String[] args) {

        Runnable runnable = new Runnable() {
            public void run() {
                for(int i=0; i<5; i++){
//                    Thread.yield();
                    System.out.println(Thread.currentThread().getName() + "線程列印的: i = " + i);
                }
            }
        };

        new Thread(runnable,"Thread-test1").start();
        new Thread(runnable,"Thread-test2").start();
        new Thread(runnable,"Thread-test3").start();

    }

}
           

結果:

Java多線程——線程操作(方法)

去掉注釋後,使用了yield()方法後的結果:

Java多線程——線程操作(方法)

我們可以看到使用了yield()方法後的列印是無規律的。

3.join()方法

public final void join()
           

該方法會使所線上程休眠,直到調用該方法的線程執行完畢之後再開始執行所線上程。

不使用該方法:

public class JoinTest {

    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = new Runnable() {
            public void run() {
                String name = Thread.currentThread().getName();
                synchronized (this){
                    Thread.yield();
                    System.out.println(name + "執行");
                }
            }
        };

        Thread thread1 = new Thread(runnable,"Thread-test1");
        Thread thread2 = new Thread(runnable,"Thread-test2");
        Thread thread3 = new Thread(runnable,"Thread-test3");
        Thread thread4 = new Thread(runnable,"Thread-test4");
        Thread thread5 = new Thread(runnable,"Thread-test5");

        thread1.start();
//        thread1.join();
        thread2.start();
//        thread2.join();
        thread3.start();
//        thread3.join();
        thread4.start();
//        thread4.join();
        thread5.start();
//        thread5.join();
    }

}
           

不使用該方法的結果:

Java多線程——線程操作(方法)

使用該方法的結果:

Java多線程——線程操作(方法)

4.interrupt()方法

public void interrupt()
           

該方法會将線程的中斷标志位修改為true。當中斷标志位為true後,如果線程未受阻塞,那麼就隻是中斷标志位變為true;若是線程處于阻塞狀态,此時受阻線程會抛出一個異常以退出阻塞狀态,同時中斷标志位變回false。

使用這個方法:

public class InterruptTest {

    public static void main(String[] args) throws InterruptedException {

        Runnable runnable = new Runnable() {
            public void run() {
                while(true){
                    boolean bool = Thread.currentThread().isInterrupted();
                    System.out.println(Thread.currentThread().getName() + "線程是否中斷:" + bool);
                    if(bool){
                        System.out.println(Thread.currentThread().getName() + "線程阻塞");
                        return;
                    }
                }
            }
        };

        Thread thread = new Thread(runnable, "Thread-test");
        thread.start();
        Thread.sleep(2);
        thread.interrupt();

    }

}
           

結果:

Java多線程——線程操作(方法)

!!!注意:interrupt()方法隻是修改了中斷标志位,實際上不會中斷線程

請看如下代碼:

public class InterruptTest {

    public static void main(String[] args) throws InterruptedException {

        Runnable runnable = new Runnable() {
            public void run() {
                while(true){
                    boolean bool = Thread.currentThread().isInterrupted();
                    System.out.println(Thread.currentThread().getName() + "線程是否中斷:" + bool);
//                    if(bool){
//                        System.out.println(Thread.currentThread().getName() + "線程阻塞");
//                        return;
//                    }
                }
            }
        };

        Thread thread = new Thread(runnable, "Thread-test");
        thread.start();
        Thread.sleep(2);
        thread.interrupt();

    }

}
           

結果:

Java多線程——線程操作(方法)

可以看到此時中斷标志位為true,但是線程卻并沒有中斷。

5.wait()方法和notify()方法

這兩個方法相對于其它方法有一些特殊,而且這兩個方法一般都是成對使用,是以我把他們放在一起。

public final void wait()
           

該方法是Object類的成員方法,調用該方法會使線程在wait()方法的代碼處停止,線程被置入“預執行隊列”中,等待通知或中斷,同時線程釋放鎖。

!!注意:wait()方法必須在同步方法或同步代碼塊中使用。

public final native void notify()
           

該方法是Object類的成員方法,調用該方法後将會在調用該方法的對象的“預執行隊列”中随機挑出一個線程進行通知。該方法一般與wait()方法成對使用。還有notifyAll()方法,則會将調用該方法的對象的“預執行隊列”中是以線程進行通知。

!!注意:notify()方法必須在同步方法或同步代碼塊中使用,而且其與wait()方法成對使用時,鎖的對象必須是同一對象。

如下代碼:

public class WaitAndNotifyTest {

    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = new Runnable() {
            public void run() {
                for(int i=0; i<10; i++){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if(i == 2){
                        System.out.println(Thread.currentThread().getName() + "調用wait方法,開始等待");
                        try {
                            synchronized (this){
                                this.wait();
                            }
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println(Thread.currentThread().getName() + "線程列印:i = " + i);
                }
            }
        };

        Thread thread1 = new Thread(runnable,"Thread-test");
        thread1.start();
        Thread.sleep(5000);
        synchronized (runnable){
            System.out.println("通知" + thread1.getName() + "線程");
            runnable.notify();
        }
    }

}
           

結果:

Java多線程——線程操作(方法)

我的所有示範代碼都上傳在github,連結:https://github.com/nodonotnodo/JavaSE-csdn