我有一些关于同步关键字的用法和意义的问题。

synchronized关键字有什么意义? 什么时候应该同步方法? 它在程序上和逻辑上意味着什么?


当前回答

你可以把它想象成一个转门,就像你在足球场看到的那样。有平行的人群想要进入,但在旋转门他们是“同步的”。一次只能有一个人通过。所有想要通过的人都可以,但他们可能要等到他们能够通过。

其他回答

在java中,为了防止多个线程操纵一个共享变量,我们使用synchronized关键字。让我们通过下面的例子来理解它:

在这个例子中,我定义了两个线程,并将它们命名为increment和decincrement。增量线程增加共享变量(计数器)的值,增量线程减少共享变量(计数器)的值,即增加5000次(结果是5000 + 0 = 5000),减少5000次(结果是5000 - 5000 = 0)。

没有同步关键字的程序:

class SynchronizationDemo {

    public static void main(String[] args){

        Buffer buffer = new Buffer();                   

        MyThread incThread = new MyThread(buffer, "increment");
        MyThread decThread = new MyThread(buffer, "decrement"); 

        incThread.start();
        decThread.start();  
       
        try {
          incThread.join();
          decThread.join();
        }catch(InterruptedException e){ }

        System.out.println("Final counter: "+buffer.getCounter());
    }
}

class Buffer {
    private int counter = 0; 
    public void inc() { counter++; }
    public void dec() { counter--; } 
    public int getCounter() { return counter; }
}

class MyThread extends Thread {

    private String name;
    private Buffer buffer;

    public MyThread (Buffer aBuffer, String aName) {            
        buffer = aBuffer; 
        name = aName; 
    }

    public void run(){
        for (int i = 0; i <= 5000; i++){
            if (name.equals("increment"))
                buffer.inc();
            else
                buffer.dec();                           
        }
    }
}

如果我们运行上面的程序,我们期望缓冲区的值是相同的,因为缓冲区的增量和减量相同,将会得到我们开始时的初始值,对吗?让我们看看输出:

正如你所看到的,无论我们运行程序多少次,我们都会得到不同的结果,因为每个线程都在同一时间操纵计数器。如果我们能设法让一个线程先增加共享变量,然后再减少它,反之亦然,我们就会得到正确的结果,这正是synchronized关键字所能做到的,只需在Buffer的inc和dec方法之前添加synchronized关键字,如下所示:

关键字为synchronized的程序:

// rest of the code

class Buffer {
    private int counter = 0; 
    // added synchronized keyword to let only one thread
    // be it inc or dec thread to manipulate data at a time
    public synchronized void inc() { counter++; }
    public synchronized void dec() { counter--; } 
    public int getCounter() { return counter; }
}

// rest of the code

输出:

不管我们运行多少次,我们得到的输出都是0

synchronized关键字是什么?

线程主要通过共享对字段和引用字段所引用的对象的访问进行通信。这种通信形式非常高效,但可能会产生两种错误:线程干扰和内存一致性错误。防止这些错误所需的工具是同步。

同步块或方法可以防止线程干扰,并确保数据是一致的。在任何时候,只有一个线程可以通过获得锁来访问同步块或方法(临界区)。其他线程将等待锁释放来访问临界区。

方法何时同步?

将synchronized添加到方法定义或声明时,方法是同步的。您还可以在方法中同步特定的代码块。

它在语法上和逻辑上意味着什么?

这意味着只有一个线程可以通过获得锁来访问临界区。除非这个线程释放这个锁,否则所有其他线程将不得不等待获得一个锁。他们不能在没有锁的情况下进入临界区。

这不是魔法能办到的。程序员的职责是识别应用程序中的关键部分并相应地保护它。Java提供了一个框架来保护你的应用程序,但是在哪里以及保护哪些部分是程序员的责任。

更多细节,请参阅java文档页

内在锁和同步:

同步是围绕一个称为内在锁或监视器锁的内部实体构建的。内在锁在同步的两个方面都发挥作用:强制对对象状态的独占访问,并建立对可见性至关重要的happens-before关系。

每个对象都有一个与之相关的内在锁。按照惯例,需要独占和一致访问对象字段的线程必须在访问对象字段之前获得对象的内在锁,然后在使用完对象字段时释放内在锁。

线程在获得锁和释放锁之间拥有内在锁。只要一个线程拥有一个内在锁,其他线程就不能获得相同的锁。另一个线程在试图获取锁时将阻塞。

当线程释放一个内在锁时,在该操作和后续获得的任何相同锁之间建立happens-before关系。

使方法同步有两个效果:

首先,对同一对象的同步方法的两次调用不可能交织。

当一个线程正在为一个对象执行同步方法时,所有为同一对象调用同步方法的其他线程将暂停执行,直到第一个线程处理完该对象。

其次,当同步方法退出时,它自动与同一对象的同步方法的任何后续调用建立happens-before关系。

这保证了对对象状态的更改对于所有线程都是可见的。

寻找同步的其他替代方案:

避免在Java中同步(这)?

Synchronized意味着在多线程环境中,具有同步方法/块的对象不允许两个线程同时访问Synchronized方法/块的代码。这意味着一个线程不能读取,而另一个线程更新它。

第二个线程将等待第一个线程完成它的执行。开销是速度,但好处是保证了数据的一致性。

如果你的应用程序是单线程的,同步块并不能带来什么好处。

你可以把它想象成一个转门,就像你在足球场看到的那样。有平行的人群想要进入,但在旋转门他们是“同步的”。一次只能有一个人通过。所有想要通过的人都可以,但他们可能要等到他们能够通过。

synchronized是Java中的关键字,用于在多线程环境中使发生在关系之前,以避免内存不一致和线程干扰错误。