Java線程相關[轉]

1、java中有幾種方法可以實現一個線程?用什么關鍵字修飾同步方法? stop()和suspend()方法為何不推薦使用?

java5以前,有如下兩種:
第一種:
new Thread(){}.start();這表示調用Thread子類對象的run方法,new Thread(){}表示一個Thread的匿名子類的實例對象,子類加上run方法后的代碼如下:
new Thread(){
public void run(){
}
}.start();

第二種:
new Thread(new Runnable(){}).start();這表示調用Thread對象接受的Runnable對象的run方法,new Runnable(){}表示一個Runnable的匿名子類的實例對象,runnable的子類加上run方法后的代碼如下:
new Thread(new Runnable(){
public void run(){
}
}
).start();

從java5開始,還有如下一些線程池創建多線程的方式:
ExecutorService pool = Executors.newFixedThreadPool(3)
for(int i=0;i<10;i++)
{
pool.execute(new Runable(){public void run(){}});
}
Executors.newCachedThreadPool().execute(new Runable(){public void run(){}});
Executors.newSingleThreadExecutor().execute(new Runable(){public void run(){}});

有兩種實現方法,分別使用new Thread()和new Thread(runnable)形式,第一種直接調用thread的run方法,所以,我們往往使用Thread子類,即new SubThread()。第二種調用runnable的run方法。

有兩種實現方法,分別是繼承Thread類與實現Runnable接口
用synchronized關鍵字修飾同步方法
反對使用stop(),是因為它不安全。它會解除由線程獲取的所有鎖定,而且如果對象處于一種不連貫狀態,那么其他線程能在那種狀態下檢查和修改它們。結果很難檢查出真正的問題所在。suspend()方法容易發生死鎖。調用suspend()的時候,目標線程會停下來,但卻仍然持有在這之前獲得的鎖定。此時,其他任何線程都不能訪問鎖定的資源,除非被"掛起"的線程恢復運行。對任何線程來說,如果它們想恢復目標線程,同時又試圖使用任何一個鎖定的資源,就會造成死鎖。所以不應該使用suspend(),而應在自己的Thread類中置入一個標志,指出線程應該活動還是掛起。若標志指出線程應該掛起,便用wait()命其進入等待狀態。若標志指出線程應當恢復,則用一個notify()重新啟動線程。

2、sleep() 和 wait() 有什么區別?
(網上的答案:sleep是線程類(Thread)的方法,導致此線程暫停執行指定時間,給執行機會給其他線程,但是監控狀態依然保持,到時后會自動恢復。調用sleep不會釋放對象鎖。 wait是Object類的方法,對此對象調用wait方法導致本線程放棄對象鎖,進入等待此對象的等待鎖定池,只有針對此對象發出notify方法(或notifyAll)后本線程才進入對象鎖定池準備獲得對象鎖進入運行狀態。)

sleep就是正在執行的線程主動讓出cpu,cpu去執行其他線程,在sleep指定的時間過后,cpu才會回到這個線程上繼續往下執行,如果當前線程進入了同步鎖,sleep方法并不會釋放鎖,即使當前線程使用sleep方法讓出了cpu,但其他被同步鎖擋住了的線程也無法得到執行。wait是指在一個已經進入了同步鎖的線程內,讓自己暫時讓出同步鎖,以便其他正在等待此鎖的線程可以得到同步鎖并運行,只有其他線程調用了notify方法(notify并不釋放鎖,只是告訴調用過wait方法的線程可以去參與獲得鎖的競爭了,但不是馬上得到鎖,因為鎖還在別人手里,別人還沒釋放。如果notify方法后面的代碼還有很多,需要這些代碼執行完后才會釋放鎖,可以在notfiy方法后增加一個等待和一些代碼,看看效果),調用wait方法的線程就會解除wait狀態和程序可以再次得到鎖后繼續向下運行。對于wait的講解一定要配合例子代碼來說明,才顯得自己真明白。

package com.huawei.interview;

