使用synchronized(非this對象)同步代碼塊解決臟讀問題


首先通過示例來學習驗證多個線程調用同一個方法時隨機的。

package syn_out_asyn;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by Administrator on 2017/1/19 0019.
 */
public class MyList {
    private List list = new ArrayList();
    synchronized public void add(String username){
        System.out.println("ThreadName="+Thread.currentThread().getName()+"執行了add方法");
        list.add(username);
        System.out.println("ThreadName="+Thread.currentThread().getName()+"退出了add方法");
    }
    synchronized public int getSize(){
        System.out.println("ThreadName= "+Thread.currentThread().getName()+"執行了getSize方法");
        int sizeValue= list.size();
        System.out.println("ThreadName= "+Thread.currentThread().getName()+"退出了getSize方法");
        return  sizeValue;
    }
}
package syn_out_asyn;

/**
 * Created by Administrator on 2017/1/19 0019.
 */
public class ThreadA extends Thread {
    private MyList list;
    public ThreadA (MyList list){
        super();
        this.list = list;
    }

    public void run(){
        for(int i=0;i<10000;i++){
            list.add("ThreadA"+(i+1));
        }
    }
}
package syn_out_asyn;

/**
 * Created by Administrator on 2017/1/19 0019.
 */
public class ThreadB extends Thread {
    private MyList list;
    public ThreadB(MyList list){
        super();
        this.list = list;
    }
    public void run(){
        for(int i=0;i<10000;i++){
            list.add("threadB"+(i+1));
        }
    }
}
package syn_out_asyn;

/**
 * Created by Administrator on 2017/1/19 0019.
 */
public class Run {
    public static void  main(String[] args){
        MyList myList = new MyList();
        ThreadA threadA =  new ThreadA(myList);
        threadA.setName("A");
        threadA.start();
        ThreadB threadB = new ThreadB(myList);
        threadB.setName("B");
        threadB.start();
    }
}
執行結果:
ThreadName=A執行了add方法
ThreadName=A退出了add方法
ThreadName=A執行了add方法
ThreadName=A退出了add方法
ThreadName=A執行了add方法
ThreadName=A退出了add方法
ThreadName=B執行了add方法
ThreadName=B退出了add方法
ThreadName=B執行了add方法
ThreadName=B退出了add方法
ThreadName=B執行了add方法
ThreadName=B退出了add方法

從結果來看,同步塊中的代碼是同步打印的,當前線程的執行和退出時成對出現的。但線程A和線程B的執行卻是異步的,這就有可能出現臟讀的環境。由於線程執行的方法的順序不確定,所以當A和B兩個線程執行帶有分之判斷的方法時,就會出現邏輯上的錯誤,有可能出現臟讀。

package t9;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by Administrator on 2017/1/20 0020.
 */
public class MyOneList {
    private List list  = new ArrayList();
    synchronized  public void add(String  data){
        list.add(data);
    }
    synchronized public  int getSize(){
        return list.size();
    }
}
package t9;

/**
 * Created by Administrator on 2017/1/20 0020.
 */
public class MyService {
    public MyOneList addServiceMethod(MyOneList list ,String data) {
        try {
            if (list.getSize() < 1) {

                Thread.sleep(2000);//模擬從遠程話費2秒取回數據
                list.add(data);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
           return list;
    }
}
package t9;

/**
 * Created by Administrator on 2017/1/20 0020.
 */
public class MyThread1 extends Thread {
    private MyOneList list;
    public MyThread1(MyOneList list){
        super();
        this.list=list;
    }
    public void run(){
        MyService myService = new MyService();
        myService.addServiceMethod(list,"A");
    }
}
package t9;

/**
 * Created by Administrator on 2017/1/20 0020.
 */
public class MyThread2  extends Thread {
    private MyOneList list;
    public MyThread2(MyOneList list){
        super();
        this.list=list;
    }
    public  void run(){
        MyService myService = new MyService();
        myService.addServiceMethod(list,"B");
    }
}
package t9;

/**
 * Created by Administrator on 2017/1/20 0020.
 */
public class Run {
    public static  void main(String [] args) throws InterruptedException {
       MyOneList list = new MyOneList();
        MyThread1 thread1 = new MyThread1(list);
        thread1.setName("A");
        thread1.start();
        MyThread2 thread2 = new MyThread2(list);
        thread2.setName("B");
        thread2.start();
        Thread.sleep(6000);
        System.out.println("listSize="+list.getSize());
    }
}
運行結果:
listSize=2

臟讀出現了,原因是兩個線程以異步的方式返回list參數的size()大小,解決的辦法就是同步化。

修改MyService.java

package t9;

/**
 * Created by Administrator on 2017/1/20 0020.
 */
public class MyService {
    public MyOneList addServiceMethod(MyOneList list ,String data) {
        try {
            synchronized (list) { if (list.getSize() < 1) {

                    Thread.sleep(2000);//模擬從遠程話費2秒取回數據
                    list.add(data);
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
           return list;
    }
}
運行結果:
listSize=1

由於list參數對象在項目中是一份實例,是單例的,而且也正需要對list參數的getSize()方法做同步的調用,所以就對list參數進行同步處理。

結論:synchronized(非this對象x):格式的寫法是將x對象本身作為“對象監視器”


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM