<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆-159  評論-114  文章-7  trackbacks-0

    class Stack{
        
    int index=0;
        
    char data[]=new char[6];
        
    public synchronized void push(char c){
                data[index]
    =c;
                System.out.println(
    "Push char: "+c+" ->");
                
                
    try{
                    Thread.sleep(
    1000);
                }

                
    catch(Exception e){}
                index
    ++;
            System.out.println(
    "--> Push "+c+" Completed  index="+index+"   Stack is "+data[0]+" "+data[1]+" "+data[2]);
        }

        
        
    public void pop(){
                index
    --;
        
                
    try{
                    Thread.sleep(
    10);
                }

                
    catch(Exception e){}    
                
                
    char c=data[index];
                data[index]
    =' ';
                System.out.println(
    "** Pop  "+c+"**  index="+index+"   Stack is "+data[0]+" "+data[1]+" "+data[2]);    

        }

    }
         

    class PushRunner implements Runnable{
        
    private Stack s;
        
    public PushRunner(Stack s){
            
    this.s=s;
        }

        
        
    public void run(){
                s.push(
    'c');
        }


        
    }


    class PopRunner implements Runnable{
        
    private Stack s;
        
    public PopRunner(Stack s){
            
    this.s=s;
        }

        
    public void run(){
            
    synchronized(s){
                s.pop();
            }

        }

    }

    public class TestSynchronize{
        
    public static void main(String[] args){
            Stack s
    =new Stack();
            s.push(
    'a');
            s.push(
    'b');
            
    new Thread(new PushRunner(s)).start();
            
    new Thread(new PopRunner(s)).start();
        }

    }

    首先,我們討論一下進程,進程是操作系統級別下,單獨執行的一個任務。

    Win32 Unix都是多任務操作系統。

    多任務,并發執行是一個宏觀概念,實際微觀串行。

    CPU同一時間刻只能執行一個任務。

    OS負責進程調度,使用CPU,也就是獲得時間片。




    在一個進程中,再可以分為多個程序順序執行流,每個執行流就是一個線程。

    分配CPU時間片的依然是CPU,多線程時,程序會變慢,每個線程分配到的時間片少了。



    進程與線程的區別,進程是數據獨占的(獨立數據空間),線程是數據共享的(這也是線程之間通訊容易的原因,不需要傳遞數據)。

    Java是語言級支持多線程的,體現在有現成的封裝類(java.lang.Thread)完成了必要的并發細節的工作(與操作系統打交道,分配PID等)。



    兩種方式來得到一個線程對象。

    public class TestThread{
        
    public static void main(String[] args)
        
    {
            Thread t1 
    = new MyThread();
            t1.start();
        }

    }


    class MyThread extends Thread
    {
        
    public void run()
        
    {
            System.out.println(
    "thread run");
        }

    }


    一個線程對象--〉代表著一個線程--〉一個順序執行流(run方法)

    這個程序有兩個線程,一個是main主線程,它調用了t1.start(),這是t1線程只是就緒狀態,還沒有真正啟動線程,main主線程結束了!!t1運行。兩個線程都退出了,進程完結。

    一個進程退出,要等待進程中所有線程都退出,再退出虛擬機。

    方式2,實現java.lang.Runable接口,這是這個類的對象是一個目標對象,而不能理解為是一個線程對象。

    public class TestThread{
        
    public static void main(String[] args)
        
    {
            Thread t1 
    = new MyThread();
            t1.start();
            
            Runnable target 
    = new MyRunnable();
            Thread t2 
    = new Thread(target);
            t2.start();
        }

    }


    class MyThread extends Thread
    {
        
    public void run()
        
    {
            System.out.println(
    "thread run");
        }

    }


    class MyRunnable implements Runnable
    {
        
    public void run()
        
    {
            System.out.println(
    "runnable run");
        }

    }

    不要調用run()方法,它只是執行一下普通的方法,并不會啟動單獨的線程。

    Untitled-1.gif

    上面只是線程狀態圖。


    在某一個時間內,處于運行狀態的線程,執行代碼,注意可能多個線程多次執行代碼。

    CPU會不斷從可運行狀態線程調入運行,不會讓CPU空閑。


    Thread.sleep(1000);當前線程睡眠1秒鐘,休眠后->進入阻塞->休眠結束->回到可運行狀態

    在run(),有異常拋出,必須try{}catch(Exception e){},不能throws Exception,因為run()方法覆蓋不能拋例外。

    能進入運行狀態,只能由操作系統來調度。

    一旦sleep-〉阻塞->交出程序執行權。


    等待用戶輸入,輸入輸出設備占用CPU,處于阻塞的線程沒有機會運行,輸入完畢,重新進入可運行狀態。


    第三種進入阻塞狀態的可能。

    t1.join()調用后,運行狀態線程放出執行權,作為t1的后續線程,等待t1結束。也就是說至少得等t1線程run完畢,才可能進入運行狀態來執行,可不是說t1執行完,一定馬上就是調用t1.join()的線程馬上進入可運行行狀態。只有操作系統有權利決定誰進入運行狀態。

    join的實質就是讓兩個線程和二為一,串行。

    t1.join();執行這條語句現場是被保護起來的。t1結束,調用線程有機會運行時,會從上次的位置繼續運行。

    public class TestThread{
        
    public static void main(String[] args)
        
    {
            Thread t1 
    = new MyThread();
            t1.start();
            
            
    for(int i = 0; i < 50; i++)
            
    {
                System.out.println(
    "#");
                
    if(i == 24)
                    
    try{
                        t1.join();
                    }
    catch(InterruptedException e)
                    
    {
                        e.printStackTrace();
                    }

            }

        }

    }


    class MyThread extends Thread
    {
        
    public void run()
        
    {
            
    for(int i = 0; i < 35; i++)
            
    {
                System.out.println(
    "*");
            }

        }

    }



    線程優先級,setPriotity(1--100),數越大,優先級越高。

    開發中不提倡自省設置優先級,操作系統可能忽略優先級,不具有跨平臺性(兩方面,可運行,執行效果一致),因為這種方式很粗略。

    static void yield(),運行狀態的線程(當前線程),調用yield方法,馬上交出執行權。回到可運行狀態。

    =============================

    Thread對象有個run方法,當start()時,Thread進行系統級調用,系統分配一個線程空間,此時對象可以獲得CPU時間片,一個順序執行流程可以獨立運行,線程結束,對象還在,只是系統回收線程。

    =============================


    兩個線程同時的資源,稱為臨界資源,會有沖突。

    堆棧數據結構,有一個char[]和一個index(表示實際長度,也表示下一個要插入元素的位置)。

    一個push操作,有兩個核心操作(加元素,修改index)。都執行和都沒執行,沒有問題。

    但假設一個線程做了一個步,就交出執行權,別的線程,執行同樣的代碼,會造成數據不一致

    數據完整性也是一個要在開發中注意的地方。

    ------------------------------------------

    所以為了保證數據安全,要給數據加鎖。

    一個Java對象,不僅有屬性和方法,還有別的東西。

    任何一個對象,都有一個monitor,互斥鎖標記,可以交給一個線程。

    只有拿到這個對象互斥鎖標記的線程,才能訪問這個對象。

    synchronized可以修飾方法和代碼塊

    synchronized(obj){
       obj.setValue(123);
    }

    不是每個線程都能進入這段代碼塊,只有拿到鎖標記的線程才能進入執行完,釋放鎖標記,給下一個線程。

    記住,鎖標記是對對象來說的,鎖的是對象。

    當synchronized標識方法時,那么就是鎖當前對象。

    class Stack{
        
    int index=0;
        
    char data[]=new char[6];
        
    public synchronized void push(char c){
                data[index]
    =c;
                System.out.println(
    "Push char: "+c+" ->");
                
                
    try{
                    Thread.sleep(
    1000);
                }

                
    catch(Exception e){}
                index
    ++;
            System.out.println(
    "--> Push "+c+" Completed  index="+index+"   Stack is "+data[0]+" "+data[1]+" "+data[2]);
        }

        
        
    public void pop(){
                index
    --;
        
                
    try{
                    Thread.sleep(
    10);
                }

                
    catch(Exception e){}    
                
                
    char c=data[index];
                data[index]
    =' ';
                System.out.println(
    "** Pop  "+c+"**  index="+index+"   Stack is "+data[0]+" "+data[1]+" "+data[2]);    

        }

    }
         

    class PushRunner implements Runnable{
        
    private Stack s;
        
    public PushRunner(Stack s){
            
    this.s=s;
        }

        
        
    public void run(){
                s.push(
    'c');
        }


        
    }


    class PopRunner implements Runnable{
        
    private Stack s;
        
    public PopRunner(Stack s){
            
    this.s=s;
        }

        
    public void run(){
            
    synchronized(s){
                s.pop();
            }

        }

    }

    public class TestSynchronize{
        
    public static void main(String[] args){
            Stack s
    =new Stack();
            s.push(
    'a');
            s.push(
    'b');
            
    new Thread(new PushRunner(s)).start();
            
    new Thread(new PopRunner(s)).start();
        }

    }



    注意此代碼中,Stack這個臨界資源類中的push方法中,有一個Thread.sleep,它讓當前進程阻塞,也就是讓擁有Stack對象s鎖標記的線程阻塞,但這時它并不釋放鎖標記

    所以Synchronized使用是有代價的,犧牲效率換數據安全,要控制synchronized代碼塊,主要是數據寫,修改做同步限制,讀就不用了。

    還有一點要注意:synchronized不能繼承, 父類的方法是synchronized,那么其子類重載方法中就不會繼承“同步”。

    一個線程可以擁有很多對象鎖標記,但一個對象的鎖標記只能給一個線程。

    等待鎖標記的線程,進入該對象的鎖池。

    1.jpg

    每個對象都有一個空間,鎖池,里面都是等待拿到該對象的鎖標記的線程。

    當然還是操作系統來決定誰來獲得鎖標記,在上一個鎖標記釋放掉后。



    死鎖,線程A拿到resourceA標記,去請求resourceB;線程B拿到resourceB標記,去請求resourceA;

    線程間通訊機制->協調機制

    一個對象不僅有鎖和鎖池,另外還有一個空間[等待隊列]。

    synchronized(路南){
          想要獲得路北資源的線程,調用路南.wait();將自己的所有鎖標記都釋放。以便其他線程滿足條件運行程序后,自己也就可以正常通過了。
    }

    調用obj.wait(),表示某一個線程釋放所有鎖標記并進入obj這個對象的等待隊列。

    等待隊列也是阻塞狀態。一個線程調用obj對象的notify(),會通知等待隊列中的一個線程可以出來,notifyAll()是通知所有線程。

    Untitled-1.gif

     

     

    public class ProducerConsumer{
        
    public static void main(String[] args){
            SyncStack s
    =new SyncStack();
            Runnable p
    =new Producer(s);
            Runnable c
    =new Consumer(s);
            
    new Thread(p).start();
            
    new Thread(c).start();
        }

    }



    class Producer implements Runnable{
        SyncStack s;
        
    public Producer(SyncStack s){
            
    this.s=s;
        }

        
        
    public void run() {
            
    for(int i=1;i<=20;i++){
                
    char c=(char)(Math.random()*26+'A');
                s.push(c);
                
    try{
                    Thread.sleep(
    20);
                }

                
    catch (InterruptedException e){
                    e.printStackTrace();
                }

            }

        }

    }


    class Consumer implements Runnable{
        SyncStack s;
        
    public Consumer(SyncStack s){
            
    this.s=s;
        }

        
        
    public void run(){
            
    for(int i=1;i<=20;i++){
                
    char c=s.pop();
                
    try{
                    Thread.sleep(
    400);
                }

                
    catch (InterruptedException e){
                    e.printStackTrace();
                }

            }

        }

    }


    class SyncStack{
        
    private int index=0;  // the index next char added into, also presents the number of chars in stack
        private char[] data=new char[6];
        
        
    public synchronized void push(char c) {
            
    while (index==data.length)  {
                
    try{
                    
    this.wait();
                }

                
    catch (InterruptedException e){}
            }

            
            data[index]
    =c;
            index
    ++;
            System.out.println(
    "Char "+c+" Pushed into Stack");
            
    for(int k=0;k<data.length;k++) System.out.print(data[k]);
            System.out.println();
            
            
    this.notifyAll();

        }

        
        
    public synchronized char pop()  {
            
    while (index==0)  {
                
    try{
                    
    this.wait();
                }

                
    catch (InterruptedException e){}
            }

            
            index
    --;
            
    char c=data[index];
            data[index]
    =' ';
            System.out.println(
    "Char "+c+" Poped  from Stack");
            
            
    for(int k=0;k<data.length;k++) System.out.print(data[k]);
            System.out.println();
            
            
    this.notifyAll();

            
    return c;
        }

    }

    上面為經典的生產者消費者問題,生產者使用SyncStack的push方法,消費者使用pop方法。


    push方法:

      while (index==data.length)  {
       try{
        this.wait();//<-----------釋放所有鎖標記,阻塞現場保留
       }
       catch (InterruptedException e){}
      }

    如果貨架滿了,生產者即使擁有鎖標記,也不能再生產商品了,必須wait()。等待消費者消費物品,否則永遠不會從SyncStack對象的等待隊列中出來。

    等待通知,何時通知呢?

    public synchronized char pop()  {
      while (index==0)  {
       try{
        this.wait();
       }
       catch (InterruptedException e){}
      }
      
      index--;
      char c=data[index];
      data[index]=' ';
      System.out.println("Char "+c+" Poped  from Stack");
      
      for(int k=0;k<data.length;k++) System.out.print(data[k]);
      System.out.println();
      
      this.notifyAll(); //<-------------所有等待隊列中的生產者都出了隊列,因為沒有鎖標記,只能進入鎖池。

      return c;
     }

    為什么判斷是一個while循環,而不是一個if呢?

    注意:我們假設一種情形:

    1) 有十個生產者線程,貨架已經滿了,10生產者依次獲得鎖標記,依次都調用this.wait(),都進入同一個SyncStack對象的等待隊列,10個進程阻塞住,

    2) 有一個消費者線程獲得該SyncStack對象鎖標記,一個消費者消費一個,執行完消費,調用this.notifyAll()
    釋放鎖標記。

    3) 剛才消費者調用SyncStack對象的notifyAll()后,10個線程都出來了,準備生產商品,全部進入鎖池。

    這十個線程的代碼現場,還在wait()這個函數調用后面,也就是一旦或者鎖標記,要繼續從這里執行。

    this.wait();//從這一句的后面繼續執行。

    4) 但如果有一個生產者push的話,貨架已經就滿了,但這時還有9個在鎖池中,依次獲得鎖標記,但由于是while需要再次判斷是否貨架滿不滿,才能繼續前行進行生產。如果是if,就會直接push,數組越界。


    ===================================

    釋放鎖標記只有兩種途徑,代碼執行完,wait()

    讓線程結束,就是想辦法讓run方法結束。

    注意下面的bStop,標志位,可以在線程進入wait狀態時,對某一線程調用interrupt(),線程拋出InterruptedException,然后根據標志位,方法返回。

    ;
    class TestInterrupt
    {
        
    public static void main(String[] args)
        
    {
            Thread1 t1
    =new Thread1();
            t1.start();
            
    int index=0;
            
    while(true)
            
    {
                
    if(index++==500)
                
    {
                    t1.stopThread();
                    t1.interrupt();
                    
    break;
                }

                System.out.println(Thread.currentThread().getName());
            }

            System.out.println(
    "main() exit");
        }

    }


    class Thread1 extends Thread
    {
        
    private boolean bStop=false;
        
    public synchronized void run()
        
    {
            
    while(!bStop)
            
    {
                
    try    
                
    {
                    wait();
                }

                
    catch(InterruptedException e)
                
    {
                    
    //e.printStackTrace();
                    if(bStop)  return;
                }

                
                System.out.println(getName());
            }

        }

        
    public void stopThread()
        
    {
            bStop
    =true;
        }

    }

    Exc:

    AB1CD2....
     
    數字與字母依次打印。用線程完成。

    public class Test{
        
    public static void main(String[] args)
        
    {
            Object o 
    = new Object();
            PrintChar pc 
    = new PrintChar(o);
            PrintNum pn 
    = new PrintNum(o);
            pn.start();
            pc.start();
            
        }


    }

    class PrintChar extends Thread{
        
    private int index = 0;
        
    private Object obj = null;
        
        
    public PrintChar(Object o)
        
    {
            
    this.obj = o;
        }

        
    public void run(){
        
        
    synchronized(obj){
                
    for( ;index < 26; index++)
                
    {
                    
                        System.out.print((
    char)(index+'A'));
                        obj.notifyAll();
                        
    if(index != 25)
                            
    try{
                                    obj.wait();
                            }
    catch(InterruptedException e){}
                }
            
        }
        
    }

    }


    class PrintNum extends Thread{
      
    private int index = 1;
      
    private Object obj = null;
      
    public PrintNum(Object o)
      
    {
          
    this.obj = o;
      }

      
        
    public void run(){
            
            
    synchronized(obj){
                
    for( ;index < 53;index++)
                
    {
                        System.out.print(index);                    
                        
    if(index%2==0){            
                            obj.notifyAll();            
                            
    if(index != 52){
                                
    try{                        
                                        obj.wait();                        
                                }
    catch(InterruptedException e)
                                
    {}
                            }

                        }
                    
                    
                }

            }

        }


    }


    posted on 2005-12-15 00:32 北國狼人的BloG 閱讀(581) 評論(2)  編輯  收藏 所屬分類: 達內學習總結

    評論:
    # re: Java 多線程編程 2008-03-21 09:18 | hhzhaoheng
    非常感謝!  回復  更多評論
      
    # re: Java 多線程編程 2008-11-17 21:11 | yiminghe
    看看 java 多線程設計模式  回復  更多評論
      
    主站蜘蛛池模板: 亚洲国产精品国自产电影| 永久免费AV无码国产网站 | 亚洲国产精品专区在线观看| 四虎成人免费网站在线| 成年人免费观看视频网站| 青春禁区视频在线观看直播免费 | 两个人看的www高清免费视频| 国产精品午夜免费观看网站| fc2成年免费共享视频18| 2022国内精品免费福利视频| 黄色视频在线免费观看| 东北美女野外bbwbbw免费| 两个人看的www高清免费观看| 日本不卡免费新一区二区三区| 免费一级毛片在线播放视频| 三年片在线观看免费大全电影| 精品无码无人网站免费视频| 免费v片在线观看视频网站| www.黄色免费网站| 成人免费无毒在线观看网站 | 免费国产成人午夜在线观看| 国产一区二区三区免费| 精品无码国产污污污免费网站 | 久久久久久久国产免费看| 国内精品免费视频精选在线观看 | 特级毛片全部免费播放| 一个人看的www在线免费视频| 99在线视频免费观看| 18级成人毛片免费观看| 最近中文字幕无吗免费高清| avtt亚洲天堂| 亚洲AV无码成人精品区天堂| 亚洲小视频在线播放| 色五月五月丁香亚洲综合网| 福利免费在线观看| 最近中文字幕无免费| 日韩免费高清一级毛片在线| 国产L精品国产亚洲区久久| 中文字幕亚洲综合精品一区| 亚洲a∨国产av综合av下载| 在线观看免费黄网站|