【问题标题】:Synchronized ArrayList return non empty list同步的 ArrayList 返回非空列表
【发布时间】:2022-01-11 07:48:37
【问题描述】:

下面的类包含同步的arraylist和两个方法,一个是清除和添加arrayLIst,另一个是获取列表。

public class Singleton {
    private static Singleton singleton;
    private List<String> strList = Collections.synchronizedList(new ArrayList<String>());

    private Singleton() {
        
    }
    
    public static synchronized Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
    
    public void addElement(List<String> list) {
        strList.clear();
        strList.addAll(list);
        
    }
    
    public List<String> getStrList(){
        return new ArrayList<>(strList);
    }
    
}

当 addElement 和 getStrList 在两个不同的线程中调用时,有时会得到空列表。

public class App
{
    public static void main(String[] args) throws InterruptedException
    {
        final Singleton singleton = Singleton.getInstance();

        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i=0; i<100; i++) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if(i<=50) {
                        singleton.addElement(Arrays.asList("ONE"));
                    } else {
                        singleton.addElement(Arrays.asList("TWO"));
                    }
                }
                
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i=0; i<100; i++) {
                    System.out.println(singleton.getStrList());
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        t1.start();
        t2.start();
        
    }
}

输出:

[TWO]
[]
[TWO]
[TWO]
[TWO]
[TWO]
[TWO]
[TWO]
[TWO]
[TWO]
[TWO]
[TWO]
[TWO]
[TWO]
[TWO]
[TWO]
[TWO]
[]
[TWO]
[TWO]
[TWO]
[TWO]
[TWO]

在两者之间有返回的空列表。当 getStrList 方法中的列表为空时,如何在不添加睡眠的情况下解决此问题?也在寻找更好的替代方法来实现这一点。

【问题讨论】:

  • 你遇到了消费问题,即使没有消费。等待并通知如何?

标签: java multithreading


【解决方案1】:

addElement 有两个语句:

strList.clear(); //statement1
strList.addAll(list); //statement2

getStrList() 有一条语句

这两种方法都是非同步的,因此在线程 t2 调用 System.out.println(singleton.getStrList()) 之前 线程 t1 刚刚执行了 statement1,它正在清除列表,导致 t2 打印空列表。

为避免这种情况,您必须使 addElement()getStrList() 同步才能解决问题。

public synchronized void addElement(List<String> list) {
    strList.clear();
    strList.addAll(list);
    
}

public synchronized List<String> getStrList(){
    return new ArrayList<>(strList);
}

【讨论】:

  • 谢谢。您的解决方案也有效。但我认为具有成本效益的方法是@James Mudd 提供的解决方案。否则请提出建议。
  • 是的,这会起作用,但会给出不一致的数据。如果在两个线程中更新相同的数据结构,则应该同步。如果您的要求不需要一致的数据,那就可以了。
  • 知道了。我可以理解升级过程中秒数的不一致,并且这些方法的同步可以正常工作。谢谢..
【解决方案2】:

我想问题出在你的 'void addElement(List)' 方法上。
它没有同步。
线程 1 获得优先级并执行到列表被清除的行。然后线程 2 获取优先级并打印空列表的状态。最后优先级返回到线程 1 并添加项目。

同步 addElement 应该可以修复它。

【讨论】:

    【解决方案3】:

    您可以将addElement 方法替换为

    public void addElement(List<String> list) {
        List<String> newStrList = Collections.synchronizedList(new ArrayList<>(list));
        strList = newStrList;
    }
    

    这样列表将被替换,您将永远不会看到处于空状态的列表。

    【讨论】:

    • 谢谢。您提供的解决方案奏效了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-12
    • 1970-01-01
    • 2018-01-11
    • 2014-11-01
    • 2015-01-12
    相关资源
    最近更新 更多