public class MultiThread {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        new Thread(new Thread1()).start();
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        new Thread(new Thread2()).start();      
    }
    
    
    private static class Thread1 implements Runnable
    {

        @Override
        public void run() {
            // TODO Auto-generated method stub
//由于這里的Thread1和下面的Thread2內部run方法要用同一對象作為監視器,我們這里不能用this,因為在Thread2里面的this和這個Thread1的this不是同一個對象。我們用MultiThread.class這個字節碼對象,當前虛擬機里引用這個變量時,指向的都是同一個對象。
            synchronized (MultiThread.class) {

                System.out.println("enter thread1...");
                
                System.out.println("thread1 is waiting");
                try {
            //釋放鎖有兩種方式,第一種方式是程序自然離開監視器的范圍,也就是離開了synchronized關鍵字管轄的代碼范圍,另一種方式就是在synchronized關鍵字管轄的代碼內部調用監視器對象的wait方法。這里,使用wait方法釋放鎖。
                    MultiThread.class.wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                
                System.out.println("thread1 is going on...");
                System.out.println("thread1 is being over!");           
            }
        }
        
    }
    
    private static class Thread2 implements Runnable
    {

        @Override
        public void run() {
            // TODO Auto-generated method stub
            synchronized (MultiThread.class) {
            
                System.out.println("enter thread2...");
                
                System.out.println("thread2 notify other thread can release wait status..");
//由于notify方法并不釋放鎖, 即使thread2調用下面的sleep方法休息了10毫秒,但thread1仍然不會執行,因為thread2沒有釋放鎖,所以Thread1無法得不到鎖。

                MultiThread.class.notify();
                
                System.out.println("thread2 is sleeping ten millisecond...");
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                
                System.out.println("thread2 is going on...");
                System.out.println("thread2 is being over!");
                
            }
        }
        
    }   

}

3、同步和異步有何異同,在什么情況下分別使用他們?舉例說明。
如果數據將在線程間共享。例如正在寫的數據以后可能被另一個線程讀到,或者正在讀的數據可能已經被另一個線程寫過了,那么這些數據就是共享數據,必須進行同步存取。
當應用程序在對象上調用了一個需要花費很長時間來執行的方法,并且不希望讓程序等待方法的返回時,就應該使用異步編程,在很多情況下采用異步途徑往往更有效率。

  1. 下面兩個方法同步嗎?(自己發明)
    class Test
{
synchronized static void sayHello3()
        {
        
        }   
    
        synchronized void getX(){}
}

5、多線程有幾種實現方法?同步有幾種實現方法?
多線程有兩種實現方法,分別是繼承Thread類與實現Runnable接口
同步的實現方面有兩種,分別是synchronized,wait與notify
wait():使一個線程處于等待狀態,并且釋放所持有的對象的lock。
sleep():使一個正在運行的線程處于睡眠狀態,是一個靜態方法,調用此方法要捕捉InterruptedException異常。
notify():喚醒一個處于等待狀態的線程,注意的是在調用此方法的時候,并不能確切的喚醒某一個等待狀態的線程,而是由JVM確定喚醒哪個線程,而且不是按優先級。
Allnotity():喚醒所有處入等待狀態的線程,注意并不是給所有喚醒線程一個對象的鎖,而是讓它們競爭。

6、啟動一個線程是用run()還是start()? .
啟動一個線程是調用start()方法,使線程就緒狀態,以后可以被調度為運行狀態,一個線程必須關聯一些具體的執行代碼,run()方法是該線程所關聯的執行代碼。

7、當一個線程進入一個對象的一個synchronized方法后,其它線程是否可進入此對象的其它方法?
分幾種情況:
1.其他方法前是否加了synchronized關鍵字,如果沒加,則能。
2.如果這個方法內部調用了wait,則可以進入其他synchronized方法。
3.如果其他個方法都加了synchronized關鍵字,并且內部沒有調用wait,則不能。
4.如果其他方法是static,它用的同步鎖是當前類的字節碼,與非靜態的方法不能同步,因為非靜態的方法用的是this。

8、線程的基本概念、線程的基本狀態以及狀態之間的關系
一個程序中可以有多條執行線索同時執行,一個線程就是程序中的一條執行線索,每個線程上都關聯有要執行的代碼,即可以有多段程序代碼同時運行,每個程序至少都有一個線程,即main方法執行的那個線程。如果只是一個cpu,它怎么能夠同時執行多段程序呢?這是從宏觀上來看的,cpu一會執行a線索,一會執行b線索,切換時間很快,給人的感覺是a,b在同時執行,好比大家在同一個辦公室上網,只有一條鏈接到外部網線,其實,這條網線一會為a傳數據,一會為b傳數據,由于切換時間很短暫,所以,大家感覺都在同時上網。

狀態:就緒,運行,synchronize阻塞,wait和sleep掛起,結束。wait必須在synchronized內部調用。
調用線程的start方法后線程進入就緒狀態,線程調度系統將就緒狀態的線程轉為運行狀態,遇到synchronized語句時,由運行狀態轉為阻塞,當synchronized獲得鎖后,由阻塞轉為運行,在這種情況可以調用wait方法轉為掛起狀態,當線程關聯的代碼執行完后,線程變為結束狀態。

