java多線程系列3-線程同步,java多線程3-線程

來源:互聯網
上載者:User

java多線程系列3-線程同步,java多線程3-線程

如果一個資源被多個線程同時訪問,可能會遭到破壞,這篇文章介紹java線程同步來解決這類問題

引入問題

某電影院目前正在上映賀歲大片,共有100張票,而它有3個售票視窗售票,請設計一個程式類比該電影院售票。

方法一:繼承Thread類

public class SellTicket extends Thread {     // 定義100張票    // private int tickets = 100;    // 為了讓多個線程對象共用這100張票,我們其實應該用靜態修飾    private static int tickets = 100;     @Override    public void run() {        // 定義100張票        // 是為了類比一直有票        while (true) {            if (tickets > 0) {                System.out.println(getName() + "正在出售第" + (tickets--) + "張票");            }        }    }}/* * 繼承Thread類來實現。 */public class SellTicketDemo {    public static void main(String[] args) {        // 建立三個線程對象        SellTicket st1 = new SellTicket();        SellTicket st2 = new SellTicket();        SellTicket st3 = new SellTicket();         // 給線程對象起名字        st1.setName("視窗1");        st2.setName("視窗2");        st3.setName("視窗3");         // 啟動線程        st1.start();        st2.start();        st3.start();    }}

方法二:實現Runnable介面

public class SellTicket implements Runnable {    // 定義100張票    private int tickets = 100;     @Override    public void run() {        while (true) {            if (tickets > 0) {                System.out.println(Thread.currentThread().getName() + "正在出售第"                        + (tickets--) + "張票");            }        }    }}/* * 實現Runnable介面的方式實現 */public class SellTicketDemo {    public static void main(String[] args) {        // 建立資來源物件        SellTicket st = new SellTicket();         // 建立三個線程對象        Thread t1 = new Thread(st, "視窗1");        Thread t2 = new Thread(st, "視窗2");        Thread t3 = new Thread(st, "視窗3");         // 啟動線程        t1.start();        t2.start();        t3.start();    }}

電影院售票程式,從表面上看不出什麼問題,在真實生活中,售票時網路是不能即時傳輸的,總是存在延遲的情況,所以,在出售一張票以後,需要一點時間的延遲

改實現介面方式的賣票程式,每次賣票延遲100毫秒,代碼如下:

public class SellTicket implements Runnable {    // 定義100張票    private int tickets = 100;    @Override    public void run() {        while (true) {            // t1,t2,t3三個線程            // 這一次的tickets = 1;            if (tickets > 0) {                // 為了類比更真實的情境,我們稍作休息                try {                    Thread.sleep(100); //t1進來了並休息,t2進來了並休息,t3進來了並休息,                } catch (InterruptedException e) {                    e.printStackTrace();                }                 System.out.println(Thread.currentThread().getName() + "正在出售第"                        + (tickets--) + "張票");                //視窗1正在出售第1張票,tickets=0                //視窗2正在出售第0張票,tickets=-1                //視窗3正在出售第-1張票,tickets=-2            }        }    }}/* * 實現Runnable介面的方式實現 */public class SellTicketDemo {    public static void main(String[] args) {        // 建立資來源物件        SellTicket st = new SellTicket();         // 建立三個線程對象        Thread t1 = new Thread(st, "視窗1");        Thread t2 = new Thread(st, "視窗2");        Thread t3 = new Thread(st, "視窗3");         // 啟動線程        t1.start();        t2.start();        t3.start();    }}

出現問題:

相同的票出現多次

CPU的一次操作必須是原子性的

還出現了負數的票

隨機性和延遲導致的

解決安全執行緒問題的基本思想與方法

首先想為什麼出現問題?(也是我們判斷是否有問題的標準)

  • 是否是多線程環境
  • 是否有共用資料
  • 是否有多條語句操作共用資料

如何解決多安全執行緒問題呢?

基本思想:讓程式沒有安全問題的環境。

把多個語句操作共用資料的代碼給鎖起來,讓任意時刻只能有一個線程執行即可。

解決安全執行緒問題實現1--同步代碼塊

格式:synchronized(對象){需要同步的代碼;}

同步可以解決安全問題的根本原因就在那個對象上。該對象如同鎖的功能。

修改上面的代碼如下:

public class SellTicket implements Runnable {    // 定義100張票    private int tickets = 100;    //建立鎖對象    private Object obj = new Object();        @Override    public void run() {        while (true) {            synchronized (obj) {                if (tickets > 0) {                    try {                        Thread.sleep(100);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                    System.out.println(Thread.currentThread().getName()                            + "正在出售第" + (tickets--) + "張票");                }            }        }    }}/* * 同步代碼塊: *         synchronized(對象){ *             需要同步的代碼; *         } *  *         A:對象是什麼呢? *             我們可以隨便建立一個對象試試。 *         B:需要同步的代碼是哪些呢? *             把多條語句操作共用資料的代碼的部分給包起來 *  *         注意: *             同步可以解決安全問題的根本原因就在那個對象上。該對象如同鎖的功能。 *             多個線程必須是同一把鎖。 */public class SellTicketDemo {    public static void main(String[] args) {        // 建立資來源物件        SellTicket st = new SellTicket();         // 建立三個線程對象        Thread t1 = new Thread(st, "視窗1");        Thread t2 = new Thread(st, "視窗2");        Thread t3 = new Thread(st, "視窗3");         // 啟動線程        t1.start();        t2.start();        t3.start();    }}

注意:同步代碼塊可以用任意對象做鎖

解決安全執行緒問題實現2--同步方法

就是把同步關鍵字加到方法上

1、同步方法的鎖對象:this

public class SellTicket implements Runnable {    private static int tickets = 100;    private Object obj = new Object();    @Override    public void run() {        while (true) {            synchronized (this) {                if (tickets > 0) {                    try {                        Thread.sleep(100);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                    System.out.println(Thread.currentThread().getName()                            + "正在出售第" + (tickets--) + "張票 ");                }            }        }    }    private synchronized void sellTicket() {        if(tickets > 0) {            try {                Thread.sleep(100);            } catch (InterruptedException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }            System.out.println(Thread.currentThread().getName()                    + "正在出售第" + (tickets--) + "張票 ");        }    }}

2、靜態方法的鎖對象:類的位元組碼檔案對象。

public class SellTicket implements Runnable {    private static int tickets = 100;    private Object obj = new Object();    @Override    public void run() {        while (true) {            synchronized (SellTicket.class) {                if (tickets > 0) {                    try {                        Thread.sleep(100);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                    System.out.println(Thread.currentThread().getName()                            + "正在出售第" + (tickets--) + "張票 ");                }            }        }    }    private static synchronized void sellTicket() {        if(tickets > 0) {            try {                Thread.sleep(100);            } catch (InterruptedException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }            System.out.println(Thread.currentThread().getName()                    + "正在出售第" + (tickets--) + "張票 ");        }    }}

同步的前提:

  • 多個線程
  • 多個線程使用的是同一個鎖對象

同步的好處:同步的出現解決了多線程的安全問題。

同步的弊端:當線程相當多時,因為每個線程都會去判斷同步上的鎖,這是很耗費資源的,無形中會降低程式的運行效率。

解決安全執行緒問題實現3--Lock鎖的使用

雖然我們可以理解同步代碼塊和同步方法的鎖對象問題,但是我們並沒有直接看到在哪裡加上了鎖,在哪裡釋放了鎖,為了更清晰的表達如何加鎖和釋放鎖,JDK5以後提供了一個新的鎖對象Lock

ReentrantLock (Java Platform SE 6)

一個可重新進入的互斥鎖 Lock,它具有與使用 synchronized 方法和語句所訪問的隱式監視器鎖相同的一些基本行為和語義,但功能更強大。

import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock; public class SellTicket implements Runnable {     // 定義票    private int tickets = 100;     // 定義鎖對象    private Lock lock = new ReentrantLock();     @Override    public void run() {        while (true) {            try {                // 加鎖                lock.lock();                if (tickets > 0) {                    try {                        Thread.sleep(100);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                    System.out.println(Thread.currentThread().getName()                            + "正在出售第" + (tickets--) + "張票");                }            } finally {                // 釋放鎖                lock.unlock();            }        }    } }

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.