2016-09-28 23 views
1

我了解变量上挥发性和静态关键字之间的区别。挥发性关键字无静态按预期工作

静态变量可以被不同的实例改变,而易失性变量可以被不同的线程改变。

但是,下面的程序(复制从互联网和很少修改)挂起,如果我删除MY_INT变量的静态关键字。

即使没有静态关键字,其他线程也应该看到变量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(); 
        } 
       } 
      } 
     } 
    } 
} 
+1

可能的重复[你有没有在Java中使用volatile关键字?](http://stackoverflow.com/questions/106591/do-you-ever-use-the-volatile-keyword-in-java) – xenteros

+0

@xenteros不,这是一个问题的重复,它询问'static'的含义。 –

回答

1

你的错误是由于这样的事实,如果你从外地i删除static关键字,你将有一个不同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()增加计数器。

+0

谢谢。我知道了!! –

1

您创建了2个PrintOddAndEven奇数和偶数线程对象,如果您从此语句中删除Static关键字volatile int i = 1;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(); 
       } 
      } 
     } 
    } 
}} 

这样你就可以分享两个线程之间。