【问题标题】:Using boolean to synchronize使用布尔值进行同步
【发布时间】:2017-06-03 06:36:29
【问题描述】:

以下代码在并发访问 List 时是线程安全的吗?
volatile 限定符是否在这里增加了任何价值?

   class concurrentList{

        private AtomicBoolean locked = new AtomicBoolean(true);
        volatile List<Integer> list=new LinkedList<Integer>();
        long start = System.currentTimeMillis();
        long end = start + 60*100;


        public void push(int e){
            while(!locked.get());
            list.add(e);
            while(!locked.compareAndSet(true,false));
        }

        public int pop(){
            int elem;
            while(locked.get());
            elem=(Integer)list.remove(0);
            while(!locked.compareAndSet(false,true));
            return elem;
        }
....
}

【问题讨论】:

  • 不确定为什么pushpop 方法中有不同的锁定条件。但除此之外,为什么不使用java.util.concurrent.Lock 对象呢?

标签: java concurrency java.util.concurrent


【解决方案1】:

不,它不是线程安全的。两个调用push() 的线程可以完美地将locked 读为true,然后并发添加到链表中。由于 LinkedList 不是线程安全的,因此您的代码也不是线程安全的。

要锁定,请使用锁定,而不是 AtomicBoolean。

【讨论】:

  • 完全正确,加上一个具体和快速的答案
【解决方案2】:

在这种情况下,我建议使用ReadWriteLock。这把锁有两个用途。当 readLock 开启时,不允许读,直到写锁被释放。读锁是非阻塞的:

class concurrentList{
    ReadWriteLock lock =new ReentrantReadWriteLock();

    private AtomicBoolean locked = new AtomicBoolean(true);
    volatile List<Integer> list=new LinkedList<Integer>();
    long start = System.currentTimeMillis();
    long end = start + 60*100;


    public void push(int e){
        lock.writeLock().lock();
        try{
          list.add(e);
        } finally {
            lock.writeLock().unlock();
        }
    }

    public int pop(){
        lock.readLock().lock();
        try {
        int elem;

        elem=(Integer)list.remove(0);
        } finally {
           lock.readLock().unlock();
        }
        return elem;
    }

.... }

【讨论】:

    【解决方案3】:

    在这种特殊情况下,我将根据[this question][1] 对方法使用同步,而不是对变量使用易失性

    类并发列表{

        private AtomicBoolean locked = new AtomicBoolean(true);
        List<Integer> list=new LinkedList<Integer>();
        long start = System.currentTimeMillis();
        long end = start + 60*100;
    
    
        public synchronized void push(int e){
            while(someLock.notCondition()); //only an example
            list.add(e);
            someLock.notify();
        }
    
        public synchronized int pop(){
            int elem;
            while(someLock.notCondition());
            elem=(Integer)list.remove(0);
            someLock.notify()
            return elem;
        }
    ....
    }
    

    【讨论】:

    • 什么是someLock
    【解决方案4】:

    在已经添加的答案中添加更多内容,即对于任何并发编程三个概念,您在编写线程安全编程时需要考虑。当未正确编写并发程序时,错误往往属于以下三类之一: AomicityVisibilityOrdering

    原子性:处理哪些动作和动作集具有不可分割的效果。通常从互斥的角度来考虑。

    可见性:确定一个线程的效果何时可以被另一个线程看到。

    排序:确定何时可以看到一个线程中的操作相对于另一个线程发生无序

    在您的代码第一个问题中,上述所有概念都失败了。您没有使用锁定,因此无法确保可见性和顺序。 为了线程安全,您可以在并发 API 中使用ReadWriteLock。或者有可用的非阻塞链表将使用 compareAndset。

    【讨论】:

      猜你喜欢
      • 2021-11-09
      • 2016-02-16
      • 1970-01-01
      • 2021-02-16
      • 1970-01-01
      • 2020-06-04
      • 1970-01-01
      • 1970-01-01
      • 2013-06-13
      相关资源
      最近更新 更多