同步停放像Lock.lock()这样的并发线程吗?

时间:2013-06-21 10:59:51

标签: java multithreading concurrency synchronization

当我们调用lock.lock()或尝试输入synchronized块时,如果某个其他线程已经采用该锁定,则我们的线程会阻塞。现在我的问题是,当我们查看lock.lock()的实现时,它委托获取对AQS的锁定,该实际停放当前线程(以便调度程序无法进一步调度)。

同样是synchronized阻止的情况吗?

我甚至认为我的线程状态也不同。例如,如果我的帖子在synchronized阻止时被阻止,那么如果我已经呼叫,它将是BLOCKING lock.lock(),然后它将是WAITING。我是对的吗?

我关注的是Thread.status方面的以下两种锁定策略与停车而非繁忙等待的性能改善之间的差异

  1. ReentrantLock.lock();
  2. synchronize { /*some code */ }

3 个答案:

答案 0 :(得分:4)

BLOCKING - 在资源上被阻止,不能被中断

WAITING - 在资源上被阻止,但可以被中断或通知或取消暂停。

正如您所看到的,WAITING更适合从另一个处理过的控制。例如如果两个线程死锁,你可以用一个中断来打破一个lock()。使用同步的两个线程,你会被卡住。

synchronized vs lock的行为非常相似,主要修订版之间的确切细节会发生变化。

我的建议是使用

  • 为需要线程安全但锁定争用率非常低的简单代码进行同步。

  • 使用Lock确定您有锁定争用,或者您需要其他功能,例如tryLock。


如果你这样做

final Lock lock = new ReentrantLock();
lock.lock();
Thread t = new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            lock.lockInterruptibly();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
});
t.start();
Thread.sleep(100);
System.out.println(t + " is " + t.getState());
lock.unlock();

打印

Thread[Thread-0,5,main] is WAITING

Thread.State


等待线程的线程状态。由于调用以下方法之一,线程处于等待状态:

  • 没有超时的Object.wait
  • 没有超时的Thread.join
  • LockSupport.park

处于等待状态的线程正在等待另一个线程执行特定操作。例如,在对象上调用Object.wait()的线程正在等待另一个线程在该对象上调用Object.notify()或Object.notifyAll()。调用Thread.join()的线程正在等待指定的线程终止。

答案 1 :(得分:4)

调用locklockInterruptibly会将帖子置于WAITING州:

  

等待线程的线程状态。由于调用以下方法之一,线程处于等待状态:

     
      
  • 没有超时的Object.wait
  •   
  • 没有超时的Thread.join
  •   
  • LockSupport.park
  •   

以下代码启动四个线程,前两个(A,B)运行相同的代码并通过lock方法锁定一些监视器。另外两个(C,D)也运行相同的代码,但它们通过lockInterruptibly方法锁定另一个监视器:

public static synchronized void dumpThreadState(List<Thread> threads) {
    System.out.println("thread state dump start");
   for (Thread t: threads) {
        System.out.println(t.getName()+" "+t.getState());
    } 
   System.out.println("thread state dump end\n");
}

public static void main(String[] args) throws InterruptedException {
    final Lock lock = new ReentrantLock();
    final Lock anotherLock = new ReentrantLock();
    List<Thread> threads = new LinkedList<Thread>();

    Runnable first = new Runnable() {

        @Override
        public void run() {                
            try {
                lock.lock();
            } 
            catch (Exception ex) {
                System.out.println(Thread.currentThread().getName()+" processing exception "+ex.getClass().getSimpleName());                    
            }
            while (true);                
        }
    } ;
    Runnable second = new Runnable() {

        @Override
        public void run() {         
            try {
                anotherLock.lockInterruptibly();
            } 
            catch (InterruptedException ex) {
                System.out.println(Thread.currentThread().getName()+" was interrupted");
            }
            while (true); 
        }
    };

    threads.add(new Thread(first,"A"));
    threads.add(new Thread(first,"B"));
    threads.add(new Thread(second,"C"));
    threads.add(new Thread(second,"D"));


    dumpThreadState(threads);

    for (Thread t: threads) {
        t.start();
    }

    Thread.currentThread().sleep(100);

    dumpThreadState(threads);

    System.out.println("interrupting " + threads.get(1).getName());
    threads.get(1).interrupt();

    dumpThreadState(threads);

    System.out.println("interrupting " + threads.get(3).getName());
    threads.get(3).interrupt();

    Thread.currentThread().sleep(100);
    dumpThreadState(threads);

    for (Thread t: threads) {
        t.join();
    }
}