9、簡述synchronized和java.util.concurrent.locks.Lock的異同 ?
主要相同點:Lock能完成synchronized所實現的所有功能
主要不同點:Lock有比synchronized更精確的線程語義和更好的性能。synchronized會自動釋放鎖,而Lock一定要求程序員手工釋放,并且必須在finally從句中釋放。Lock還有更強大的功能,例如,它的tryLock方法可以非阻塞方式去拿鎖。
舉例說明(對下面的題用lock進行了改寫):

package com.huawei.interview;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ThreadTest {

    /**
     * @param args
     */
    
    private int j;
    private Lock lock = new ReentrantLock();
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ThreadTest tt = new ThreadTest();
        for(int i=0;i<2;i++)
        {
            new Thread(tt.new Adder()).start();
            new Thread(tt.new Subtractor()).start();
        }
    }

    private class Subtractor implements Runnable
    {

        @Override
        public void run() {
            // TODO Auto-generated method stub
            while(true)
            {
                /*synchronized (ThreadTest.this) {          
                    System.out.println("j--=" + j--);
                    //這里拋異常了,鎖能釋放嗎?
                }*/
                lock.lock();
                try
                {
                    System.out.println("j--=" + j--);
                }finally
                {
                    lock.unlock();
                }
            }
        }
        
    }
    
    private class Adder implements Runnable
    {

        @Override
        public void run() {
            // TODO Auto-generated method stub
            while(true)
            {
                /*synchronized (ThreadTest.this) {
                System.out.println("j++=" + j++);   
                }*/
                lock.lock();
                try
                {
                    System.out.println("j++=" + j++);
                }finally
                {
                    lock.unlock();
                }               
            }           
        }
        
    }
}

10、設計4個線程,其中兩個線程每次對j增加1,另外兩個線程對j每次減少1。寫出程序。
以下程序使用內部類實現線程,對j增減的時候沒有考慮順序問題。

public class ThreadTest1 
{ 
private int j; 
public static void main(String args[]){ 
   ThreadTest1 tt=new ThreadTest1(); 
   Inc inc=tt.new Inc(); 
   Dec dec=tt.new Dec(); 
   for(int i=0;i<2;i++){ 
       Thread t=new Thread(inc); 
       t.start(); 
           t=new Thread(dec); 
       t.start(); 
       } 
   } 
private synchronized void inc(){ 
   j++; 
   System.out.println(Thread.currentThread().getName()+"-inc:"+j); 
   } 
private synchronized void dec(){ 
   j--; 
   System.out.println(Thread.currentThread().getName()+"-dec:"+j); 
   } 
class Inc implements Runnable{ 
   public void run(){ 
       for(int i=0;i<100;i++){ 
       inc(); 
       } 
   } 
} 
class Dec implements Runnable{ 
   public void run(){ 
       for(int i=0;i<100;i++){ 
       dec(); 
       } 
   } 
} 
} 

----------隨手再寫的一個-------------

class A
{
JManger j =new JManager();
main()
{
    new A().call();
}

void call
{
    for(int i=0;i<2;i++)
    {
        new Thread(
            new Runnable(){ public void run(){while(true){j.accumulate()}}}
        ).start();
        new Thread(new Runnable(){ public void run(){while(true){j.sub()}}}).start();
    }
}
}

class JManager
{
    private j = 0;
    
    public synchronized void subtract()
    {
        j--
    }
    
    public synchronized void accumulate()
    {
        j++;
    }
    
}

11、子線程循環10次,接著主線程循環100,接著又回到子線程循環10次,接著再回到主線程又循環100,如此循環50次,請寫出程序。

最終的程序代碼如下:

public class ThreadTest {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        new ThreadTest().init();

    }

    public void init()
    {
        final Business business = new Business();
        new Thread(
                new Runnable()
                {

                    public void run() {
                        for(int i=0;i<50;i++)
                        {
                            business.SubThread(i);
                        }                       
                    }
                    
                }
        
        ).start();
        
        for(int i=0;i<50;i++)
        {
            business.MainThread(i);
        }       
    }
    
    private class Business
    {
        boolean bShouldSub = true;//這里相當于定義了控制該誰執行的一個信號燈
        public synchronized void MainThread(int i)
        {
            if(bShouldSub)
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }       
                
            for(int j=0;j<5;j++)
            {
                System.out.println(Thread.currentThread().getName() + ":i=" + i +",j=" + j);
            }
            bShouldSub = true;
            this.notify();
        
        }
        
        
        public synchronized void SubThread(int i)
        {
            if(!bShouldSub)
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }   
                
            for(int j=0;j<10;j++)
            {
                System.out.println(Thread.currentThread().getName() + ":i=" + i +",j=" + j);
            }
            bShouldSub = false;             
            this.notify();          
        }
    }
}

