Java併發編程藝術 4 Java併發編程基礎

第四章 Java併發編程基礎

優先級
操作系統使用時分的形式調度運行的線程。每個線程會被分配到若干時間片,當線程時間片用戶線程發生調度,執行下一個線程。當前線程等待下次分配。
通過成員變量priority設置優先級。範圍1-10。默認5。數值越大優先級越高,10最高。

線程的狀態
Java線程可能處於一下6中狀態,在給定的某一時刻只能處於一種狀態。


1.實例化後,調用start()之前都處於初始狀態。
2.當線程調用start()後,線程進入運行狀態
3.如果線程中有對象執行了wait()方法,當前線程進入等待狀態。需要notify()方法喚醒
4.如果線程中有對象執行了wait(long)、sleep(long)方法等,進入超時等待狀態。需要notify()方法喚醒或者超時時間到。對等待狀態設置了超時時間
5.如果調用同步方法,在沒有獲得鎖的情況下會進入阻塞狀態。
6.線程執行完進入終止狀態。

Java將運行和就緒兩個狀態合併爲運行狀態。
阻塞狀態是進入synchronized方法/代碼塊(獲取鎖)時的狀態

Daemon線程(守護線程)
Java可以創建兩種線程:用戶線程和守護線程。用戶線程就是一般創建的線程。守護線程是後臺線程,可以使JVM的線程也可以自己創建守護線程。
Daemon線程是一種支持型線程,當線程只剩下守護線程時,JVM就會退出。如果還有其他的用戶線程,JVM就不會退出。
Thread.setDaemon(true)來設置爲Daemon線程。
注意:要在線程運行之前設置爲守護線程,啓動之後設置就沒用了
注意:守護線程會因爲JVM關閉,立即終止。所以守護線程中的finally塊不一定會執行。不能用來確保執行close操作和清理資源的邏輯。


Thread初始化
private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc) {
     if (name == null) {
           throw new NullPointerException("name cannot be null");
     }

     this.name = name;
          。。。。。
     Thread parent = currentThread();  //當前線程就是該線程的父線程,有當前線程創建子線程
          。。。。
     this.group = g;
     this.daemon = parent.isDaemon();
     this.priority = parent.getPriority();  //使用父線程的daemon和priority
     if (security == null || isCCLOverridden(parent.getClass()))
           this.contextClassLoader = parent.getContextClassLoader();
     else
           this.contextClassLoader = parent.contextClassLoader;
     this.inheritedAccessControlContext = acc != null ? acc : AccessController.getContext();
     this.target = target;
     setPriority(priority);
     if (parent.inheritableThreadLocals != null)
           this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
     /* Stash the specified stack size in case the VM cares */
     this.stackSize = stackSize;

     //使用靜態變量,使用++來獲取下一個線程id
     tid = nextThreadID();
}

啓動線程
Thread通過start()方法啓動線程,方法含義:當前線程(pattern線程)同步告知JVM,只要線程規劃器空閒,應立即調用start()方法。
調用了 start0()。   private native void start0();
注意:對於自定義的線程,最好可以添加名稱,方便排查錯誤或者分析程序。

volatile和synchronized關鍵字
支持多個線程同時訪問同一個對象或者成員變量。每個線程擁有對象的拷貝,保存在線程內存中。每個線程內存對其他線程不可見。
使用volatile可以用來修飾字段,告知程序線程中對volatile變量的修改,立即更新到主內存中。可以線程中使用的對象是主存中最新的狀態。但是並不能解決同步問題(同時獲取到同一個值)。
synchronized可以修飾方法或者同步塊的形式來使用,確保多個線程在同一時刻,只有一個線程處於同步塊或者同步方法中。(對象鎖)。確保線程對變量訪問的可見性和排他性。

等待和通知(wait/notify)
public class WaitNotify {
     public static Object lock = new Object();
     public static boolean flag = true;

     public static void main(String[] args) throws InterruptedException {
           new Thread(new Wait()).start();
           Thread.sleep(2000);
           new Thread(new Notify()).start();
     }

     static class Wait implements Runnable {
           @Override
           public void run() {
                //加鎖,擁有lock的Monitor
                synchronized (lock) {
                     while (flag) {
                           try {
                                System.out.println(Thread.currentThread() + "flag is true" );
                                lock.wait();  //釋放synchronized (lock) 擁有的鎖,釋放同步鎖
                           } catch (Exception e) {e.printStackTrace();}
                     }
                }
                //Notify線程設置flag爲false,結束工作
                System.out.println(Thread.currentThread() + "flag is false");
           }
     }
     static class Notify implements Runnable {
           @Override
           public void run() {
                //因爲lock.wait()放棄了同步鎖,所以Notify線程能獲取lock的鎖,擁有lock的Monitor
                synchronized (lock) {
                     System.out.println(Thread.currentThread() + " hold lock , notify" );
                     lock.notify(); //喚醒等待的線程(Wait)
                     flag = false;
                     try {Thread.sleep(5000);} catch (InterruptedException e) {}
                }
                System.out.println(Thread.currentThread() + "flag is false" );
                //當Wait線程執行完Synchronized同步塊後,進入同步代碼塊
                synchronized (lock) {
                     System.out.println(Thread.currentThread() + " hold lock again, notify" + System.currentTimeMillis());
                }
           }
     }
}
1.使用wait(),notify(),notifyAll(),需要先對調用對象加鎖(可以就是說要放在synchronized代碼塊中)
2.從wait()方法返回得前提是獲得了調用對象的鎖(也就是所其他線程調用了notify(),並且其他線程釋放鎖)
3.調用wait()方法後,線程從RUNNING變爲WAITING,放到對象的線程等待隊列。
4.其他線程調用notify() 方法後,線程被喚醒,從線程等待池中進入等鎖池,從WAITING變爲BLOCKED。等待其他線程執行完畢,釋放同步鎖。

