使用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对象本身作为“对象监视器”

posted @ 2017-01-19 19:54  霓裳梦竹  阅读(1813)  评论(0编辑  收藏  举报