输出:

thread state dump start
A NEW
B NEW
C NEW
D NEW
thread state dump end

thread state dump start
A RUNNABLE
B WAITING
C RUNNABLE
D WAITING
thread state dump end

interrupting B
thread state dump start
A RUNNABLE
B WAITING
C RUNNABLE
D WAITING
thread state dump end

interrupting D
D was interrupted
thread state dump start
A RUNNABLE
B WAITING
C RUNNABLE
D RUNNABLE
thread state dump end

可以看出,通过lock方法锁定的线程无法中断,而使用lockInterruptibly锁定的线程可以。

在另一个示例中,启动了三个线程,前两个(A,B)运行相同的代码,并通过synchronized块锁定在同一个监视器上。第三个线程锁定另一个监视器,但通过wait方法等待:

 public static void main(String[] args) throws InterruptedException {
    final Object lock = new Object();
    final Object anotherLock = new Object();

    List<Thread> threads = new LinkedList<Thread>();

    Runnable first = new Runnable() {

        @Override
        public void run() {
            synchronized(lock) {
                while (true);
            }
        }
    } ;
    Runnable second = new Runnable() {

        @Override
        public void run() {         
            synchronized(anotherLock) {
                try {
                    anotherLock.wait();
                } 
                catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
            }
        }
    };

    threads.add(new Thread(first,"A"));
    threads.add(new Thread(first,"B"));
    threads.add(new Thread(second,"C"));

    dumpThreadState(threads);

    for (Thread t: threads) {
        t.start();
    }

    Thread.currentThread().sleep(100);

    dumpThreadState(threads);

    for (Thread t: threads) {
        t.join();
    }

}

输出:

thread state dump start
A NEW
B NEW
C NEW
thread state dump end
thread state dump start
A RUNNABLE
B BLOCKED
C WAITING
thread state dump end

线程C最终处于WAITING状态,而线程B最终处于BLOCKING状态:

  

线程的线程状态被阻塞等待监视器锁定。处于阻塞状态的线程正在等待监视器锁定以进入同步块/方法或在调用Object.wait后重新输入同步块/方法。

编辑:

这是一个非常好的UML diagram线程状态。

答案 2 :(得分:3)

停止线程和同步阻塞是非常不同的。当您尝试输入同步块时,您明确尝试获取对象实例上的监视器。如果无法获取监视器,则线程将进入BLOCKING状态,直到监视器可用。停放更类似于Object.wait()方法,因为代码知道在某些其他条件成立之前它不能继续。阻止这里是没有意义的,因为它会毫无结果,因为我继续的条件目前是正确的。此时我进入WAITING或TIMED_WAITING(取决于发出等待的方式)状态,直到我收到通知(通过诸如notify()notifyAll()unpark()之类的通知。一旦我的情况变为真,我就会出现,如果我的等待状态,然后可能会尝试获取监视器并进入阻塞,如果我需要它们。如果我拿到我的显示器,我会进入RUNNING并继续我的快乐方式

所以等待真的是知道我不能做某事并让其他线程在我认为可以的时候通知我。我醒来后会导致阻塞。阻止只是在没有明确的其他先决条件的情况下竞争访问监视器。

lock()实例上调用Lock时,调用线程实际上处于等待状态且未阻塞。这样做的好处是可以中断这种等待状态,这有助于避免死锁。通过类似Lock类的内容,您可以通过tryLock()tryLock(long,TimeUnit)lock()lockInterruptibly()获得所需等待行为的大量选项。您可以指定等待的时间以及是否可以通过您调用的方法中断。使用synchronized代码,您没有这样的选项。你阻塞,你被阻塞,直到一些线程放弃你想要的监视器,如果它永远不会,你就陷入僵局。这就是为什么自Java 5和concurrent包以来,你应该避免使用synchronized关键字,而是试着用LockCondition之类的东西来实现类似的语义。