Java,这个线程安全吗

Java,这个线程安全吗,java,multithreading,Java,Multithreading,我是并发方面的初学者,所以如果有人检查一下这段代码,我会很高兴 我有一个列表,我没有锁定整个列表,而是锁定第一个元素,然后锁定第二个元素,移动到第二个元素,从第一个元素释放锁,依此类推 public class List { private Node head; private Random random = new Random(); public List(int o) { this.head = new Node(o); } pu

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

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

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();
    }
}
和节点类

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

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

就Java内存模型而言,它是安全的,但它非常容易出错。管理这么多事情,很可能会出错。我已经看到您只是偶尔使用
finally…unlock
习惯用法(我假设,因为将其放入循环是复杂的),这就是我的意思

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

还有一件小事,即节点元素没有在其自身的锁下分配:

public Node(int element) {
    this.element = element;
}
在前一个节点上有一个锁,但从技术上讲,这意味着需要遍历列表,以确保可以看到指定的元素

相对于内存一致性,锁定的工作方式是:当获得锁时,保证可见的操作仅是在持有该特定锁时采取的操作(以及导致这些操作的操作)

现在,当您创建新节点时,它所做的是:

actual.lock();
Node newNode = new Node(random.nextInt());
...
actual.unlock();
这意味着在
newNode
中分配元素时持有的锁是
actual
上的锁。因此,为了保证另一个线程能够看到
新节点的正确值,它需要获取
实际值的锁。(如果遍历列表,就会发生这种情况。)

虽然实际上再次查看它,但我看到
元素
被声明为最终元素,所以在没有锁的情况下应该保证它的可见性。此外,在构建
newNode
后,您会立即获取并释放该锁。但这是一种需要注意的复杂互动

如果
元素
不是最终元素,您只需执行以下操作:

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

管理这么多锁容易出错,这是我想指出的更重要的缺点。目前只有2个方法,但例如
java.util.List
大约有20个。若要完全实现列表,需要编写大量代码。

谢谢您的评论。你能不能用别的话给我解释一下最后两句话,因为我很难理解。我应该使用setter而不是constructor吗?请看我的编辑,虽然我现在看到
元素是final,所以它肯定是可以的。我使用finally。。unlock是因为有一个return语句,所以我想在离开方法之前解锁lock,并最终能够保证这一点,将unlock最终放在内部实际上是使用锁的首选方式,如中所示。它确保即使抛出异常也会调用unlock。您对while循环有何看法。我检查下一个元素是否不为null。如果是真的,我得到锁。是否可能在这些操作之间删除此元素更改(我也有删除方法)