【问题标题】:why does the thread still run after it released lock?为什么线程释放锁后仍然运行?
【发布时间】:2014-12-04 16:34:20
【问题描述】:

这是Oracle关于Lock对象的java教程中的例子。请有人确认我是否正确解释了代码。

我将只考虑第一个线程,因为其他线程的工作方式相同。

首先,它获取alphonse的锁,并访问方法impendingBow。该方法现在尝试将两个实例的两个锁分配给线程。如果线程不能同时获得两个锁,它会释放它获得的那个。这是我卡住的时候。如果线程释放锁,另一个线程可以访问这两个实例,并且第一个线程应该在释放锁后立即停止执行。但实际上并没有。它仍然返回布尔值并继续在 bow 方法中运行 else 语句。为什么它可能发生?我认为就像同步代码一样,线程应该停止执行,直到它再次获得锁。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.Random;

public class Safelock {
    static class Friend {
        private final String name;
        private final Lock lock = new ReentrantLock();

        public Friend(String name) {
            this.name = name;
        }

        public String getName() {
            return this.name;
        }

        public boolean impendingBow(Friend bower) {
            Boolean myLock = false;
            Boolean yourLock = false;
            try {
                myLock = lock.tryLock();
                yourLock = bower.lock.tryLock();
            } finally {
                if (! (myLock && yourLock)) {
                    if (myLock) {
                        lock.unlock();
                    }
                    if (yourLock) {
                        bower.lock.unlock();
                    }
                }
            }
            return myLock && yourLock;
        }

        public void bow(Friend bower) {
            if (impendingBow(bower)) {
                try {
                    System.out.format("%s: %s has"
                        + " bowed to me!%n",
                        this.name, bower.getName());
                    bower.bowBack(this);
                } finally {
                    lock.unlock();
                    bower.lock.unlock();
                }
            } else {
                System.out.format("%s: %s started"
                    + " to bow to me, but saw that"
                    + " I was already bowing to"
                    + " him.%n",
                    this.name, bower.getName());
            }
        }

        public void bowBack(Friend bower) {
            System.out.format("%s: %s has" +
                " bowed back to me!%n",
                this.name, bower.getName());
        }
    }

    static class BowLoop implements Runnable {
        private Friend bower;
        private Friend bowee;

        public BowLoop(Friend bower, Friend bowee) {
            this.bower = bower;
            this.bowee = bowee;
        }

        public void run() {
            Random random = new Random();
            for (int n = 0; n <5;n++) {
                try {
                    Thread.sleep(Math.round(Math.random()*1000));
                } catch (InterruptedException e) {}
                bowee.bow(bower);
            }
        }
    }


    public static void main(String[] args) {
        final Friend alphonse =
            new Friend("Alphonse");
        final Friend gaston =
            new Friend("Gaston");
        new Thread(new BowLoop(alphonse, gaston)).start();
        new Thread(new BowLoop(gaston, alphonse)).start();
    }
}

【问题讨论】:

  • 是什么让你认为线程应该在释放Lock对象后停止执行?
  • 我把锁对象和同步代码联系起来,它只允许线程在获得锁时运行代码,如果没有则阻塞线程。
  • 不运行如何获取锁?
  • 反过来是我的问题,为什么它释放了锁但仍然运行。如果一个线程在同步代码中运行,我并不反对,它已经获得了锁。但我不确定锁定对象。
  • 锁用于包围关键部分,因此一次只有一个线程可以访问该部分。如果一个线程释放了一个锁,那么它就不再处于临界区并且不应该等待任何东西。

标签: java synchronization deadlock


【解决方案1】:

同步块之间存在细微差别,Lock.lock()Lock.tryLock()Lock.tryLock(timeout)

tryLock 方法只有在调用时没有被另一个线程持有时才会获取锁。所以当它无法获得锁时,它会立即返回。

lock 方法将等待直到锁可用,同时等待该线程处于休眠状态。将代码与同步语句同步也是如此。线程将一直等待,直到它可以获取该代码块上的锁。

tryLock(timeout) 方法将等待直到锁可用或超时到期。

或多或少在 javadoc 中有所描述:https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Lock.html

对于您期望的行为,您将tryLock 语句更改为lock,您会注意到线程最终会陷入死锁。

【讨论】:

  • 你说:tryLock方法只有在调用的时候没有被其他线程持有,才会获取锁。因此,当它无法获取锁时,它会立即返回。你是什​​么意思“立即返回”?这意味着该方法继续运行其余代码?
  • 是的。那是对的。该方法会立即返回,因为它不会停止等待锁变得可用。您可以通过查看存储在myLockyourLock 中的返回值来判断它获得了锁。在示例中,您可以将tryLock 替换为tryLock(timeout)。然后在语句周围添加一些代码来测量它所花费的时间。您会看到 tryLock 永远不会花费很长时间,而 tryLock(timeout) 通常会与超时持续时间一样长。
【解决方案2】:

线程释放锁时不会停止执行。如果出现以下情况,它将停止执行:

  1. 它试图锁定一个锁但不能(不会在此处停止执行,因为您使用了 trylock)
  2. 它明确地产生控制,例如通过睡觉 (sleep())
  3. 调度程序决定它现在更喜欢另一个线程。出于所有意图和目的,您应该假设这是一个随机事件。

这些事情都不会在这里发生,所以一个线程会继续执行直到 3) 发生。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-06-16
    • 2018-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-26
    • 1970-01-01
    相关资源
    最近更新 更多