等待/通知的範式
等待方
synchronized (對象) {                //1.獲取對象鎖·
     while (條件不滿足) {             //2.如果條件不滿足,那麼調用對象的wait()方法,被通知後仍要檢查條件(自旋鎖)
           對象.wait()
     }
     對應的處理邏輯                   //3.條件滿足則執行對應的邏輯
}
通知方
synchronized (對象) {                //1.獲取對象鎖·
     2.改變條件
     對象.notifyAll()                 //3.通知所有等待池中的線程
}


管道輸入/輸出
主要用於線程之間的數據傳輸。主要有一下4個實現 PipeOutputStream,PipeInputStream和PipeReader,PipeWriter前面兩者面向字節流,後面兩者面向字符
public class PipeInOut {

     public static void main(String[] args) throws IOException {
           PipedReader in = new PipedReader();
           PipedWriter out = new PipedWriter();
           out.connect(in);   //對應Pipe類型的流需要 使用connect進行綁定。沒有將輸入輸出進行綁定,對流的的訪問會有問題。
           new Thread(new Send(out)).start();
           new Thread(new Print(in)).start();

     }

     static class Send implements Runnable{
           private PipedWriter out ;
           public Send(PipedWriter out) {
                super();
                this.out = out;
           }
           @Override
           public void run() {
                int receive = 0;
                try {
                     while((receive = System.in.read() ) != -1){
                           out.write(receive);
                     }
                } catch (IOException e) {e.printStackTrace();
                } finally{
                     out.close();  
                }
           }
     }
     static class Print implements Runnable{
           private PipedReader in ;
           public Print(PipedReader in) {
                super();
                this.in = in;
           }
           @Override
           public void run() {
                int receive = 0;
                try {
                     while((receive = in.read() ) != -1){
                          System.out.println((char)receive);  //接受到的爲int型,需要轉化爲Char
                     }
                } catch (IOException e) {e.printStackTrace();}
           }
     }

}


Thread.join()方法
如果當前線程A,執行了thread.join()。當前線程A會等待thread執行完以後再從join()返回。還提供了join( long )的具有超時特性的方法。
join()會使主線程等待,但是本身的線程沒有影響。
public class JoinTest {
     public static void main(String[] args) {
           Thread pervious = Thread.currentThread();
           for(int i=0; i<10 ;i++){
                Thread thread = new Thread(new Domino(pervious));
                thread.start();
                pervious = thread;
           }
           System.out.println(Thread.currentThread().getName() + " terminate");
     }
     static class Domino implements Runnable{
           Thread thread;
           public Domino(Thread thread) {
                this.thread = thread;
           }
           public void run() {
                try {
                     thread.join();      //當前線程會被阻塞,thread對象線程 不會被阻塞
                } catch (InterruptedException e) {e.printStackTrace();}
                System.out.println(Thread.currentThread().getName() + " terminate");
           }
     }
}
join主線程等待子線程執行完畢
public static void main(String[] args){
    System.out.println("main thread starting...");
    List<MyThread> list = new ArrayList<MyThread>();
        for (int i = 1; i <= 5; i++) {
            MyThread my = new MyThread("Thrad " + i);
            my.start();
            list.add(my);
        }
        try{
            for (MyThread my : list){
                my.join();                 //main線程會等待,但是my線程並沒有影響
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
   System.out.println("main thread end...");
    }
}


ThreadLocal

public class ThreadLocalTest {
     String name;
     static ThreadLocal<Integer> sint = new ThreadLocal<Integer>(){
           protected Integer initialValue(){
                return (int)(Math.random()*10);
           }
     };
     ThreadLocal<Integer> oint = new ThreadLocal<Integer>(){
           protected Integer initialValue(){
                return (int)(Math.random()*10);
           }
     };

     public static void main(String[] args) throws InterruptedException {
           ThreadLocalTest e1 = new ThreadLocalTest("e1");
           ThreadLocalTest e2 = new ThreadLocalTest("e2");
           new Thread(new MyRunnable(e1,e2)).start();
           Thread.sleep(100);
           new Thread(new MyRunnable(e1,e2)).start();
     }
     static class MyRunnable implements Runnable{
           打印e1,e2的靜態變量和成員變量
     }
可以看出來:

在同一線程中,static變量的值是一樣的。但在不同線程中,static的值不同。
在同一線程中,成員變量的值是不一樣的。在不同線程中就更不一樣了。
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章