没有静态的易失性关键字无法按预期工作

时间:2016-09-28 10:09:42

标签: java multithreading volatile

我理解变量上volatile和static关键字的区别。

静态变量可以由不同的实例更改,而Volatile变量可以由不同的线程更改。

但如果我删除了MY_INT变量的静态关键字,则下面的程序(从互联网复制并稍加修改)会挂起。

即使没有静态关键字,其他线程也应该看到对变量MY_INT的更新。但如果我删除静态它会挂起。

请帮助我理解这个问题。

{{1}}

2 个答案:

答案 0 :(得分:1)

您的错误是因为如果您从字段static中删除i关键字,则会有一个不同的字段i PrintOddAndEven的每个实例因此,您拥有2个实例,您有 2个不同的字段 i,以便Even线程将永远循环,因为它的i永远不会被修改,Odd线程会因同样的原因永远等待。当您将字段声明为static时,线程会共享相同字段 i,这样您就不会遇到错误。

您应该创建一个专用类来保存您的计数器并使用它的实例作为对象监视器,您将在PrintOddAndEven个实例之间共享,如下所示:

public class MyClass {
    volatile int i = 1;
}

public class PrintOddAndEven extends Thread {

    MyClass lock;

    PrintOddAndEven(MyClass lock) {
        this.lock = lock;
    }

    public static void main(String[] args) throws Exception {
        MyClass obj = new MyClass();
        PrintOddAndEven odd = new PrintOddAndEven(obj);
        PrintOddAndEven even = new PrintOddAndEven(obj);
        odd.setName("Odd");
        even.setName("Even");
        odd.start();
        even.start();
    }

    @Override
    public void run() {
        while (lock.i <= 10) {
            if (lock.i % 2 == 0 && Thread.currentThread().getName().equals("Even")) {
                synchronized (lock) {
                    System.out.println(Thread.currentThread().getName() + " - " + lock.i);
                    lock.i++;
                    lock.notify();
                }
            }
            if (lock.i % 2 == 1 && Thread.currentThread().getName().equals("Odd")) {
                synchronized (lock) {
                    System.out.println(Thread.currentThread().getName() + " - " + lock.i);
                    lock.i++;

                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

如果您只有计数器,您还可以考虑使用类AtomicInteger的实例作为计数器和对象监视器。代码将与上述相同,不同之处在于您将使用new AtomicInteger(1)创建一个实例,以将计数器初始化为1,然后使用get()获取当前值并incrementAndGet()增加柜台。

答案 1 :(得分:1)

你创建了2个PrintOddAndEven奇数和偶数线程对象,如果从这个语句中删除Static关键字 volatile int i = 1; i 没有保持类级别,因此每个thread对象有自己的i副本,当奇数线程执行它时,它更新了odd.i ++。但是,even.i保持为1,并且条件没有通过,它会使你的线程挂起而没有静态。

public class PrintOddAndEven extends Thread {
//static volatile  int i = 1;

Lock lock;

PrintOddAndEven(Lock lock) {
    this.lock = lock;
}


static class Lock {
     volatile  int i = 1;
}
public static void main(String ar[]) {
    Lock obj = new lock();
    PrintOddAndEven odd = new PrintOddAndEven(obj);
    PrintOddAndEven even = new PrintOddAndEven(obj);
    odd.setName("Odd");
    even.setName("Even");
    odd.start();
    even.start();
}

@Override
public void run() {
    while (lock.i <= 10) {
        if (lock.i % 2 == 0 && Thread.currentThread().getName().equals("Even")) {
            synchronized (lock) {
                System.out.println(Thread.currentThread().getName() + " - " + lock.i);
                lock.i++;
                lock.notify();
            }
        }
        if (lock.i % 2 == 1 && Thread.currentThread().getName().equals("Odd")) {
            synchronized (lock) {
                System.out.println(Thread.currentThread().getName() + " - " + lock.i);
                lock.i++;
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}}

通过这种方式,您可以在两个主题中共享锁定 i