【问题标题】:Can't relase the lock with notify无法通过通知释放锁
【发布时间】:2017-09-22 20:53:54
【问题描述】:

我对 java 线程编码进行了测试,但我遇到了一些基本问题。经过数小时的尝试和搜索,我决定在那里尝试!

我不明白为什么即使在我通知之后我的等待仍然被锁定:

在这里你可以找到我的代码:

public class Mymain {

    public static void main(String[] args) {

        for( int i=0;i<100;i++){
            new ThreadClass(i).start();     
        }
    }
}

public class ThreadClass extends Thread {
    static boolean ok = false;
    int id;

    public ThreadClass(int i) {
        id = i;
    }

    public void run() {
        System.out.println("Thread start " + id);
        Last.toDo(id);
        if (id == 5)
            try {
                waiting();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        if (id != 5)
            awaking();

        System.out.println("thread end " + id);
    }

    private synchronized void awaking() {
        // TODO Auto-generated method stub
        if (ok) {
            System.out.println("i'm " + id + " and i'm Awaking 5");
            ok = false;
            notify();
            System.out.println("I did the notify and i'm " + id);
        }
    }

    private synchronized void waiting() throws InterruptedException {
        System.out.println("Sleeping");
        ok = true;
        wait();
        System.out.println("Awake 5");
    }
}

Result

然后它开始循环或者它进入死锁不确定..它应该只是停止 id=5 的线程,然后下一个线程应该重新启动 id = 5..但是线程 5 在之后永远不会唤醒通知...

结果如你所见,我有 2 个线程试图唤醒线程 5,而线程 5 从一开始就一直在等待^^

【问题讨论】:

  • 你能告诉我们你的输出是什么,应该是什么?
  • 你能把它放在你的问题中,以便对其进行格式化以便于分析吗?
  • @WarrenDew 完成^^
  • 仅作记录,您接受的答案确实不清楚,没有意义。请考虑选择另一个。

标签: java multithreading locking wait notify


【解决方案1】:

问题是您没有在调用 wait() 的同一对象上调用 notify()。特别是,线程 5 对自身调用 wait(),但例如,线程 8 对自身调用 notify(),而不是对线程 5。因此,线程 5 永远不会收到通知。

另外,您需要将ok 变量设置为volatile 以确保当一个线程设置它时,其他线程可以看到更改。在这种特殊情况下,这不会给您带来问题,但在其他情况下可能会导致问题。

【讨论】:

  • 我也达到了这个目的(前 3 行),但我不明白我应该如何通知线程 5。这可能是我忽略的基本内容..^^ Ty帮助:D
  • 一种方法是将“ok”变量替换为 volatile Object 变量。您将检查 nonnull 而不是 true,线程 5 将创建对象并等待它。如果它在那里,其他线程会通知它。
  • 是的,我明白了 ^^
【解决方案2】:

为什么不使用 notifyAll() 方法?当你调用 notify() 时,意味着只有一个线程会将状态从 waiting 变为 runnable,但也可能存在多个线程的情况其他线程也在排队等待,他们不会收到这个通知。在我看来,最好使用 notifyAll。

【讨论】:

  • 不要这么认为^^我按照其他成员的建议解决了^^
  • 这篇文章没有准确回答这个问题。如果线程不使用相同的锁,那么它们调用哪个方法都没有关系。
【解决方案3】:

我不明白为什么即使在我通知之后我的等待仍然被锁定:

当使用相同的对象instance时等待并通知工作。例如,如果您有:

 String x1 = "...";
 String x2 = "...";

线程 #1 会:

synchronized (x1) { x1.wait(); }

然后线程 #2 会这样做:

synchronized (x2) { x2.wait(); }

然后线程#1 仍将等待,因为通知仅针对x2。在您的示例中,ID 为 5 的线程正在等待其自己的 ThreadClass 实例,因为您正在使用方法同步。然后当其他线程调用awaking() 时,它们也会在ThreadClass 的实例上调用通知。如果你想让线程 #5 看到另一个线程的通知,那么它们应该共享一个锁对象。

可能是这样的:

 final Object lock = new Object();
 for (int id = 0; id < 100; id++){
        new ThreadClass(id, lock).start();     
 }
 ...
 public class ThreadClass extends Thread {
     private final Object lock;
     ...
     public ThreadClass(int id, Object lock) {
         this.id = id;
         this.lock = lock;
     }
     ...
     private void awaking() {
        ...
        synchronized (lock) {
            lock.notify();
        }
        ...
     }
     private void waiting() throws InterruptedException {
        ...
        synchronized (lock) {
            lock.wait();
        }
        ...
    }
}

【讨论】:

    【解决方案4】:

    我对您的代码做了一些更改:

    1. 你不能只notify(),你会通知this。而且您不能只是 wait() ,您将永远等待。您必须在对象上使用这些函数,所以我添加了一个 Integer 对象(只是为了向您展示 - 您必须选择正确的对象)。
    2. synchronizedstatic synchronized 之间的关系你懂吗。快速搜索将引导您找到完美的答案。
    3. 为什么函数waiting()是同步的?只有 5 号线程调用它。
    4. 调用Object.notify() / Object.wait() 时,您必须在对象上声明一个同步块。

    这里有一些代码:

    public class Threads {
        public static void main(String[] args) {
            Integer intObject = new Integer(0);
            for( int i=0;i<100;i++){
    
                new ThreadClass(i, intObject).start();     
            }
        }
    }
    class ThreadClass extends Thread {
        static boolean ok = false;
        int id;
        Integer intObject;
        public ThreadClass(int i, Integer intObject) {
            id = i;
            this.intObject = intObject;
        }
    
        public void run() {
            System.out.println("Thread start " + id);
            //Last.toDo(id);
            if (id == 5)
                waiting();
            else
                awaking(this);
    
            System.out.println("thread end " + id);
        }
    
        private static synchronized void awaking(ThreadClass t) {
            if(ok) {
                System.out.println("i'm " + t.id + " and i'm Awaking 5");
                ok = false;
                synchronized (t.intObject) {
                        t.intObject.notify();
                    }
                System.out.println("I did the notify and i'm " + t.id);
            }
        }
        private void waiting(){
            System.out.println("Sleeping");
            ok = true;
            synchronized (intObject) {
                try {
                    intObject.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("Awake 5");
        }
    }
    

    【讨论】:

    • 是的,我写了它,只是为了演示如何在对象上使用同步
    • 这行不通。而不是锁定this,而是锁定一个自动装箱的Integer 字段,这更糟。首先没有其他人会调用它,所以你仍然有同样的问题。但等待/通知可以轻松更改的对象也是一种可怕的模式(如@NathanHughes)。锁对象至少也应该是final
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-12-27
    • 1970-01-01
    • 2015-01-18
    • 1970-01-01
    • 2020-07-07
    • 1970-01-01
    • 2020-06-01
    相关资源
    最近更新 更多