我试图理解什么使得锁在并发如此重要,如果一个人可以使用同步(这)。在下面的虚拟代码中,我可以这样做:

同步整个方法或同步脆弱区域(Synchronized (this){…}) 或者使用ReentrantLock锁定易受攻击的代码区域。

代码:

    private final ReentrantLock lock = new ReentrantLock(); 
    private static List<Integer> ints;

    public Integer getResult(String name) { 
        .
        .
        .
        lock.lock();
        try {
            if (ints.size()==3) {
                ints=null;
                return -9;
            }   

            for (int x=0; x<ints.size(); x++) {
                System.out.println("["+name+"] "+x+"/"+ints.size()+". values >>>>"+ints.get(x));
            }

        } finally {
            lock.unlock();
        } 
        return random;
}

当前回答

您可以使用带有公平策略或超时的重入锁来避免线程饥饿。您可以应用线程公平策略。这将有助于避免线程永远等待获取资源。

private final ReentrantLock lock = new ReentrantLock(true);
//the param true turns on the fairness policy. 

“公平策略”选择下一个要执行的可运行线程。它是基于优先级,从上次运行到现在的时间等等

同时, 同步如果不能脱离阻塞,可以无限阻塞。Reentrantlock可以设置超时。

其他回答

ReentrantReadWriteLock是一个专用锁,而synchronized(this)是一个通用锁。它们很相似,但不完全相同。

你是对的,你可以使用synchronized(this)来代替ReentrantReadWriteLock,但相反的情况并不总是正确的。

如果您想更好地理解ReentrantReadWriteLock的特殊之处,请查阅一些关于生产者-消费者线程同步的信息。

一般来说,您可以记住,在大多数应用程序中都可以使用全方法同步和通用同步(使用synchronized关键字),而无需过多考虑同步的语义,但如果您需要从代码中挤出性能,则可能需要探索其他更细粒度的或特殊用途的同步机制。

顺便说一下,使用synchronized(this)——以及使用公共类实例的一般锁定——可能会有问题,因为它会导致代码出现潜在的死锁,因为其他人可能会在不知情的情况下尝试锁定程序中的其他地方的对象。

同步锁不提供任何等待队列机制,在其中一个线程执行后,任何并行运行的线程都可以获得锁。因此,在系统中运行较长时间的线程永远没有机会访问共享资源,从而导致饥饿。

重入锁非常灵活,并且有一个公平策略,如果一个线程等待较长时间,在当前执行的线程完成后,我们可以确保等待较长的线程获得访问共享资源的机会,从而降低系统的吞吐量,使其更加耗时。

我认为wait/notify/notifyAll方法不属于Object类,因为它用很少使用的方法污染了所有对象。它们在专门的Lock类上更有意义。因此,从这个角度来看,也许最好使用为手头的工作明确设计的工具——即ReentrantLock。

有一件事要记住: 名称“ReentrantLock”给出了关于其他锁定机制的错误信息,即它们不是可重入的。这是不对的。通过“synchronized”获得的锁在Java中也是可重入的。

关键的区别是“同步”使用内在锁(每个对象都有),而锁API没有。

让我们假设这段代码运行在一个线程中:

private static ReentrantLock lock = new ReentrantLock();

void accessResource() {
    lock.lock();
    if( checkSomeCondition() ) {
        accessResource();
    }
    lock.unlock();
}

由于线程拥有锁,它将允许多次调用lock(),因此它将重新进入锁。这可以通过引用计数来实现,这样它就不必再次获得锁。