再學(xué)—Java基礎(chǔ):多線程(2)

上一篇獲得了三個喜歡和一個關(guān)注地来,給我了繼續(xù)寫下去的動力,謝謝熙掺,謝謝N窗摺!币绩!
以前沒寫過蜡秽,寫的時候發(fā)現(xiàn)真的很有挑戰(zhàn),自己也從中收獲很多缆镣,希望簡友們覺得如果覺得有一篇文章真的給自己一點感動芽突,一點幫助等,請給作者一個贊董瞻,可能真的會產(chǎn)生蝴蝶效應(yīng)寞蚌,再次謝謝你們看我的文章。

以上都是廢話钠糊,哈哈挟秤。接著上篇,繼續(xù)討論多線程的數(shù)據(jù)安全問題

  • 為什么會出現(xiàn)重復(fù)的數(shù)據(jù)和負數(shù)的數(shù)據(jù)的情況抄伍?
    因為多線程操作的是共享數(shù)據(jù)艘刚,mTicket,是三個線程的共享數(shù)據(jù)截珍。如果每個線程都有自己單獨的數(shù)據(jù)昔脯,是沒有問題啄糙,因為那是單線程了。

重復(fù)的數(shù)據(jù):System.out.println(Thread.currentThread().getName() + " 出售車票= " + mTicket-- + " 張");這行代碼翻譯成計算機可以執(zhí)行的指令云稚,當0號線執(zhí)行sop()隧饼,輸出7,但是剛要執(zhí)行--操作的時候静陈。1號線線程搶到了CPU的執(zhí)行權(quán)燕雁,這時候0號線程沒有執(zhí)行 -- 操作,所以mTicket 還是7鲸拥,所以輸出7拐格,出現(xiàn)重復(fù)的數(shù)據(jù)。

負數(shù)的數(shù)據(jù):當mTicket =1時刑赶,0號線程做判斷 > 1,進入sleep(10)捏浊,放棄執(zhí)行資格和執(zhí)行權(quán),1號線程判斷 >0,進去進入sleep(10)撞叨,這時候0號線程醒來搶到執(zhí)行資格金踪,輸入0,1號線程醒來牵敷,搶到執(zhí)行資格胡岔,輸入-1。

  • 怎么解決多線程操作共享數(shù)據(jù)的問題呢枷餐?
  1. 同步代碼塊 格式 : synchronized(對象){ 需要同步的代碼}

把多個語句操作共享數(shù)據(jù)的代碼給鎖起來靶瘸,讓任意時刻只能有一個線程執(zhí)行。

public class RunnableDemo1 implements Runnable {
    //定義一百張車票
    private int mTicket = 100;
    private Object obj = new Object();

    @Override
    public void run() {
      while(true){
        synchronized (obj) {
            if(mTicket > 0) {
                // 模擬卡機  讓線程休眠10毫秒
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } 

                System.out.println(Thread.currentThread().getName() + " 出售車票= " + mTicket-- + " 張");
            }
        }
    }
   }
}

執(zhí)行效果圖.png

用同步代碼塊就可以解決毛肋,這里主要是那個obj怨咪,多線程同步的時候,必須是同一把鎖润匙。要不然還會出現(xiàn)共享數(shù)據(jù)的問題诗眨。

  1. 同步方法:就是把關(guān)鍵字synchronized 加在方法上
 public class RunnableDemo implements Runnable {
    //定義一百張車票
    private int mTicket = 100;

    @Override
    public void run() {
        while (true) {
                sale();
        }
    }

    private synchronized void sale() {
        if (mTicket > 0) {
            // 模擬卡機  讓線程休眠10毫秒
//            try {
//                Thread.sleep(10);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
            System.out.println(Thread.currentThread().getName() + " 出售車票= " + mTicket-- + " 張");
        }
    }
}

非靜態(tài)方法的鎖是本類對象的引用 this。

public class RunnableDemo1 implements Runnable {
    //定義一百張車票
    private static int mTicket = 100;

    @Override
    public void run() {
        while (true) {
                sale();
        }
    }

    private synchronized static void sale() {
        if (mTicket > 0) {
            // 模擬卡機  讓線程休眠10毫秒
//            try {
//                Thread.sleep(10);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
            System.out.println(Thread.currentThread().getName() + " 出售車票= " + mTicket-- + " 張");
        }
    }
}

靜態(tài)方法的鎖是本類對象的class 字節(jié)碼文件趁桃。

  • 總結(jié)下:
  1. 同步的前提
    多個線程使用的是同一個鎖對象
    多個線程操作共享資源
  2. 同步的好處
    同步的出現(xiàn)解決了多線程的安全問題辽话。
  3. 同步的弊端
    當線程相當多時肄鸽,因為每個線程都會去判斷同步上的鎖卫病,這是很耗費資源的,無形中會降低程序的運行效率典徘,但是為了數(shù)據(jù)的安全也值了蟀苛,魚和熊掌不能兼得。
  • 兩個線程實現(xiàn)交替打哟濉(等待與喚醒機制)
/**
 * 兩個線程 實現(xiàn)交替打印 一
 * <p>
 * 輸入線程   輸出 一
 * <p>
 * 輸出線程   輸出 二
 */
public class AlternatePrintThread {

    public static boolean mFlag = true;

    public static void main(String[] args) {


        new Thread(new InputThread()).start();

        new Thread(new OutputThread()).start();
    }


}


class InputThread implements Runnable {

    @Override
    public void run() {
        while (true) {
            synchronized (AlternatePrintThread.class) {

                if (AlternatePrintThread.mFlag) {
                    try {
                        AlternatePrintThread.class.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                AlternatePrintThread.mFlag = true;
                System.out.println("我是一 ");
                AlternatePrintThread.class.notify();
            }
        }
    }
}

class OutputThread implements Runnable {
 

    @Override
    public void run() {
        while (true) {
            synchronized (AlternatePrintThread.class) {
                if (!AlternatePrintThread.mFlag) {
                    try {
                        AlternatePrintThread.class.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                AlternatePrintThread.mFlag = false;
                System.out.println(".......我是二 ");
                AlternatePrintThread.class.notify();
            }
        }
    }
}

實現(xiàn)思路:
交替打印肯定是兩個線程 定義兩個線程類
不管你用的什么對象作為鎖帜平,連個線程一定是同一把鎖

  • 多生產(chǎn)與多消費
public class ThreadDemo {

    public static void main(String[] args) {
        Product p = new Product();
        new Thread(new Produce(p)).start();
        new Thread(new Produce(p)).start();
        new Thread(new Produce(p)).start();
        new Thread(new Produce(p)).start();
        new Thread(new Consumer(p)).start();
        new Thread(new Consumer(p)).start();
        new Thread(new Consumer(p)).start();
        new Thread(new Consumer(p)).start();
    }
}


class Product {
    //名字
    private String name;
    //計數(shù)器
    private int count;
    //標記
    private boolean flag = true;
    //生產(chǎn)方法幽告,是讓生產(chǎn)線程調(diào)用
    public synchronized void set(String name) {
        while (!flag)
            try {
                this.wait();
            } catch (Exception e) {
            }
        this.name = name + count++;
        System.out.println(Thread.currentThread().getName() + "生產(chǎn)第  " + this.name);
        flag = false;
        this.notify();
    }

    //消費方法,是讓消費線程調(diào)用
    public synchronized void get() {
        while (flag)
            try {
                this.wait();
            } catch (Exception e) {
            }
        System.out.println(Thread.currentThread().getName() + "消費第... " + this.name);
        flag = true;
        this.notify();
    }
}

//生產(chǎn)者線程
class Produce implements Runnable {
    private Product p;

    Produce(Product p) {
        this.p = p;
    }

    public void run() {
        while (true)
            p.set("Iphone");
    }
}

//消費者線程
class Consumer implements Runnable {
    private Product p;

    Consumer(Product p) {
        this.p = p;
    }

    public void run() {
        while (true)
            p.get();
    }
}

notify():喚醒的是等待順序喚醒(這是的 多生產(chǎn)與多消費不能用這個)
notifyAll():喚醒的是等待的全部線程 (太浪費資源裆甩,如果我想喚醒 A線程的按等待順序的線程的一個 就好了 冗锁,所以Lock接口來了)
線程還有一個特點就是 從那倒下去的 從那起來繼續(xù)執(zhí)行(要想讓一段代碼流轉(zhuǎn)起來,那就是死循環(huán)嗤栓,滿足條件才讓出去冻河,否則一直轉(zhuǎn)圈)

  • jdk since 1.5出現(xiàn)行的管理線程的類
/**
 * since JDK1.5   Lock ,Condition 生產(chǎn)者 和 消費者
 */
public class LockDemo {
    public static void main(String[] args) {
        Resource resource = new Resource();
        new Thread(new ConsumeThread(resource)).start();
        new Thread(new ConsumeThread(resource)).start();
        new Thread(new ConsumeThread(resource)).start();
        new Thread(new ConsumeThread(resource)).start();

        new Thread(new ProductThread(resource)).start();
        new Thread(new ProductThread(resource)).start();
        new Thread(new ProductThread(resource)).start();
        new Thread(new ProductThread(resource)).start();
    }
}

/**
 * 資源類
 */
class Resource {
    private String productName;
    private int count = 0;
    private boolean flag = true;


    private Lock lock = new ReentrantLock();
    // 用鎖創(chuàng)建了 兩個線程的管理器   用自己的管理器去讓自己的管理的線程等待  用對方的管理器去喚醒對方等待的線程
    private Condition pro = lock.newCondition();
    private Condition con = lock.newCondition();

    public void setProductName(String productName) {
        lock.lock();
        while (!this.flag) {
            try {
                pro.await();//自己的線程管理器
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.productName = productName + "   " + count++;
        System.out.println("生產(chǎn)=" + this.productName);
        this.flag = false;//    和  while (this.flag) 方法必須向反 要不 這個線程會無限等待
        con.signal();//對方的線程管理器
        lock.unlock();
    }

    public void getProductName() {

        lock.lock();
        while (this.flag) {
            try {
                con.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("消費=......" + this.productName);
        this.flag = true;
        pro.signal();
        lock.unlock();

    }
}


/**
 * 生產(chǎn)線程
 */
class ProductThread implements Runnable {
    Resource mResource;

    public ProductThread(Resource resource) {
        this.mResource = resource;
    }

    @Override
    public void run() {
        while (true) {
            mResource.setProductName("Iphone100");
        }
    }
}

/**
 * 消費線程
 */
class ConsumeThread implements Runnable {
    Resource mResource;

    public ConsumeThread(Resource resource) {
        this.mResource = resource;
    }

    @Override
    public void run() {
        while (true) {
            mResource.getProductName();
        }
    }
}

JDK1.5中茉帅,java.util.concurrent.locks包
提供線程的新的管理方式
接口Lock叨叙,替代了原有的synchronized的使用,使用靈活廣泛
接口中的方法 獲取鎖lock() 釋放鎖 unlock()
接口實現(xiàn)類[ReentrantLock]
接口Condition 替代原有監(jiān)視器方法 wait notify notifyAll
新舊方法的對比
接口中Condition Object類
await() wait()
signal() notify()
signalAll() notifyAll()
獲取接口的實現(xiàn)類對象堪澎,用Lock接口方法newCondition實現(xiàn)
分解成截然不同對象(線程管理對象)

  • 死鎖
/**
 * 多線程的死鎖
 */
public class DeadLockDemo {


    public static class DeadRunnable implements Runnable {
        private boolean mFlag;

        public DeadRunnable(boolean flag) {
            this.mFlag = flag;
        }

        @Override
        public void run() {
            while (true) {
                if (mFlag) {
                    //A鎖
                    synchronized (ALock.mALock) {
                        System.out.println("A..............鎖");
                        synchronized (BLock.mBLock) {
                            System.out.println("B..............鎖");
                        }
                    }
                } else {
                    //B鎖
                    synchronized (BLock.mBLock) {
                        System.out.println("B..............鎖");
                        synchronized (ALock.mALock) {
                            System.out.println("A..............鎖");

                        }
                    }
                }
            }
        }
    }


    /**
     * A鎖
     */
    public static class ALock {
        public static final ALock mALock = new ALock();
    }

    /**
     * B鎖
     */
    public static class BLock {
        public static final BLock mBLock = new BLock();
    }


    public static void main(String[] args) {
        new Thread(new DeadRunnable(true)).start();
        new Thread(new DeadRunnable(false)).start();

    }

}

多線程的一種程序的假死狀態(tài)擂错,同步的嵌套,停了樱蛤,但是沒退出钮呀,出現(xiàn)在多線程爭搶同一個同步鎖的時候,才會出現(xiàn)

如有出入刹悴,望大佬扶正行楞,謝謝!M猎取子房!!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市就轧,隨后出現(xiàn)的幾起案子证杭,更是在濱河造成了極大的恐慌,老刑警劉巖妒御,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件解愤,死亡現(xiàn)場離奇詭異,居然都是意外死亡乎莉,警方通過查閱死者的電腦和手機送讲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來惋啃,“玉大人哼鬓,你說我怎么就攤上這事”呙穑” “怎么了异希?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長绒瘦。 經(jīng)常有香客問我称簿,道長扣癣,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任憨降,我火速辦了婚禮父虑,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘授药。我一直安慰自己频轿,他們只是感情好,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布烁焙。 她就那樣靜靜地躺著航邢,像睡著了一般。 火紅的嫁衣襯著肌膚如雪骄蝇。 梳的紋絲不亂的頭發(fā)上膳殷,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天,我揣著相機與錄音九火,去河邊找鬼赚窃。 笑死,一個胖子當著我的面吹牛岔激,可吹牛的內(nèi)容都是我干的勒极。 我是一名探鬼主播,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼虑鼎,長吁一口氣:“原來是場噩夢啊……” “哼辱匿!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起炫彩,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤匾七,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后江兢,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體昨忆,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年杉允,在試婚紗的時候發(fā)現(xiàn)自己被綠了邑贴。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡叔磷,死狀恐怖拢驾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情世澜,我是刑警寧澤独旷,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布署穗,位于F島的核電站寥裂,受9級特大地震影響嵌洼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜封恰,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一麻养、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧诺舔,春花似錦鳖昌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至褥赊,卻和暖如春糕档,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背拌喉。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工速那, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人尿背。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓端仰,卻偏偏與公主長得像,于是被迫代替她去往敵國和親田藐。 傳聞我的和親對象是個殘疾皇子荔烧,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360