【问题标题】:can I use static boolean variable as a lock for a synchronized thread?我可以使用静态布尔变量作为同步线程的锁吗?
【发布时间】:2014-07-01 10:33:51
【问题描述】:

我尝试使用静态布尔变量来锁定和解锁两个同步线程。 于是我写了如下代码:

public class Main {

    public static void main(String[] args){

        //MyObject lock = new MyObject();
        Thread1 t1 = new Thread1(100,'#');
        Thread1 t2 = new Thread1(100,'*');

        t1.start();
        t2.start();
    }
}

public class Thread1 extends Thread {

    public static boolean lock;
    int myNum;
    char myChar;

    public Thread1(int num, char c){
        myNum = num;
        myChar = c;
        lock = false;
    }

    public synchronized void run(){
        System.out.println(getName() + " is runing");
        while (Thread1.lock == true){
            System.out.println(getName() + " is waiting");
            try{wait();}
            catch(InterruptedException e){}
        }
        Thread1.lock = true;
        for(int i = 0; i<myNum; i++){
            if(i%10==0)
                System.out.println("");
            System.out.print(myChar);
        }
        Thread1.lock = false;
        notifyAll();
    }
}

可能我做得不对,因为只有一个线程正在打印“mychar”,而另一个线程只是进入 wait() 并且在我执行 notifyAll() 时没有醒来。 我认为这可能是为整个类使用静态布尔变量的好方法,而不是每次都更改它并调用 notifyAll() 来检查其他对象中的这个标志...

输出示例:

Thread-0 is runing

Thread-1 is runing
Thread-1 is waiting
##########
##########
##########
##########
##########
##########
##########
##########
##########
##########

【问题讨论】:

  • 如果你想串行运行线程,最好使用循环而不是线程。
  • 学习多线程的经典例子是实现生产者/消费者线程。生产者生产东西并通知消费者;同时消费者在消费后等待。

标签: java multithreading synchronized


【解决方案1】:

为什么它不起作用

notify() 和 wait() 与调用它们的对象的“监视器”一起工作。在您的情况下,这是 this,正在运行的 Thread1 的特定实例。

所以,当Thread-0 运行时:

  • 它检查lock
  • 找到了false
  • 它运行主要代码
  • 它在this(它本身就是Thread-0)上调用notifyAll()。

Thread-1 运行:

  • 它检查lock
  • 发现是true
  • 并在this(它本身,Thread-1)上调用wait()

由于Thread-0 在this(它本身)上调用notifyAll(),并且Thread-1 在this(它本身)上调用wait(),Thread-1 是在与Thread-0 不同的监视器上等待正在通知,因此它永远不会被释放。

解决方案

如果您的计划只是让该代码按顺序运行,请使用:

public class Thread1 extends Thread {

    private static final Object lock = new Object();

    public void run(){
        // non-sequential code
        System.out.println(getName() + " is running");
        synchronized (lock) {
            // this code will be run sequentially by one thread at a time
        }
        // non-sequential code
    }
}

【讨论】:

  • 为什么“public static final Object lock”需要是final的?
  • @YuvalLevy 不是需要,但它表明线程无法更改它。如果发生这种情况,那么在旧实例上等待的对象将不会通过调用新对象得到通知。
  • @YuvalLevy 也是,它应该是私有的,因为它在 Thread1 类之外没有用。 (已编辑)
【解决方案2】:

抱歉,但这提供了负值,因为它没有任何保证,只会给人一种安全的错觉。这有几个问题:

  1. 该变量是非易失性的,因为来自一个线程的此类更改可能对另一个线程不可见。
  2. 您的锁定不是原子的。因此,可能发生的情况是两个线程都可以通过 while 循环,然后都将布尔值设置为 true。
  3. notifyAll() 唤醒所有等待对象实例的线程。而且由于每个线程都在等待自己作为监视器,因此它们不会被唤醒。

Java 中有很多很好的锁定机制 - 尝试并发包中的那些(尽管在您的情况下,一个简单的同步块就足够了)。如果您坚持使用布尔值作为锁,则需要:

  1. 制作一个静态的finalAtomicBoolean作为你的信号器(非final也可以,只要你不改变它并保证可见性)
  2. 使用compareAndSet 作为循环/锁定条件,以便原子访问。
  3. Wait() 在 AtomicBoolean 上,以便线程共享它们正在等待的监视器(以及 notifyAll() 在它上面)。

【讨论】:

  • 您能再解释一下第 3 期吗?如果线程正在等待自己作为监视器,为什么它不会唤醒?
  • 线程等待对象实例。在这种情况下,您有 2 个实例:t1 和 t2。线程 1 等待 t1。线程 2 工作,完成后通知所有正在等待调用 notify 的对象,即t2。然而,t2 上没有任何东西在等待(t1 和 t2 是不同的对象),因此没有任何东西被唤醒。
【解决方案3】:

您可以使用更简单的方式来同步线程。

class Thread1 extends Thread {

    private static final Object lock = new Object();
    int myNum;
    char myChar;

    public Thread1(int num, char c){
        myNum = num;
        myChar = c;
    }

    public void run(){
        System.out.println(getName() + " is runing");
        synchronized(lock) {
            for(int i = 0; i<myNum; i++){
                if(i%10==0)
                    System.out.println("");
                System.out.print(myChar);
            }
        }
    }
}

【讨论】:

  • 呃,Jamie 不是已经提供了几乎相同的一段代码,以及一些解释(并且在我的回答中也简要提到过)吗?此外,您不需要在锁上使用 notifyAll。
猜你喜欢
  • 2013-05-23
  • 2021-09-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多