Java - 同步块无法正常工作

时间:2017-05-19 17:58:27

标签: java multithreading synchronization

我有以下课程

public class OddPrinter implements Runnable  {
    public void run() {
        try {
            for (int n = 0; n <= 10; n++) {
                if((n%2) != 0)
                    System.out.println(" Odd Thread" + n);
                Thread.sleep(1000);
            }
            System.out.println("Exiting Odd Thread");
        }
        catch(InterruptedException e)
        {
            e.printStackTrace();
        }
    }
}

尝试对上述类的对象进行同步访问的主类

public class MultiThread {
    public static void main(String[] args) {
        Thread t1, t2;
        OddPrinter first = new OddPrinter();            
        synchronized(first)
        {           
            t1 = new Thread(first, "firstThread");
            t1.start();

            t2 = new Thread(first, "secondThread");
            t2.start();
        }           
    }       
}

我得到的输出如下

Odd Thread1
Odd Thread1
Odd Thread3
Odd Thread3
Odd Thread5
Odd Thread5
Odd Thread7
Odd Thread7
Odd Thread9
Odd Thread9
Exiting Odd Thread
Exiting Odd Thread
  

同步块确保   只调用当前线程后,才会调用对象成员的方法   成功进入了对象的监视器。

根据上面的参考文献(Java2 - The Complete Reference - Herbert Schildt),我期待一个输出,其中一个线程等待另一个线程完成奇数的打印。但那并没有发生。这是什么问题?

6 个答案:

答案 0 :(得分:1)

  

同步块确保只有在当前线程成功进入对象监视器后才会调用对象成员的方法。

不,它没有。它确保只有在当前线程退出块后才会出现 synchronized 方法,该方法是对象的实例成员,如果在同一个对象上调用,并且同一个对象上的另一个同步块在该块退出之前不会执行。

其中一些条件并不适用于您的代码。

答案 1 :(得分:0)

同步锁应该放在你的runnable代码中,而不是你的main方法。 我想你可以把同步放到你的方法

public class OddPrinter implements Runnable  {
    public synchronized void run() {
        try {
            for (int n = 0; n <= 10; n++) {
                if((n%2) != 0)
                    System.out.println(" Odd Thread" + n);
                Thread.sleep(1000);
            }
            System.out.println("Exiting Odd Thread");
        }
        catch(InterruptedException e)
        {
            e.printStackTrace();
        }
    }
}

或者,您可以将它放在您的方法中

public class OddPrinter implements Runnable  {
    static Object lock;
    public void run() {
      synchronized (lock) {
        try {
            for (int n = 0; n <= 10; n++) {
                if((n%2) != 0)
                    System.out.println(" Odd Thread" + n);
                Thread.sleep(1000);
            }
            System.out.println("Exiting Odd Thread");
        }
        catch(InterruptedException e)
        {
            e.printStackTrace();
        }
      }
    }
}

答案 2 :(得分:0)

您只同步了一次访问。其他线程不在对象上同步,因此不会阻止任何内容。即使他们这样做了,一旦两个线程都启动了,main中的代码块就完成了,并且线程可以自由运行。

此外,您在main中使用的同步发生在可能发生冲突的任何其他线程之前,并且主线程首先不需要任何共享状态,因此同步无用。 / p>

研究概念“关键部分”,“内存障碍”和Java“之前发生”。购买和研究Brian Goetz等人的书 Java Concurrency in Practice

答案 3 :(得分:0)

您需要更改OddPrinter,您可以在其中放置同步块并使用单个锁进行同步,以便一个线程可以进入临界区。并从main方法中删除synchronized块。

    public class OddPrinter implements Runnable {

        private Object lock;

        public OddPrinter(Object lock) {
            this.lock = lock;
        }

        public void run() {

            synchronized (lock) {
                for (int n = 0; n <= 10; n++) {
                    if ((n % 2) != 0)
                        System.out.println(" Odd Thread" + n);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                System.out.println("Exiting Odd Thread");
            }

        }
    }

public class MultiThread {
    public static void main(String[] args) {
        Thread t1, t2;
        Object object=new Object();        

            t1 =new Thread(new OddPrinter(object),"firstThread");
            t1.start();

            t2 =new Thread(new OddPrinter(object),"secondThread"); 
            t2.start();

    }       
}

答案 4 :(得分:0)

这绝对不是同步应该实现的方式。在您的实现中,没有监视器/锁定或通知实现,因为获取的锁定很快就超出了主线程的控制范围。

这样做是完全错误的。如果你想做一些独家的事情,锁应该被锁在你的线程内 - 而不是在外面!。

public class MyThread implements Runnable {

 private Object mutex;
 public MyThread(Object sharedObject) {
     this.mutex = sharedObject;
 }

 public void run() {

    // Method 1 -- Class reference used as the  mutex: locks and executes only one instance between the blocks
    synchronized (MyThread.class) {
    }

   // Method 2 -- All the same instance of the object reference used as mutex receives a blocked interference, and only one thread is executed.
   synchronized (mutex) {
   }
 }

 // Method 3 - Only one synchronized method inside the class is executed at any given point in time.
 private synchronized void produce() {
 }

 // Method 3 (Contd.) - Added in conjunction with produce() call
 private synchronized void consume() {
 }
}

答案 5 :(得分:0)

  

我期待一个输出,其中一个线程等待另一个线程完成奇数的打印。

在开始线程t1之前等待线程t2完成:

t1.start():
t1.join();
t2.start();