简体   繁体   English

为什么同步在以下代码中不起作用?

[英]Why Synchronization isn't working in the following Code?

This code sometime throwing an Exception even i have used synchronized Method removeFirst within synchronized block of run method, I am adding and removing element on a synchronizedList . 即使我在run方法的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;
      }
}

The exception it is throwing: 它抛出的异常是:

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    

You're creating a new NameDropper instance for each thread. 您正在为每个线程创建一个新的NameDropper实例。
Therefore, the synchronized methods aren't actually locking, since each instance is never used by two threads. 因此, synchronized方法实际上并没有锁定,因为每个实例都不会被两个线程使用。

As pointed out by other people, you have a race condition because all of your threads are synchronized on themselves. 正如其他人所指出的,您之所以处于竞争状态,是因为所有线程都在自己身上同步。 You need a common object to synchronize on. 您需要一个公共对象进行同步。

I would recommend that you synchronize on the list itself. 我建议您在列表本身上进行同步。 It will mean that any instances that are contending for the same list are blocked on each other and any threads that are not, will not be blocked. 这意味着争用同一列表的任何实例都将彼此阻塞,而没有阻塞的任何线程都将不会被阻塞。 Your add and remove methods should be: 您的添加和删除方法应为:

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;
    }
}

Even though you are using Collections.synchronizedList there is a race condition that exists in your code. 即使您使用的是Collections.synchronizedList ,代码中也存在竞争条件。

Below is the example of the race codition inside your code. 下面是代码中种族代码示例。

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

Since you are creating NameDropper instance for each thread which shares single instance of List you have this race condition. 由于您正在为which shares single instance of List实例的每个线程创建NameDropper实例, which shares single instance of List您具有这种竞争条件。

What you can do is create separate list for each NameDropper 您可以做的是为每个NameDropper创建单独的列表

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

This way each NameDropper will have its own instance of List . 这样,每个NameDropper都会拥有自己的List实例。

In general: 一般来说:
1) Since you are creating a new instance of your class each time, you basically have no "common" object for all threads to to lock upon. 1)由于您每次都在创建类的新实例,因此对于所有要锁定的线程,基本上都没有“公共”对象。 You should define something like: 您应该定义如下内容:

static final Object lock = new Object();

and synchronize on this object instead. 并与此对象synchronize

2) IMHO it's preferable to implement Runnable rather than extending Thread . 2)恕我直言,最好实现Runnable而不是扩展Thread

As others have stated, NameList is not being shared. 正如其他人所述,NameList没有被共享。 Here is one way with minimal recoding to fix your code (there are others): 这是通过最少的重新编码来修复代码的一种方法(还有其他方法):

Change the constructor to take a NameList (not List). 更改构造函数以使用NameList(而不是List)。

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

Create the NameList where you are currently creating the List. 在当前正在创建列表的位置创建NameList。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM