代码之家  ›  专栏  ›  技术社区  ›  Daddy Warbox

同步在Java中是如何工作的

  •  14
  • Daddy Warbox  · 技术社区  · 16 年前

    第一 here's a sample :

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

    我不明白的是 怎样

    “同步”到底阻止了什么?同一个函数为同一个对象运行(如我最初所想)?对同一类的所有对象使用相同的函数?同一对象的所有同步函数?同一类的所有对象的所有同步函数?

    帮帮我。

    4 回复  |  直到 8 年前
        1
  •  29
  •   Eddie    16 年前

    在Java中,每个 Object 提供线程执行以下操作的能力: synchronize bow bowBack 都是 synchronized ,两者都在同一个班 Friend . 这意味着执行这些方法的任何线程都将在 朋友 实例作为其锁。

    将导致死锁的一系列事件是:

    1. alphonse.bow(gaston) ,即 同步的 alphonse 朋友 对象这意味着线程必须从此对象获取锁。
    2. 第二个线程开始调用 gaston.bow(alphonse) ,即 同步的 gaston 朋友 对象这意味着线程必须从此对象获取锁。
    3. bowback 等待锁打开 加斯顿
    4. 第二个线程现在开始调用 弓背 等待锁打开 阿尔方斯

    要更详细地显示事件顺序,请执行以下操作:

    1. main() 朋友
    2. 主线程使用代码启动其第一个新线程(称为线程#2) new Thread(new Runnable() { ... . 线程#2个调用 阿尔方斯弓(加斯顿) ,即 阿尔方斯 朋友 对象因此,线程#2获得 阿尔方斯 对象并输入
    3. 此时会出现一个时间片,原始线程有机会进行更多处理。
    4. 主线程启动第二个新线程(称为Thread#3),就像第一个线程一样。线程#3个调用 加斯顿·鲍(阿尔方斯) ,这是在上同步的 朋友 对象因为现在还没有人能获得这个系统的“锁” 加斯顿 对象实例,线程#3成功获取此锁并进入 方法
    5. 这里出现一个时间片,线程#2有机会进行更多处理。
    6. 线程2现在调用 bower.bowBack(this); 具有 bower 作为对实例的引用 加斯顿 . 这在逻辑上相当于调用 gaston.bowBack(alphonse) . 因此,这种方法是可行的 加斯顿 例子此对象的锁已被获取,并由另一个线程(线程#3)持有。因此,线程2必须等待锁定 加斯顿
    7. 线程#3现在调用 弓背 alphonse.bowBack(gaston) . 为此,它需要获取 阿尔方斯 实例,但该锁由线程2持有。此线程现在处于等待状态。

    现在,两个线程都无法执行。线程2和线程3都在等待释放锁。但是,如果线程没有进展,两个锁都无法释放。但是,如果不释放锁,两个线程都无法取得进展。

    因此:僵局!

    死锁通常取决于发生的特定事件序列,这会使调试变得困难,因为它们很难重现。

        2
  •  2
  •   1800 INFORMATION    16 年前

    Synchronized has two effects :

    因此,简而言之,它阻止了对同一对象的同步方法的任何调用。

        3
  •  2
  •   Steve Jessop    16 年前

    死锁是一种典型的锁反转。一根线锁住阿尔方斯。然后(或同时在多核系统上)另一个线程锁定gaston。这一部分要求线程的调度恰好在正确的点进行交错。

    然后,每个线程(以任何顺序或同时)尝试获取另一个线程已经持有的锁,因此每个线程进入睡眠状态。在另一方释放其锁之前,两者都不会唤醒,但在唤醒(或终止)之前,两者都不会释放其锁。

        4
  •  2
  •   Varkhan    16 年前

    synchronized(this) {
      /// code here ...
    }
    

    对于给定的对象实例 ,一次只能运行一个线程 同步(o) 块其他每一个尝试的线程都会哀号,直到运行该块的线程(具有 同步锁 在其上)退出该块(放弃锁定)。

    在您的例子中,当Alphonse开始在线程1中弯曲,从而进入同步块时,就会发生死锁。然后,线程1被系统调出,因此线程2可以启动,并使加斯顿弓形。但加斯顿还不能退后,因为它正在阿尔方斯同步,线程1已经有了锁。因此,它将等待线程1离开该块。然后,系统将线程1换回,这将尝试使阿尔方斯弓回。但它不能这样做,因为线程2在Gaston上有同步锁。现在两条线都卡住了,等待另一条线完成弯曲,然后才能向后弯曲。。。