如何在Java中同步工作
首先, 这是一个示例 :
public class Deadlock { static class Friend { private final String name; public Friend(String name) { this.name = name; } public String getName() { return this.name; } public synchronized void bow(Friend bower) { System.out.format("%s: %s has bowed to me!%n", this.name, bower.getName()); bower.bowBack(this); } public synchronized void bowBack(Friend bower) { System.out.format("%s: %s has bowed back to me!%n", this.name, bower.getName()); } } public static void main(String[] args) { final Friend alphonse = new Friend("Alphonse"); final Friend gaston = new Friend("Gaston"); new Thread(new Runnable() { public void run() { alphonse.bow(gaston); } }).start(); new Thread(new Runnable() { public void run() { gaston.bow(alphonse); } }).start(); } }
我得不到的是堵塞的发生。 主函数启动两个线程,每个线程开始自己的弓。
什么是“同步”块? 为同一个对象运行同样的function(正如我原来的想法)? 同一个类的所有对象的相同的function? 同一个对象的所有同步函数? 同一类的所有对象的所有同步函数?
帮帮我。
在Java中,每个Object
提供了一个线程synchronize
或locking的能力。 当一个方法被同步时,该方法使用它的对象实例作为锁。 在你的例子中,方法bow
和bowBack
是synchronized
,并且都在同一个class级Friend
。 这意味着执行这些方法的任何线程都将在Friend
实例上同步它的锁。
导致死锁的一系列事件是:
- 第一个线程开始调用
alphonse.bow(gaston)
,它在alphonse
Friend
对象上synchronized
。 这意味着线程必须从这个对象获取锁。 - 第二个线程开始调用
gaston.bow(alphonse)
,它在gaston
Friend
对象上synchronized
。 这意味着线程必须从这个对象获取锁。 - 现在开始的第一个线程调用了
bowback
并等待gaston
上的锁被释放。 - 现在开始的第二个线程调用
bowback
并等待alphonse
上的锁被释放。
要更详细地显示事件的顺序:
-
main()
开始在主Therad中执行(称之为Thread#1),创build两个Friend
实例。 到现在为止还挺好。 - 主线程使用代码
new Thread(new Runnable() { ...
Thread#2调用alphonse.bow(gaston)
,它在alphonse
Friend
对象上synchronized
来启动它的第一个新线程(称为线程2)。线程#2因此获得了对于alphonse
对象的“locking”并进入了bow
法。 - 时间片发生在这里,原来的线程有机会做更多的处理。
- 主线程开始第二个新线程(称为线程#3),就像第一个线程一样。 线程#3调用
gaston.bow(alphonse)
,它在gaston
Friend
对象上同步。 由于没有人获得gaston
对象实例的“locking”,所以线程#3成功获取该锁并进入bow
方法。 - 时间片发生在这里,线程#2有机会做更多的处理。
- 线程#2现在调用
bower.bowBack(this);
与bower
是gaston
的例子的gaston
。 这是gaston.bowBack(alphonse)
的调用的逻辑等价物。 因此,这个方法在gaston
实例上是synchronized
的。 该对象的锁已经被获取,并被另一个线程(线程#3)保存。 因此,线程#2必须等待gaston
上的锁被释放。 线程处于等待状态,允许线程3进一步执行。 - 线程#3现在调用
bowback
,在这种情况下在逻辑上与调用alphonse.bowBack(gaston)
。 为此,需要获取alphonse
实例的锁,但该锁由Thread#2保存。 此线程现在处于等待状态。
而你现在处于一个线程无法执行的位置。 线程#2和线程#3正在等待一个锁被释放。 但是没有一个线程正在进行,这两个锁都不能被释放。 但是,没有一个锁被释放,任何线程都不能取得进展。
因此:僵局!
死锁通常取决于事件发生的特定顺序,由于难以重现,可能难以debugging。
同步有两个作用 :
- 首先,同一个对象上的两个同步方法的调用是不可能交错的。 当一个线程正在执行一个对象的同步方法时,所有其他调用同一对象的同步方法的线程将阻塞(挂起执行),直到第一个线程完成对象。
- 其次,当一个同步的方法退出时,它会自动build立一个与先前同步对象的任何后续调用同步方法的before-before关系。 这保证了对所有线程都可见的对象状态的改变。
简而言之,它会阻止对同一个对象的同步方法的任何调用。
同一个对象的所有同步函数。 标记一个方法“synchronized”与在方法的整个内容中放置一个“synchronized(this){”方块非常相似。 我不说“相同”的原因是因为我不知道编译器是否发出相同的字节码,但AFAIK定义的运行时效果是相同的。
死锁是一个经典的locking反转。 一个线程lockingalphonse。 然后(或同时在多核系统上)另一个线程lockinggaston。 这部分要求线程的调度恰好在正确的点交错。
然后,每个线程(以任何顺序或同时)尝试获取已由另一个线程保持的锁,从而每个线程都进入hibernate状态。 直到另一个释放它的锁,它们都不会唤醒,但是直到唤醒(或终止)它们都不会释放它的锁。
synchronized方法和把所有这些方法的代码封装成一样
synchronized(this) { /// code here ... }
块。
对于给定的对象实例o ,一次只有一个线程可以运行任何同步的(o)块。 每一个其他的线程都会试图嚎,大哭,直到运行该块的线程(具有同步的锁 )退出该块(放弃锁)。
在你的情况下,当Alphonse开始在线程1中鞠躬时,发生死锁,从而进入同步块。 线程1然后被系统交换出去,所以线程2可以启动,让Gaston鞠躬。 但加斯顿还不能回头,因为它在阿尔方斯上同步,而线程1已经有了这个锁。 它将因此等待线程1离开该块。 系统然后将交换线程1,这将尝试让Alphonse回头。 除了不能这样做,因为线程2在Gaston上有同步locking。 这两个线程现在都卡住了,等待另一个完成鞠躬,才能够回头…