1. 程式人生 > >JAVA執行緒(升級)

JAVA執行緒(升級)

同步方法

package com.thread.ticket3;

/**
 * 同步程式碼塊中的物件針對多個執行緒必須是同一個,
 * 其實這個物件被稱為同步鎖物件
 * 
 * 同步程式碼塊的鎖物件是誰呢?
 * 任意物件
 * 
 * 如果一個方法,一進去我們就看到程式碼被同步了,所以,我就想,這個同步能不能加在方法上面呢?
 *  能:這個東西被稱為同步方法:就是把同步關鍵字加在同步方法上。
 *  
 * 現在我們在這樣的一個程式碼中
 *  if(){
 *      同步程式碼塊
 *  }else{
 *      同步方法
 *      private synchronized void show()
 *  }
 *  
 *  又會出現負票,為什麼會有問題呢?
 *      就是同步的程式碼,必須保證鎖物件一致。
 *  同步方法的鎖物件是誰呢?
 *      this物件
 *  靜態同步方法的鎖物件是誰呢?
 *      Ticket.class 位元組碼檔案物件
 *     當前類的位元組碼檔案物件
 */
public class TicketDemo { public static void main(String[] args) { Ticket t1 = new Ticket(); Thread t = new Thread(t1, "視窗1"); Thread t4 = new Thread(t1, "視窗2"); Thread t2 = new Thread(t1, "視窗3"); Thread t3 = new Thread(t1, "視窗4"); t.start(); t4.start(); t2.start(); t3.start(); } } package
com.thread.ticket3; public class Ticket implements Runnable { private static int tickets = 100; //private final Object b = new Object(); private int x = 0; @Override public void run() { while (true) { if (x % 2 == 0) { synchronized
(this) { if (tickets > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "張票"); } } } else { show(); } x++; } } //同步方法 private static synchronized void show() { if (tickets > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "張票"); } } }

上面在同步方法上加了 static 又導致了執行緒安全問題 如下圖:
這裡寫圖片描述

那麼靜態同步方法的物件又是什麼呢??

預設當前類的位元組碼檔案物件,所以要將同步程式碼塊中的物件改為Ticket.class  同步方法和同步程式碼塊中的物件一致。
 synchronized (Ticket.class) {
                    if (tickets > 0) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "張票");

                    }
                }

JDK1.5 的新特性

package com.thread.ticket4;

/**
 * jdk1.5的新特性
 * 以前同步關鍵字的方式,可以實現同步,並且是我們比較常見的方式,
 * 但是我們老是需要去考慮它使用的哪個鎖物件,比較麻煩
 * 所以,在JDK5以後,提供了一個新的方式: 
 *  Lock鎖物件
 * 這種方式在以前的程式碼中比較少見,但是以後開發中可以這樣中
 * vector:執行緒安全,加了synchronizd
 * ArrayList:執行緒不安全 
 * hashTable:執行緒安全
 * hashMap :執行緒不安全
 * 
 * @author yuliyang
 * @version $Id: TicketDemo.java, v 0.1 2016年12月4日 下午7:28:39 yuliyang Exp $
 */