備注:不可能一上來就寫出上面的完整代碼,最初寫出來的代碼如下,問題在于兩個線程的代碼要參照同一個變量,即這兩個線程的代碼要共享數據,所以,把這兩個線程的執行代碼搬到同一個類中去:

package com.huawei.interview.lym;

public class ThreadTest {
    
    private static boolean bShouldMain = false;
    
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        /*new Thread(){
        public void run()
        {
            for(int i=0;i<50;i++)
            {
                for(int j=0;j<10;j++)
                {
                    System.out.println("i=" + i + ",j=" + j);
                }
            }               
        }
        
    }.start();*/        
        
        
        //final String str = new String("");

        new Thread(
                new Runnable()
                {
                    public void run()
                    {
                        for(int i=0;i<50;i++)
                        {
                            synchronized (ThreadTest.class) {
                                if(bShouldMain)
                                {
                                    try {
                                        ThreadTest.class.wait();} 
                                    catch (InterruptedException e) {
                                        e.printStackTrace();
                                    }
                                }
                                for(int j=0;j<10;j++)
                                {
                                    System.out.println(
                                            Thread.currentThread().getName() + 
                                            "i=" + i + ",j=" + j);
                                }
                                bShouldMain = true;
                                ThreadTest.class.notify();
                            }                           
                        }                       
                    }
                }
        ).start();
        
        for(int i=0;i<50;i++)
        {
            synchronized (ThreadTest.class) {
                if(!bShouldMain)
                {
                    try {
                        ThreadTest.class.wait();} 
                    catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }               
                for(int j=0;j<5;j++)
                {
                    System.out.println(
                            Thread.currentThread().getName() +                      
                            "i=" + i + ",j=" + j);
                }
                bShouldMain = false;
                ThreadTest.class.notify();              
            }           
        }
    }

}

下面使用jdk5中的并發庫來實現的:

import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;

public class ThreadTest
{
    private static Lock lock = new ReentrantLock();
    private static Condition subThreadCondition = lock.newCondition();
    private static boolean bBhouldSubThread = false;
    public static void main(String [] args)
    {
        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        threadPool.execute(new Runnable(){
            public void run()
            {
                for(int i=0;i<50;i++)
                {
                    lock.lock();                    
                    try
                    {                   
                        if(!bBhouldSubThread)
                            subThreadCondition.await();
                        for(int j=0;j<10;j++)
                        {
                            System.out.println(Thread.currentThread().getName() + ",j=" + j);
                        }
                        bBhouldSubThread = false;
                        subThreadCondition.signal();
                    }catch(Exception e)
                    {                       
                    }
                    finally
                    {
                        lock.unlock();
                    }
                }           
            }
            
        });
        threadPool.shutdown();
        for(int i=0;i<50;i++)
        {
                lock.lock();                    
                try
                {   
                    if(bBhouldSubThread)
                            subThreadCondition.await();                             
                    for(int j=0;j<10;j++)
                    {
                        System.out.println(Thread.currentThread().getName() + ",j=" + j);
                    }
                    bBhouldSubThread = true;
                    subThreadCondition.signal();                    
                }catch(Exception e)
                {                       
                }
                finally
                {
                    lock.unlock();
                }                   
        }
    }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 本文主要講了java中多線程的使用方法、線程同步、線程數據傳遞、線程狀態及相應的一些線程函數用法、概述等。 首先講...
    李欣陽閱讀 2,493評論 1 15
  • Java多線程學習 [-] 一擴展javalangThread類 二實現javalangRunnable接口 三T...
    影馳閱讀 2,986評論 1 18
  • 該文章轉自:http://blog.csdn.net/evankaka/article/details/44153...
    加來依藍閱讀 7,381評論 3 87
  • 寫在前面的話: 這篇博客是我從這里“轉載”的,為什么轉載兩個字加“”呢?因為這絕不是簡單的復制粘貼,我花了五六個小...
    SmartSean閱讀 4,790評論 12 45
  • 早上好!身體健康!生活愉快!
    0d38240ae2f7閱讀 147評論 0 0