繁体   English   中英

同步块如何工作?

[英]how synchronized block works?

我正在学习多线程。 我编写了一个使用同步程序来使用两个线程打印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

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

  • 您的同步块同步在this

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

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

    new SynchronizationDemo().printTable(num);

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

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

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

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

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

  2. synchronized(this)块(甚至一个方法)等待并获取SynchronizationDemo类的对象锁,每个对象都有一个锁。 当每个run()方法创建其自己的new SynchronizationDemo()实例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();
}

不同之处在于您还将方法更改为不再是static

public static synchronized void printTable(int num) { }

锁定在类上,而不在任何单个实例上

等效的同步块将是

synchronized(SynchronizationDemo.class){
}

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

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

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

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

如果thread1thread2早工作,请运行两次或两次以上的代码,可以看到不同的输出。

您应该将static synchronized关键字与printTable方法一起使用;

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM