arraylist上的java Volatile/synchronization

arraylist上的java Volatile/synchronization,java,multithreading,thread-safety,synchronized,volatile,Java,Multithreading,Thread Safety,Synchronized,Volatile,我的程序如下所示: public class Main { private static ArrayList<T> list; public static void main(String[] args) { new DataListener().start(); new DataUpdater().start(); } static class DataListener extends Thread {

我的程序如下所示:

public class Main {
    private static ArrayList<T> list;

    public static void main(String[] args) {
        new DataListener().start();
        new DataUpdater().start();
    }

    static class DataListener extends Thread {
        @Override
        public void run() {
            while(true){
                //Reading the ArrayList and displaying the updated data
                Thread.sleep(5000);
            }
        }
    }

    static class DataUpdater extends Thread{
        @Override
        public void run() {
            //Continuously receive data and update ArrayList;
        }
    }
}
公共类主{
私有静态数组列表;
公共静态void main(字符串[]args){
新建DataListener().start();
新建DataUpdater().start();
}
静态类DataListener扩展线程{
@凌驾
公开募捐{
while(true){
//读取ArrayList并显示更新的数据
睡眠(5000);
}
}
}
静态类DataUpdater扩展线程{
@凌驾
公开募捐{
//持续接收数据并更新ArrayList;
}
}
}
为了在两个线程中使用此ArrayList,我知道两个选项:

  • 使ArrayList易变。然而,我读到,只有在“写入变量不依赖于其当前值”的情况下,才允许将变量设置为volatile。我认为在本例中是这样的(因为例如,当您对ArrayList执行添加操作时,此操作后ArrayList的内容取决于ArrayList的当前内容,是吗?). 此外,DataUpdater必须时不时地从列表中删除一些元素,我还了解到不可能从不同线程编辑易失性变量

  • 使此ArrayList成为同步变量。但是,我的DataUpdater将持续更新ArrayList,因此这不会阻止DataListener读取ArrayList吗


  • 我是否误解了这里的任何概念,或者是否有另一种选择使之成为可能?

    事实上,这两种解决方案都不够。实际上,您需要同步arraylist上的完整迭代,以及对arraylist的每次写入访问:

    synchronized(list) {
        for (T t : list) {
            ...
        }
    }
    


    这根本帮不了你。
    volatile
    的意思是线程A对共享变量所做的更改对线程B立即可见。通常,这样的更改可能在某些缓存中,仅对创建它们的线程可见,
    volatile
    只是告诉JVM不要进行任何缓存或优化,否则会导致值更新延迟

    因此,这不是一种同步手段。这只是确保变化可见度的一种手段。此外,它是对变量的更改,而不是对该变量引用的对象的更改。也就是说,如果将
    列表
    标记为
    volatile
    ,则只有将新列表分配给
    列表
    ,而不是更改列表的内容,才会产生任何影响


    您的另一个建议是将
    ArrayList
    设置为同步变量。这里有一个误解。无法同步变量。唯一可以同步的是代码——要么是整个方法,要么是其中的特定块。将对象用作同步监视器

    监视器是对象本身(实际上,它是对象的逻辑部分,即监视器),而不是变量。如果在对旧值进行同步后将不同的对象分配给同一变量,则旧监视器将不可用

    但无论如何,同步的不是对象,而是您决定使用该对象同步的代码

    因此,您可以使用
    列表
    作为监视器来同步其上的操作。但是您不能使
    列表
    同步


    假设您想使用列表作为监视器来同步操作,您应该设计它,使writer线程不会一直持有锁。也就是说,它只需抓取它进行一次读取更新、插入等操作,然后将其释放。再次抓取它进行下一次操作,然后释放它。如果同步整个方法或整个更新循环,则另一个线程将永远无法读取它

    在阅读线程中,您可能应该执行以下操作:

    List<T> listCopy;
    
    synchronized (list) {
        listCopy = new ArrayList(list);
    }
    
    // Use listCopy for displaying the value rather than list
    
    列表复制;
    已同步(列表){
    listCopy=新的ArrayList(列表);
    }
    //使用listCopy显示值而不是列表
    
    这是因为显示可能很慢-可能涉及I/O、更新GUI等。因此,为了最小化锁定时间,您只需从列表中复制值,然后释放监视器,以便更新线程可以完成其工作


    除此之外,
    java.util.concurrent
    包等中有许多类型的对象,用于在这样的情况下提供帮助,其中一方在写,另一方在读。查看文档-也许一个
    ConcurrentLinkedQue
    对您有用

  • 使ArrayList易变

  • 不能使ArrayList
    易失性
    。您不能使任何对象不稳定。Java中唯一可以不稳定的是字段

    在您的示例中,
    list
    不是
    ArrayList

    private static ArrayList<T> list;
    

    执行该行后,列表已更改,但字段仍引用相同的列表对象。

    这是另一种可能性,但不会有太大更改,因为OP只有一个读卡器。同意,如果OP选择ConcurrentSkipList怎么办?JDK中没有此类类。无论如何,如果不知道确切的用例,我无法提供最佳解决方案。我所知道的是,两个线程必须同时访问一个列表。您可以使用
    并发
    包中的一个队列,而不是
    ArrayList
    。您不能使ArrayList
    变为volatile
    。您不能使任何对象不稳定。Java中唯一不稳定的东西是字段。但是,为什么在这种情况下我需要同步?@Xander,因为列表上的操作不是原子的。在另一个线程的更新中间进行的读取可以看到它处于不一致的状态。例如,如果删除ArrayList中的一个项,则所有其他项都会复制到左侧的一个位置。如果您的读者当时正在阅读,它可能会因此跳过某个项目。这很有意义!非常感谢。如果我复印一份
    private static ArrayList<T> list;
    
    list.add(e);