没有 static 的 volatile 关键字不能按预期工作
Volatile keyword without static not working as expected
我了解变量的 volatile 和 static 关键字之间的区别。
static 变量可以被不同的实例改变,而 Volatile 变量可以被不同的线程改变。
但是如果我删除 MY_INT 变量的 static 关键字,下面的程序(从互联网复制并稍作修改)就会挂起。
变量 MY_INT 的更新应该被其他线程看到,即使没有静态关键字。但是如果我删除静态它会挂起。
请帮助我理解这个问题。
public class PrintOddAndEven extends Thread {
static volatile int i = 1;
Object lock;
PrintOddAndEven(Object lock) {
this.lock = lock;
}
public static void main(String ar[]) {
Object obj = new Object();
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 (i <= 10) {
if (i % 2 == 0 && Thread.currentThread().getName().equals("Even")) {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + " - " + i);
i++;
lock.notify();
}
}
if (i % 2 == 1 && Thread.currentThread().getName().equals("Odd")) {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + " - " + i);
i++;
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
您的错误是因为如果您从字段 i
中删除 static
关键字,您将有一个 不同的 字段 i
每个 PrintOddAndEven
的实例 所以这里因为你有 2
个实例,所以你有 2 个不同的字段 i
这样 Even
线程将永远循环,因为它的 i
永远不会被修改,并且 Odd
线程出于同样的原因永远等待。当您将字段声明为 static
时,线程共享 相同的字段 i
这样您就不会遇到错误。
您应该创建一个专用的 class 来保存您的计数器,并将它的一个实例用作您将在 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();
}
}
}
}
}
}
如果您只有一个计数器,您还可以考虑使用class AtomicInteger
的实例作为计数器和对象监视器。除了您将使用 new AtomicInteger(1)
创建一个实例以将计数器初始化为 1
然后使用 get()
获取当前值和 incrementAndGet()
之外,代码将与上面相同增加计数器。
你已经创建了 2 个 PrintOddAndEven 奇数和偶数线程对象,如果你从此语句中删除 Static 关键字 volatile int i = 1; i 没有保持 class 级别,因此每个线程对象都有自己的 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();
}
}
}
}
}}
这样你就可以在两个线程之间共享 lock 和 i。
我了解变量的 volatile 和 static 关键字之间的区别。
static 变量可以被不同的实例改变,而 Volatile 变量可以被不同的线程改变。
但是如果我删除 MY_INT 变量的 static 关键字,下面的程序(从互联网复制并稍作修改)就会挂起。
变量 MY_INT 的更新应该被其他线程看到,即使没有静态关键字。但是如果我删除静态它会挂起。
请帮助我理解这个问题。
public class PrintOddAndEven extends Thread {
static volatile int i = 1;
Object lock;
PrintOddAndEven(Object lock) {
this.lock = lock;
}
public static void main(String ar[]) {
Object obj = new Object();
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 (i <= 10) {
if (i % 2 == 0 && Thread.currentThread().getName().equals("Even")) {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + " - " + i);
i++;
lock.notify();
}
}
if (i % 2 == 1 && Thread.currentThread().getName().equals("Odd")) {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + " - " + i);
i++;
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
您的错误是因为如果您从字段 i
中删除 static
关键字,您将有一个 不同的 字段 i
每个 PrintOddAndEven
的实例 所以这里因为你有 2
个实例,所以你有 2 个不同的字段 i
这样 Even
线程将永远循环,因为它的 i
永远不会被修改,并且 Odd
线程出于同样的原因永远等待。当您将字段声明为 static
时,线程共享 相同的字段 i
这样您就不会遇到错误。
您应该创建一个专用的 class 来保存您的计数器,并将它的一个实例用作您将在 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();
}
}
}
}
}
}
如果您只有一个计数器,您还可以考虑使用class AtomicInteger
的实例作为计数器和对象监视器。除了您将使用 new AtomicInteger(1)
创建一个实例以将计数器初始化为 1
然后使用 get()
获取当前值和 incrementAndGet()
之外,代码将与上面相同增加计数器。
你已经创建了 2 个 PrintOddAndEven 奇数和偶数线程对象,如果你从此语句中删除 Static 关键字 volatile int i = 1; i 没有保持 class 级别,因此每个线程对象都有自己的 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();
}
}
}
}
}}
这样你就可以在两个线程之间共享 lock 和 i。