public class TicketDemo {
    public static void main(String[] args) {
        Ticket t = new Ticket();
        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        Thread t3 = new Thread(t);
        Thread t4 = new Thread(t);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

package com.thread.ticket4;

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

public class Ticket implements Runnable {
    private int        ticket = 100;

    //private final ReentrantLock lock = new ReentrantLock();
    //定義一個鎖物件
    private final Lock lock   = new ReentrantLock();

    @Override
    public void run() {

        while (true) {
            lock.lock();
            try {
                Thread.sleep(100);
                if (ticket > 0) {
                    System.out.println(Thread.currentThread().getName() + "---正在出售:" + (ticket--) + "張票");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                //釋放鎖
                lock.unlock();
            }
        }
    }

}

生產者與消費者問題

package com.thread.ticket5;

/**
 * 執行緒間的通訊問題:
 *      不同種類的執行緒對同一資源的操作問題。
 * 
 * 需求:我有一個學生,我可以對其屬性賦值,我也可以獲取其屬性值,請用執行緒間的通訊的案例來體現
 * 
 * 資源:學生student
 * 獲取執行緒:GetThread
 * 設定執行緒:SetThread
 * 測試類:StudentDemo
 * 
 * 問題一:每個執行緒操作的是自己的那一個學生物件,
 *  怎麼解決:讓多個執行緒操作同一個學生物件
 * 問題二:因為執行緒的隨機性,一次賦值和獲取值得操作,資料出現了問題
 *  怎麼解決:
 * 問題三:執行緒操作的東西一般都是比較複雜的操作
 * 所以我們加入了迴圈操作。產生了更大的問題。
 * 資料產生了混亂
 *  A:同一資料出現多次:
 *      CPU的一點點時間就足夠一個執行緒執行很多次。
 *  B:資料出現了問題:
 *      由於CPU每一次的執行點應該是一次原子性的操作。
 *  解決執行緒安全問題:
 *      分析:真的是執行緒安全問題嗎?
 *          1:是多執行緒環境嗎?
 *              是
 *          2:有共享資料嗎?
 *              有:student
 *          3:對共享資料有多條語句操作嗎?    
 *              有:s.name s.age
 *  
 *  如何解決?
 *      把出現問題的程式碼用同步程式碼塊包起來。
 *  問題4:加入同步方法塊之後還是出現了問題。為什麼?
 *      A:多種執行緒進行操作的時候,要保證同步,必須是每一種型別的執行緒都必須加同步
 *      B:即使是給多種型別的執行緒加鎖,也必須是同一把鎖
 *  到此解決了執行緒安全問題。
 *  
 * @author yuliyang
 * @version $Id: TicketDemo.java, v 0.1 2016年12月4日 下午7:28:39 yuliyang Exp $
 */
public class StudentDemo {
    public static void main(String[] args) {
        Student student = new Student();
        SetThread st = new SetThread(student);
        GetThread gt = new GetThread(student);
        Thread g = new Thread(gt);
        Thread s = new Thread(st);
        s.start();
        g.start();
    }
}


package com.thread.ticket5;

public class Student {
    private String name;
    private String age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

}
package com.thread.ticket5;

public class SetThread implements Runnable {
    private final Student s;
    private int           x = 0;

    public SetThread(Student s) {
        this.s = s;
    }

    @Override
    public void run() {
        while (true) {
            //加同步,加同一把鎖(s)
            synchronized (s) {
                if (x % 2 == 0) {
                    s.setName("aaa");
                    s.setAge("20");
                } else {
                    s.setName("vvv");
                    s.setAge("10");
                }
            }
            x++;
        }
    }
}

package com.thread.ticket5;

public class GetThread implements Runnable {
    private final Student student;

    public GetThread(Student student) {
        this.student = student;
    }

    @Override
    public void run() {
        while (true) {
            //加同步,加同一把鎖
            synchronized (student) {
                System.out.println(student.getName() + ":" + student.getAge());
            }
        }
    }
}

等待喚醒機制

package com.thread.ticket6;

/**
 * 執行緒間的通訊問題:
 *      不同種類的執行緒對同一資源的操作問題。
 * 
 * 需求:我有一個學生,我可以對其屬性賦值,我也可以獲取其屬性值,請用執行緒間的通訊的案例來體現
 * 
 * 資源:學生student
 * 獲取執行緒:GetThread
 * 設定執行緒:SetThread
 * 測試類:StudentDemo
 * 
 * 問題一:店鋪生產了包子,客人才可以買,賣完了,店鋪再生產
 *  剛才的資料是大片大片的資料,此時需要改為:只有設定了學生的屬性,才能獲
 *  取到學生的屬性值,依次的出現,怎麼辦呢?
 * 原理:
 *      如果我的學生屬性沒有值,應該先賦值,然後才可以使用,
 *      如果我的學生屬性有值,應該使用後再賦值。
 * 在Student中加入了一個flag標記,false表示沒有值
 * 
 * 為了配合這種操作,java提供了一種機制:等待喚醒機制。
 *  notify(); //該方法在Object中
 *  wait();  //該方法在Object中
 *  這兩個方法的呼叫,必須是鎖物件(物件監視器)呼叫。
 *      為什麼這兩個方法沒有定義到Thread類裡面呢,
 *      因為如果放到了Thread類中,就得使用Thread物件去呼叫這兩個方法,
 *      但是,同步程式碼塊的鎖物件是任意的。那麼只能用Object。
 * 怎麼用?
 *  看程式碼例子。
 *  
 */
public class StudentDemo {
    public static void main(String[] args) {
        Student student = new Student();
        SetThread st = new SetThread(student);
        GetThread gt = new GetThread(student);
        Thread g = new Thread(gt);
        Thread s = new Thread(st);
        s.start();
        g.start();
    }
}


package com.thread.ticket6;

public class SetThread implements Runnable {
    private final Student student;
    private int           x = 0;

    public SetThread(Student s) {
        this.student = s;
    }

    @Override
    public void run() {
        while (true) {
            //加同步,加同一把鎖(s)
            synchronized (student) {
                //如果有值:等待,(店家如果有包子,就等它賣完)
                if (student.getFlag()) {
                    try {
                        student.wait();
                        /**
                         * s執行緒就等待在這裡了。
                         */
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                //設定值(如果沒有包子,就設定值)
                if (x % 2 == 0) {
                    student.setName("餘麗陽");
                    student.setAge("20");
                } else {
                    student.setName("簡小慧");
                    student.setAge("10");
                }
                x++;

                //有值後,修改標記為true(告訴顧客有包子了)
                student.setFlag(true);
                //喚醒等待程式
                student.notify();
                /**
                 * 喚醒等待的執行緒,喚醒其他的執行緒,不代表其他的執行緒就能夠立即執行
                 */
            }
            //可能g執行緒,可能s執行緒
        }
    }
}


package com.thread.ticket6;

public class GetThread implements Runnable {
    private final Student student;

    public GetThread(Student student) {
        this.student = student;
    }

    @Override
    public void run() {
        while (true) {
            //加同步,加同一把鎖
            synchronized (student) {
                //如果沒有值,就等待。(顧客發現如果沒有包子,就等著)
                if (!student.getFlag()) {
                    try {
                        student.wait();
                        /**
                         * g執行緒在這裡等待了,那麼它就會釋放鎖資源。
                         * 將來,當它再次獲取鎖物件的時候,
                         * 是從哪裡等待,哪裡醒來
                         */
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //如果有值,則輸出(如果有包子,就買了吃)
                System.out.println(student.getName() + ":" + student.getAge());
                //告訴店家沒有包子了
                student.setFlag(false);
                //喚醒:(叫店家做包子)
                student.notify();
            }
            //可能g執行緒,可能s執行緒
        }
    }
}


package com.thread.ticket6;

public class Student {
    private String  name;
    private String  age;
    private boolean flag = false; //預設表示沒有值

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    public boolean getFlag() {
        return flag;
    }

    public void setFlag(boolean f) {
        this.flag = f;
    }
}

死鎖

package com.thread.DieLock;

/**
 * 舉例:
 *  五個哲學家的故事
 *  
 * 面試題:
 *  死鎖例子:
 *      有兩把鎖 A,B 一個執行緒先使用了A鎖,然後使用B鎖
 *                一個執行緒先使用了B鎖,然後使用了A鎖。
 * 
 * @author yuliyang
 * @version $Id: DieLockDemo.java, v 0.1 2016年12月4日 下午9:59:10 yuliyang Exp $
 */
public class DieLockDemo {
    public static void main(String[] args) {
        DieLock d1 = new DieLock(true);
        DieLock d2 = new DieLock(false);
        d2.start();
        d1.start();
    }

}


package com.thread.DieLock;

public class DieLock extends Thread {
    private final boolean flag;

    public DieLock(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        if (flag) {
            synchronized (MyLock.objA) {
                System.out.println("if objA");
                synchronized (MyLock.objB) {
                    System.out.println("if objB");
                }

            }
        } else {
            synchronized (MyLock.objB) {
                System.out.println("else objB");
                synchronized (MyLock.objA) {
                    System.out.println("else objA");
                }
            }
        }
    }
}


package com.thread.DieLock;

public class MyLock {
    public static final Object objA = new Object();
    public static final Object objB = new Object();
}