Java,这个线程是安全的

时间:2014-04-09 21:24:40

标签: java multithreading

我是并发的初学者,所以如果有人检查这段代码,我会很高兴。

我有一个列表,而不是锁定整个列表,我锁定第一个元素,然后锁定第二个元素,移动到第二个元素,从第一个元素释放锁定,依此类推。

public class List {
    private Node head;
    private Random random = new Random();

    public List(int o) {
        this.head = new Node(o);
    }

    public void addToList(int o) {      
        Node actual = head;
        actual.setLock();
        Node next;
        while( (next = actual.next() )!= null) {
            next.setLock();
            System.out.println(actual.getElement());
            actual.unlock();
            actual = next;

        }
        Node newNode = new Node(random.nextInt());
        newNode.setLock();
        actual.setNext(newNode);
        actual.unlock();
        newNode.unlock();
    }

    public void printList() {
        Node actual = head;
        actual.setLock();
        Node next;
        while( (next = actual.next() )!= null) {
            next.setLock();
            System.out.println(actual.getElement());
            actual.unlock();
            actual = next;

        }
        System.out.println(actual.getElement());
        actual.unlock();
    }
}

和Node类

public class Node {
    private final Lock lock = new ReentrantLock();
    private final int element;
    private Node next = null;

    public Node(int element) {
        this.element = element;
    }

    public int getElement() {
       try {
           lock.lock();
           return element;
       } finally {
           lock.unlock();
       }
    }

    public Node next() {
        try {
            lock.lock();
            return next;
        } finally {
            lock.unlock();
        }
    }

    public void setLock() {
        lock.lock();
    }

    public void unlock() {
        lock.unlock();
    }

    public void setNext(Node node) {
        try {
            lock.lock();
            next = node;
        } finally {
            lock.unlock();
        }
    }
}

我知道我可以使用库中的列表,但我想了解它是如何工作的

1 个答案:

答案 0 :(得分:1)

它在Java内存模型方面是安全的,但它非常容易出错。管理这么多东西,很可能会犯错误。我已经看到你有时只使用finally...unlock成语(我假设因为把它放在循环中很复杂)而且这就是我的意思。

获取如此多的锁也是进行并发编程的相对昂贵的方法。由于每个节点都需要一个额外的对象,因此也没有内存效率。

还有一个小问题,即Node元素没有在自己的锁下分配:

public Node(int element) {
    this.element = element;
}

锁定在前一个节点上,但这意味着在技术上需要遍历列表以确保看到指定的元素。

锁定相对于内存一致性的方式,当获取锁定时,保证可见的操作只是在保持特定锁定时采取的操作(和导致这些的行动)。

现在,当您创建一个新节点时,它的作用是:

actual.lock();
Node newNode = new Node(random.nextInt());
...
actual.unlock();

这意味着在newNode中分配元素时所持有的锁定是actual上的锁定。因此,为了保证另一个线程能够看到newNode的正确值,它需要获取actual的锁定。 (如果遍历列表,就会发生这种情况。)

虽然实际上再次查看它,但我发现element被宣布为最终版本,因此应该保证它的可见性而不需要锁定。您也可以在构建newNode之后立即获取并释放锁定。但这是一种需要注意的复杂互动。

如果element不是最终版,你就会这样做:

public Node(int element) {
    lock.lock();
    try {
        this.element = element;
    } finally {
        lock.unlock();
    }
}

管理这么多锁的容易出错的性质是我想指出的更重要的缺点。目前你只有2个方法,但是例如java.util.List大约有20个。如果你要完全实现List,那么很多代码都是正确的。