同步块如何工作?

时间:2019-01-08 06:27:50

标签: java multithreading synchronization

我正在学习多线程。我编写了一个使用同步程序来使用两个线程打印10和5表的程序。虽然同步方法给了我预期的结果,但同步块却没有。我究竟做错了什么?

public class SynchronizationDemo {

public static void main(String[] args) {
    Thread1 t=new Thread1(10);
    Thread1 t1=new Thread1(5);
    Thread thread1=new Thread(t);
    Thread thread2=new Thread(t1);
    thread1.start();
    thread2.start();
}
//synchronized method
/*public static synchronized void printTable(int num) {
    for (int i = 1; i <= 10; i++) {
        System.out.println(num*i);
        try {
            Thread.sleep(1000);
        }catch(InterruptedException ie){
            ie.printStackTrace();
        }
    }
}*/
//synchronized block
public void printTable(int num)
{
    synchronized(this){
        for (int i = 1; i <= 10; i++) {
            System.out.println(num*i);
            try {
                Thread.sleep(1000);
            }catch(InterruptedException ie){
                ie.printStackTrace();
            }
        }
     }
   }
 }

class Thread1 implements Runnable{
    int num;
    Thread1(int num){
        this.num=num;
    }
    @Override
    public void run() {
        new SynchronizationDemo().printTable(num);
    }
}

代码输出: 10 5 10 20 30 15 20 40 25 50 60 30 70 35 40 80 90 45 100 50

预期输出: 10 20 30 40 50 60 70 80 90 100 5 10 15 20 25 30 35 40 45 50

4 个答案:

答案 0 :(得分:4)

这两种方法的主要区别在于细节小而重要。

  • 您的同步块正在this上进行同步。

  • 您的同步方法(已注释掉!)是static方法。这意味着它正在SynchronizationDemo类对象上进行同步!

但这并不能解释全部。另一件事是调用方法的方式。

    new SynchronizationDemo().printTable(num);

您正在创建一个新实例,然后在其上调用该方法。

  • 调用静态方法时,使用哪个实例没有任何区别,因为您正在Class对象上进行同步。

  • 调用实例方法时,实例是不同的,因此因此根本没有相互排斥。当两个线程在同一个对象上同步时,您只会得到互斥和适当的同步。

答案 1 :(得分:1)

您在两个不同的上下文中使用synchronized关键字。

  1. static synchronized方法等待并获得SynchronizationDemo类级别的锁,并且该类只有一个锁。因此,该类的所有实例都必须按顺序等待该锁。

  2. synchronized(this)块(甚至是一个方法)等待并获取SynchronizationDemo类的对象锁,每个对象都有一个锁。当每个run()方法创建自己的实例new SynchronizationDemo()时,该方法无需等待其他方法完成即可。

尝试通过传递对象来更改Thread1类。

class Thread1 implements Runnable{
    SynchronizationDemo demo;
    int num;

    Thread1(SynchronizationDemo demo, int num){
        this.demo = demo;
        this.num = num;
    }

    @Override
    public void run() {
        demo.printTable(num);
    }
}

现在,您可以将SynchronizationDemo类的相同对象传递给两个线程,如下所示。

public static void main(String[] args) {
    SynchronizationDemo demo = new SynchronizationDemo();
    Thread1 t=new Thread1(demo, 10);
    Thread1 t1=new Thread1(demo, 5);
    Thread thread1=new Thread(t);
    Thread thread2=new Thread(t1);
    thread1.start();
    thread2.start();
}

答案 2 :(得分:0)

区别在于您还将方法更改为不再是static

public static synchronized void printTable(int num) { }

locks on the class, not on any single instance

等效的同步块为

synchronized(SynchronizationDemo.class){
}

因此,即使您有两个实例,它们仍然使用相同的锁(因为它们都是同一类),而synchronized(this)则锁在两个独立的对象上。

答案 3 :(得分:0)

您正在SynchronizationDemo线程方法中创建run()对象的新实例。

@Override
public void run() {
    new SynchronizationDemo().printTable(num);
}

因此,每个线程都有一个新对象。因此,在SynchronizationDemo.class中,如果两个或多个线程无法访问同一对象,则同步块是无用的。因此,线程工作于不同的对象,并且每个线程都打印出其编号。

如果您的代码运行了2次或更多次,则无论thread1是否早于thread2,您都可以看到不同的输出。

您应将static synchronized关键字与printTable方法配合使用;

public static synchronized  void printTable(int num){
     //do sth.
}