Synchronized不一定是线程安全的吧?_wait释放同步锁

来源:互联网 发布:苹果4s支持3g网络吗 编辑:程序博客网 时间:2024/05/20 18:44

在做并发开发的时候,为了保证某对象的线程安全,一般都会对其用Synchronized进行保护,比如:
synchronized(obj) {
// Do something with obj.
}
当所有对obj进行的操作,都用同步块保护时,我们一般认为其是线程安全的。

但这个线程安全到底有多安全呢?请先看看这个简单的例子:
代码实现很简单,两个线程同时修改一个对象的属性,在其属性原值基础上进行计算,然后再回写;
但是这里面故意使用了 obj.wait(),实际上造成对synchronized同步锁的释放。

public class synchronizedWrongUse {    public static void main(String[] args) throws Exception{        //初始化        SomeoneObject obj = new SomeoneObject();        Worker a = new Worker("A", obj);        Worker b = new Worker("B", obj);        //启动线程        a.start();        b.start();        //等待线程启动        Thread.sleep(100);        //唤醒obj        synchronized(obj){            obj.notifyAll();            System.out.println("notifyAll done.");        }        //等待线程结束        a.join();        b.join();        //输出结果        System.out.println("Amount:"+obj.getAmount());//只有50    }    static class Worker extends Thread{        private SomeoneObject obj;        public Worker(String name,SomeoneObject obj){            super(name);            this.obj = obj;        }        public void run(){            System.out.println("Worker["+this.getName()+"]started.");            synchronized(obj){//为保证obj操作的互斥性,加同步锁,但是wait释放了同步锁                int amount = obj.getAmount();                amount += 50;//              try{//                  obj.wait();//这里wait释放了同步锁//              }catch(InterruptedException e){//                  return;//              }                obj.setAmount(amount);                System.out.println("Worker["+this.getName()+"]setting done.");            }        }    }    /**     * @author lix     *某对象     */    static class SomeoneObject{        int amount;//属性        public int getAmount(){            return amount;        }        public void setAmount(int amount){            this.amount = amount;        }    }}

两个线程各 +50,期望结果是100;但实际结果仍然只有50。

接触过事务的人应该能很快理解,在synchronized块中,未必能绝对保证事务一致性;因为wait释放了同步锁。

当然,避免这种情况发生一般来说也很简单:
1、尽量不要将基于原有状态的计算过程被中断,也就是你别在 amount += 50; 中间插入wait;
2、可以做乐观检查,也就是set之前重新检查下当前状态是否与原状态保持一致,不一致就要重新发起计算。

阅读全文
0 0