線程的安全問題

線程安全問題:java

問題:當某個線程操做共享數據中,還沒有操做完成時,其餘線程參與進來,也操做共享數據。安全

解決方法:當一個線程在操做共享數據時,其餘線程不能參與進來。即便操做中的線程出現阻塞,也不能改變。多線程

例子:建立三個窗口賣票,總票數爲100張,使用實現Runable接口的方式。ide

JAVA 中經過同步機制 來解決線程的安全問題。this

方式1、同步代碼塊

 synchronized(同步監視器){spa

    //須要被同步的代碼   線程

code

說明:對象

1.操做共享數據的代碼,爲須要被同步的代碼繼承

2.共享數據,多個線程共同操做的變量,

3.同步監視器,俗稱:鎖。任何一個類的對象,均可以充當鎖。

    要求:多個線程必需要共用同一把鎖。

 補充:在實現Runable接口建立多線程的方式中,咱們能夠考慮使用this 充當同步監視器

使用實現 Runable方式:

public class TheardWindow {
    public static void main(String[] args) {
        TicketWindow w1 = new TicketWindow();
        Thread t1=new Thread(w1);
        Thread t2= new Thread(w1);
        Thread t3 =new Thread(w1);
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();;
        t2.start();
        t3.start();

    }
}
class TicketWindow implements Runnable{
    private  int ticket=100;
    final Object obj=new Object();

    @Override
    public void run() {
        while (true){
            //synchronized (obj){
            synchronized (this){
                if (ticket>0){
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+":賣票 票號:"+ticket);
                    ticket--;
                }else {
                    break;
                }
            }
        }
    }
}

使用繼承方式: 

package org.zhanghl;

public class WindowB {
    public static void main(String[] args) {
        TicketRun t1= new TicketRun();
        TicketRun t2= new TicketRun();
        TicketRun t3 =new TicketRun();
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}

class TicketRun extends Thread{
    private static int ticket=100;
    private static Object obj=new Object();
    @Override
    public void run() {
        while (true){
            //synchronized (obj){
            //能夠用當前類作鎖
            synchronized (WindowB.class){
                if (ticket>0){
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+":賣票 票號:"+ticket);
                    ticket--;
                }else {
                    break;
                }
            }
        }
    }
}

 

使用同步代碼塊 解決繼承Thread類的方式的線程安全問題

注意:

1. 共享數據與鎖對象都應是惟一的,這裏爲static.

2. 用繼承Thread類建立多線程的方式時,慎用this充當同步監視器,考慮使用當前類對象作鎖。Class只會加載一次。

3. 同步的方式,優勢是解決了線程的安全問題,

    缺點是操做同步代碼時,只能由一個線程參與,其餘線程等待。先當與是一個線程的過程,效率低。

 

方式2、同步方法

若是操做共享數據的代碼完整的聲明在一個方法中。咱們不妨將此方法聲明同步的

package org.zhanghl;

/*
 * 使用同步方法解決實現Runable接口的線程安全問題
 * */

class WindowC1 implements Runnable {
    private int ticket = 100;
    final Object obj = new Object();

    @Override
    public void run() {
        while (true) {
            if (ticket > 0) {
                show();
            } else {
                break;
            }
        }
    }

    /*同步監視器:this */
    private synchronized void show() {
        if (ticket > 0) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":賣票 票號:" + ticket);
            ticket--;
        }
    }
}
/*
*使用同步方法處理繼承Thread類的方式中的線程安全問題
* */
class WindowC2 extends Thread{
    private static int ticket = 100;
    final Object obj = new Object();

    @Override
    public void run() {
        while (true) {
            if (ticket > 0) {
                show();
            } else {
                break;
            }
        }
    }

    /*同步監視器:this */
    private static synchronized void show() { /*此時同步監視器是 WindowC2.class*/
        //private synchronized void show() 此種方式是錯誤的
        if (ticket > 0) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":賣票 票號:" + ticket);
            ticket--;
        }
    }
}
public class WindowC {
    public static void impl_show(){
        WindowC1 w1 = new WindowC1();
        Thread t1 = new Thread(w1);
        Thread t2 = new Thread(w1);
        Thread t3 = new Thread(w1);
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }

    public static  void extd_show(){
        WindowC2 t1=new WindowC2();
        WindowC2 t2=new WindowC2();
        WindowC2 t3=new WindowC2();
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
    public static void main(String[] args) {
        /*實現方式,同步方法*/
       //impl_show();
        /*繼承方式,同步方法*/
        extd_show();
    }
}

 

總結:

1.同步方法仍然涉及到同步監視器,只是不須要咱們顯示的聲明。

2.非靜態的同步方法,同步監視器是this.

靜態的同步方法,同步監視器是 當前類自己。

相關文章
相關標籤/搜索