1. 程式人生 > >java 執行緒間通訊

java 執行緒間通訊

1.使用join,執行緒同步

Object object;
public static  void TestJoin(){
    object=  new Object();
  final Thread one =new Thread(new Runnable() {
      @Override
      public void run() {
        printNumber("one");
      }
  });

  Thread two=new Thread(new Runnable() {
      @Override
      public void run() {
          Log.d("threadName","two開始等待one");
          try {
              one.join();//join方法的意思是,放棄當前執行緒的執行,並返回呼叫join方法的執行緒,並直到呼叫join方法的執行緒執行完畢,才到當前執行緒的執行。
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
          printNumber("two");
      }
  });
  one.start();
  two.start();
private static void printNumber(String threadName) {
    int i = 0;
    while (i++ < 3) {
        synchronized (object){
        try {

            Thread.sleep(100);
            Log.d("threadName", threadName + "數字:" + i);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
    }

結果:

   two開始等待one
   one數字:1
   one數字:2
   one數字:3
   two數字:1
   two數字:2
   two數字:3

2.使用synchronized wait notify,執行緒同步

private static void printNumberOne(String threadName) {
        int i = 0;
        while (i++ < 3) {
            //synchronized同步程式碼塊,synchronized無法知道執行緒有沒有成功獲取到鎖
            // synchronized不論是加在方法上還是物件上,它取得的鎖都是物件,不需要使用者手動去釋放鎖
            //synchronizedd的作用域分為物件級別和類級別:
            //類級別:每個類都對應一把鎖,如果將類的靜態成員函式宣告為 synchronized,將對類的所有例項物件起作用
            //物件級別:可以防止一個物件裡的幾個執行緒同時訪問synchronized修飾的方法,但是如果一個執行緒訪問了synchronized修飾
            的方法,其它執行緒不能同時訪問此物件內的任意一個synchronized修飾的方法,但是類的不同勢例項物件勢可以的。
            synchronized (object){ 
            try {

                Thread.sleep(100);
                Log.d("threadName", threadName + "數字:" + i);
               //wait()呼叫wait方法的執行緒,會釋放物件的鎖,並將當前執行緒放置到物件的等待佇列,只有等待另外執行緒的通知或者中斷,才會返回            
              //wait(long)超時等待一段時間,如果沒有通知就超時返回
                if(i==1) object.wait(); 
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
        }
    }
    private static void printNumberTwo(String threadName) {
        int i=0;
        while (i++ < 3) {
            synchronized (object) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.d("threadName", threadName + "數字:" + i);
                if(i==2){
                   //通知一個物件上等待的執行緒,等待的執行緒進入阻塞狀態,從等待佇列移動到同步佇列,等待cpu的排程重新獲得鎖,過得鎖後,該執行緒從wait方法返回
                    object.notify();
                }
            }
        }

    }
Object object;

public static  void ThreadOne(){
    object=  new Object();
  final Thread one =new Thread(new Runnable() {
      @Override
      public void run() {
          printNumberOne("one");
      }
  });

    Thread two=new Thread(new Runnable() {
        @Override
        public void run() {
            printNumberTwo("two");
        }
    });
    one.start();
    two.start();
}

結果:

 one數字:1  two數字:1  two數字:2  two數字:3  one數字:2  one數字:3

3.使用lock,condition,執行緒同步

    //ReentrantLock可重入鎖,有以下幾種方法:
    //void lock 無條件的鎖  
    //void lockInterruptibly throws InterruptedException 可中斷的鎖 如果獲取了鎖,則立即返回,如果沒有獲取到鎖,則當前執行緒則進入等待狀態,直到獲取到鎖或者被別的執行緒中斷。如果使用synchronized,如果執行緒沒有獲取到鎖,則會一直等待下去。
   // boolean tryLock() 嘗試獲取鎖,如果獲取到鎖,返回true,如果被其它執行緒持有,返回false,不會等待
  //boolean tryLock(long timeout,TimeUnit unit) 如果獲取到鎖,返回true,別的執行緒持有,有等待時間,在等待時間內獲取到鎖,返回true,反之則返回false
 //(lock是一個介面,必須手動釋放鎖,可重入,可中斷,可判斷,可(非)公平,適用於大量同步,
 //  synchronized是Java的關鍵字,執行完或者發生異常,都會主動釋放鎖,可重入,不可中斷,非公平,使用於少量同步)
    final Lock lock = new ReentrantLock();

    //對於同一的鎖,我們可以建立多個Condition,進行不同的操作,也可以喚醒或讓具體執行緒等待,這是Object的notify()方法做不到的
    final Condition inputCondation = lock.newCondition();
    final Condition removeCondation = lock.newCondition();

    List<Integer> list = new ArrayList<Integer>();
    int number = 0;

    void InputNum() {
        //加鎖
        lock.lock(); 

        try {
      
            list.add(number++);
            Log.d("ThreadName","inputsize="+list.size());
            //相當於Object中的notify()方法
            removeCondation.signal();

            //相當於Object中的wait()方法
            inputCondation.await();
            list.add(number++);
            Log.d("ThreadName","inputsize="+list.size());


        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
           //加了鎖,必須要手動釋放鎖,要不然在異常情況下,lock不會主動釋放鎖,會造成死鎖現象
            lock.unlock();
        }


    }


    void RemoveNum() {
        lock.lock();
        try {

            removeCondation.await();
            list.remove(list.size()-1);
            Log.d("ThreadName","removesize="+list.size());
            inputCondation.signal();

        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    final Thread one =new Thread(new Runnable() {
        @Override
        public void run() {
            InputNum();
        }
    });

    Thread two=new Thread(new Runnable() {
        @Override
        public void run() {
            RemoveNum();
        }
    });
 two.start();
 one.start();

結果:

inputsize=1 removesize=0 inputsize=1

4.使用AtomicInteger,執行緒同步

private AtomicInteger numCount=new AtomicInteger(0);
void Increment(){
   for(int i=0;i<100;i++){
       numCount.getAndIncrement();
   }
   Log.d("ThreadName","number++="+numCount);
}
new Thread(new Runnable() {
    @Override
    public void run() {
        Increment();
    }
}).start();

new Thread(new Runnable() {
    @Override
    public void run() {
        Increment();
    }
}).start();

結果:

ThreadName: number++=100
ThreadName: number++=200

5.使用volatile,執行緒同步

//使用volatile,執行緒修改了volatile修飾的值,會被立即強制重新整理到主記憶體中,讓所有執行緒可見,如果某個執行緒在修改變數的值,其它執行緒對於變數的快取會無效
//volatile能保證可見性,必不能保證原子性,所有對於volatile修飾的變數。不能含有變量表達式的操作,比如num=num++,這部書原子操作
volatile Boolean isTure=false;

void changStation(){
    isTure=!isTure;
    Log.d("ThreadName","修改了值");
}

void getStation(){
    if(isTure){
        Log.d("ThreadName","獲取到修改了的值");
        return;
    }
        Log.d("ThreadName","沒獲取到修改了的值");

} 

 new Thread(new Runnable() {
      @Override
      public void run() {
          changStation();
      }
  }).start();

new Thread(new Runnable() {
    @Override
    public void run() {
        getStation();
    }
}).start();

結果:

ThreadName: 修改了值 
ThreadName: 獲取到修改了的值