【问题标题】:Why Synchronization isn't working in the following Code?为什么同步在以下代码中不起作用?
【发布时间】:2012-10-26 13:50:18
【问题描述】:

即使我在运行方法的synchronized 块中使用了synchronized 方法removeFirst,此代码有时也会抛出异常,我在synchronizedList 上添加和删除元素。

public class NameDropper extends Thread {

    private NameList n1;

    public NameDropper(List list) {
        this.n1 = new NameList(list);
    }

    public static void main(String[] args) {
        List l = Collections.synchronizedList(new LinkedList());
        NameDropper n = new NameDropper(l);
        n.n1.add("Ozymandias");
        Thread[] t = new NameDropper[10];
        for (int i = 1; i <= 10; i++) {
            t[i - 1] = new NameDropper(l);
            t[i - 1].setName("T" + Integer.toString(i - 1));
            t[i - 1].start();
        }
    }

    public void run() {
        synchronized (this) {
            try {
                Thread.sleep(50);
                String name = n1.removeFirst();
                System.out.println(Thread.currentThread().getName() + ": "
                    + name);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
     }
}

class NameList {
    private List names = null;

    public NameList(List list) {
        this.names = list;
    }

    public synchronized void add(String name) {
        names.add(name);
    }

    public synchronized String removeFirst() {
        if (names.size() > 0)
            return (String) names.remove(0);
        else
            return null;
      }
}

它抛出的异常:

T1: Ozymandias    
T2: null    
*Exception in thread "T3" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0    
    at java.util.LinkedList.entry(Unknown Source)
    at java.util.LinkedList.remove(Unknown Source)
    at java.util.Collections$SynchronizedList.remove(Unknown Source)
    at NameList.removeFirst(NameDropper.java:57)*
T0: null    
T8: null    
*at NameDropper.run(NameDropper.java:33)*      
T6: null    
T4: null    
T9: null    
T7: null    
T5: null    

【问题讨论】:

  • 如果您在同步方法中进行所有列表访问,则不需要同步列表。您不会冒险对结构进行任何并发访问,因此添加的同步只会减慢您的代码。

标签: java multithreading jakarta-ee synchronized


【解决方案1】:

您正在为每个线程创建一个新的 NameDropper 实例。
因此,synchronized 方法实际上并没有锁定,因为每个实例都不会被两个线程使用。

【讨论】:

  • 对。建议始终在其引用永远不会更改的 final 对象上进行同步。
【解决方案2】:

正如其他人所指出的,您有一个竞争条件,因为您的所有线程都在自己同步。您需要一个公共对象来进行同步。

我建议您在列表本身上进行同步。这将意味着竞争同一列表的任何实例都被相互阻塞,而任何没有竞争的线程都不会被阻塞。您的添加和删除方法应该是:

public void add(String name) {
    synchronized (name) {
        names.add(name);
    }
}

public String removeFirst() {
    synchronized (name) {
        if (names.size() > 0)
            return (String) names.remove(0);
        else
            return null;
    }
}

【讨论】:

    【解决方案3】:

    一般来说:
    1)由于您每次都在创建类的新实例,因此基本上没有所有线程可以锁定的“通用”对象。您应该定义如下内容:

    static final Object lock = new Object();
    

    synchronize 代替此对象。

    2) 恕我直言,最好实现 Runnable 而不是扩展 Thread

    【讨论】:

      【解决方案4】:

      即使您使用的是Collections.synchronizedList,您的代码中也存在竞争条件。

      以下是您的代码中的比赛代码示例。

      lock(NameDropper[0])                            lock(NameDropper[1])
       names.size() > 0 is true                       names.size() > 0 is true  
                                                      names.remove(0)
       names.remove(0) <--- Error here.
      

      由于您正在为每个线程 which shares single instance of List 创建 NameDropper 实例,因此您有这种竞争条件。

      您可以为每个NameDropper 创建单独的列表

              List l1 = Collections.synchronizedList(new LinkedList());
              t[i - 1] = new NameDropper(l1);
      

      这样每个NameDropper 都会有自己的List 实例。

      【讨论】:

        【解决方案5】:

        正如其他人所说,NameList 没有被共享。这是一种用最少的重新编码来修复代码的方法(还有其他方法):

        将构造函数更改为采用 NameList(不是 List)。

        public NameDropper(NameList list) {
            this.n1 = list;
        }
        

        在您当前创建列表的位置创建 NameList。

        NameList l = new NameList(Collections.synchronizedList(new LinkedList()));
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-11-12
          • 2017-02-17
          • 1970-01-01
          • 1970-01-01
          • 2021-08-16
          • 2021-08-27
          • 1970-01-01
          • 2014-02-14
          相关资源
          最近更新 更多