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

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


当前回答

synchronized关键字可以防止多个线程并发访问一个代码块或对象。Hashtable的所有方法都是同步的,因此一次只有一个线程可以执行其中的任何方法。

当使用非同步结构(如HashMap)时,必须在代码中构建线程安全特性以防止一致性错误。

其他回答

在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

What the other answers are missing is one important aspect: memory barriers. Thread synchronization basically consists of two parts: serialization and visibility. I advise everyone to google for "jvm memory barrier", as it is a non-trivial and extremely important topic (if you modify shared data accessed by multiple threads). Having done that, I advise looking at java.util.concurrent package's classes that help to avoid using explicit synchronization, which in turn helps keeping programs simple and efficient, maybe even preventing deadlocks.

ConcurrentLinkedDeque就是这样一个例子。与命令模式一起,它允许通过将命令填充到并发队列中来创建高效的工作线程——不需要显式同步,不可能发生死锁,不需要显式sleep(),只需通过调用take()轮询队列。

简而言之:“内存同步”隐式地发生在线程启动、线程结束、读取volatile变量、解锁监视器(留下同步块/函数)等时候。这种“同步”影响(在某种意义上“刷新”)在该特定操作之前完成的所有写操作。在前面提到的ConcurrentLinkedDeque的情况下,文档“说”:

内存一致性影响:与其他并发集合一样, 对象放入线程之前的操作 ConcurrentLinkedDeque访问之后的happens -before操作 或者从另一个ConcurrentLinkedDeque中删除该元素 线程。

这种隐式行为在某种程度上是有害的,因为大多数没有太多经验的Java程序员会因此而得过其实。然后突然被这个线程绊倒,因为Java在生产中没有做它“应该”做的事情,因为有不同的工作负载——并且很难测试并发性问题。

同步法等效于正常方法 同步语句(使用此语句)

class A {
    public synchronized void methodA() {
        // all function code
    }

    equivalent to

    public void methodA() {
        synchronized(this) {
             // all function code
        }
    } 
}

同步静态方法等价于Synchronized语句(使用类)

class A {
    public static synchronized void methodA() {
        // all function code
    }

    equivalent to

    public void methodA() {
        synchronized(A.class) {
             // all function code
        }
    } 
}

同步语句(使用变量)

class A {
    private Object lock1 = new Object();

    public void methodA() {
        synchronized(lock1 ) {
             // all function code
        }
    } 
}

对于同步,我们有同步方法和同步语句。然而,同步方法类似于同步语句,所以我们只需要理解同步语句。

基本上,我们会有

synchronized(object or class) { // object/class use to provides the intrinsic lock
   // code 
}

这里有2个想法,有助于理解同步

每个对象/类都有一个与之相关的内在锁。 当线程调用同步语句时,它会自动获取同步语句对象的内在锁,并在方法返回时释放锁。只要一个线程拥有一个内在锁,其他线程就不能获得相同的锁=>线程安全。

= > 当线程a调用synchronized(this){// code 1} =>时,所有synchronized(this)的块代码(类内)和所有synchronized的正常方法(类内)都被锁定,因为是SAME锁。它将在线程A解锁("// code 1"完成)后执行。

此行为类似于synchronized(变量){// code 1}或synchronized(类)。

SAME LOCK =>锁(不依赖于哪个方法?或者哪些陈述?)

使用同步方法还是同步语句?

我更喜欢同步语句,因为它更易于扩展。例如,在将来,你只需要同步方法的一部分。例如,你有两个synchronized方法,它们之间没有任何关联,但是当一个线程运行一个方法时,它会阻塞另一个方法(它可以通过使用synchronized(一个变量)来防止)。

然而,应用同步方法很简单,代码看起来很简单。对于某些类,只有一个同步方法,或者类中所有同步方法彼此相关=>,我们可以使用同步方法使代码更短,更容易理解

Note

(它与同步无关,它是对象与类或非静态与静态之间的区别)。

当你使用synchronized或普通方法或synchronized(this)或synchronized(非静态变量)时,它将基于每个对象实例进行同步。 当你使用synchronized或静态方法或synchronized(类)或synchronized(静态变量)时,它将基于类进行同步

参考

https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html

希望能有所帮助

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

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

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

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