'同步'是什么意思?

Joh*_*nna 958 java multithreading synchronized keyword

我对synchronized关键字的用法和重要性有一些疑问.

  • synchronized关键字有什么意义?
  • 方法何时应该synchronized
  • 这在编程和逻辑上意味着什么?

Stu*_*son 855

synchronized关键字是所有不同的线程读取和写入相同的变量,对象和资源.这不是Java中的一个简单主题,但这里引用了Sun:

synchronized 方法启用一个简单的策略来防止线程干扰和内存一致性错误:如果一个对象对多个线程可见,则对该对象变量的所有读取或写入都是通过同步方法完成的.

简而言之:如果有两个线程正在读取和写入相同的"资源",比如一个名为的变量foo,则需要确保这些线程以原子方式访问变量.如果没有synchronized关键字,您的线程1可能看不到更改线程2 foo,或者更糟糕的是,它可能只有一半更改.这不是你逻辑上所期望的.

同样,这是Java中的一个非平凡的主题.要了解更多信息,请在此处探索有关SO和Interwebs的主题:

继续探索这些主题,直到名称"Brian Goetz"与您大脑中的"并发"一词永久相关.

  • synchronized关键字是使您的代码线程安全的工具之一.只在方法或变量上使用synchronized可能会也可能不会.对Java内存模型有基本的了解对于获得正确的并发性非常重要. (79认同)
  • 那么,基本上这个Synchronized关键字使你的方法是线程安全的吗? (70认同)
  • 除非你是Brian Goetz(或者也许是Jon Skeet),否则几乎不可能只使用语言原语(synchronized,volatile)来使Java并发正确.对于初学者,请使用java.util.concurrent包并在此基础上构建. (26认同)
  • 更清楚:不能从多个线程同时调用同步方法. (12认同)
  • @peterh synchronized比那更多,因此更详细的解释 (2认同)
  • @dhfromkorea nope.完全不同.实际上,"序列化"是获取一个对象的过程,该对象可以存储在各种位置的存储器中,并将其"序列化"为一维的字节数组.这是为了存储或传输. (2认同)

Dhe*_*han 282

好吧,我认为我们有足够的理论解释,所以请考虑这个代码

public class SOP {
    public static void print(String s) {
        System.out.println(s+"\n");
    }
}

public class TestThread extends Thread {
    String name;
    TheDemo theDemo;
    public TestThread(String name,TheDemo theDemo) {
        this.theDemo = theDemo;
        this.name = name;
        start();
    }
    @Override
    public void run() {
        theDemo.test(name);
    }
}

public class TheDemo {
    public synchronized void test(String name) {
        for(int i=0;i<10;i++) {
            SOP.print(name + " :: "+i);
            try{
                Thread.sleep(500);
            } catch (Exception e) {
                SOP.print(e.getMessage());
            }
        }
    }
    public static void main(String[] args) {
        TheDemo theDemo = new TheDemo();
        new TestThread("THREAD 1",theDemo);
        new TestThread("THREAD 2",theDemo);
        new TestThread("THREAD 3",theDemo);
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:synchronized只要前一个线程的执行没有完成,就阻止下一个线程调用方法test().线程可以一次访问一个方法.没有synchronized所有线程可以同时访问此方法.

当一个线程调用对象的synchronized方法'test'时(这里的对象是'TheDemo'类的实例)它获取该对象的锁,任何新线程都不能调用相同对象的ANY synchronized方法,只要前一个线程获得锁的人不会释放锁.

当调用类的任何静态同步方法时,会发生类似的事情.线程获取与类关联的锁(在这种情况下,任何线程都可以调用该类实例的任何非静态同步方法,因为该对象级锁仍然可用).只要当前持有锁的线程没有释放类级别锁,任何其他线程将无法调用该类的任何静态同步方法.

输出同步

THREAD 1 :: 0
THREAD 1 :: 1
THREAD 1 :: 2
THREAD 1 :: 3
THREAD 1 :: 4
THREAD 1 :: 5
THREAD 1 :: 6
THREAD 1 :: 7
THREAD 1 :: 8
THREAD 1 :: 9
THREAD 3 :: 0
THREAD 3 :: 1
THREAD 3 :: 2
THREAD 3 :: 3
THREAD 3 :: 4
THREAD 3 :: 5
THREAD 3 :: 6
THREAD 3 :: 7
THREAD 3 :: 8
THREAD 3 :: 9
THREAD 2 :: 0
THREAD 2 :: 1
THREAD 2 :: 2
THREAD 2 :: 3
THREAD 2 :: 4
THREAD 2 :: 5
THREAD 2 :: 6
THREAD 2 :: 7
THREAD 2 :: 8
THREAD 2 :: 9
Run Code Online (Sandbox Code Playgroud)

输出没有同步

THREAD 1 :: 0
THREAD 2 :: 0
THREAD 3 :: 0
THREAD 1 :: 1
THREAD 2 :: 1
THREAD 3 :: 1
THREAD 1 :: 2
THREAD 2 :: 2
THREAD 3 :: 2
THREAD 1 :: 3
THREAD 2 :: 3
THREAD 3 :: 3
THREAD 1 :: 4
THREAD 2 :: 4
THREAD 3 :: 4
THREAD 1 :: 5
THREAD 2 :: 5
THREAD 3 :: 5
THREAD 1 :: 6
THREAD 2 :: 6
THREAD 3 :: 6
THREAD 1 :: 7
THREAD 2 :: 7
THREAD 3 :: 7
THREAD 1 :: 8
THREAD 2 :: 8
THREAD 3 :: 8
THREAD 1 :: 9
THREAD 2 :: 9
THREAD 3 :: 9
Run Code Online (Sandbox Code Playgroud)

  • 很好的例子,知道理论很好,但代码总是更具体和完整. (6认同)
  • @boltup_im_coding:start()方法将Thread置于"RUNNABLE"状态,这意味着它已准备好执行或已经执行.可能发生在Runnable状态中的另一个线程(通常但不一定具有更高优先级)跳转队列并开始执行.在上面的例子中,THREAD 3恰好在THREAD 2之前获得了CPU. (3认同)
  • @SantiIglesias"完整"?不.此示例演示了`synchronized`的锁定行为,但忽略了内存一致性. (2认同)
  • @Stu Thompson内存一致性是锁定的结果 (2认同)

jmo*_*253 112

synchronized关键字可防止多个线程对代码块或对象的并发访问.默认情况下,a Hashtablesynchronized,因此一次只能有一个线程访问该表.

在使用non-synchronized类似的结构时HashMap,必须在代码中构建线程安全功能以防止内存一致性错误.


Cod*_*nci 80

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

第二个线程将等待,直到第一个线程完成其执行.开销是速度,但优点是保证数据的一致性.

如果您的应用程序是单线程的,则synchronized块不会带来好处.


Thi*_*ilo 53

synchronized关键字导致线程在进入方法时获得锁定,因此只有一个线程可以同时执行该方法(对于给定的对象实例,除非它是静态方法).

这通常被称为使类线程安全,但我会说这是一个委婉说法.虽然同步保护Vector的内部状态不会被破坏,但这通常不会帮助Vector的用户.

考虑一下:

 if (vector.isEmpty()){
     vector.add(data);
 }
Run Code Online (Sandbox Code Playgroud)

即使所涉及的方法是同步的,因为它们被单独锁定和解锁,两个不幸的定时线程可以创建具有两个元素的向量.

因此,实际上,您还必须在应用程序代码中进行同步.

因为当你不需要它时方法级同步是昂贵的而且b)当你需要同步时不够用,所以现在有不同步的替换(在Vector的情况下为ArrayList).

最近,并发软件包已经发布,有许多聪明的实用程序可以解决多线程问题.


Gim*_*ima 27

概观

Java中的同步关键字与线程安全有关,也就是说,当多个线程读取或写入相同的变量时.
这可以直接发生(通过访问相同的变量)或间接发生(通过使用使用另一个访问同一变量的类的类).

synchronized关键字用于定义代码块,其中多个线程可以安全地访问同一变量.

更深的

语法 - synchronized关键字采用Objectas的参数(称为锁定对象),然后是a { block of code }.

  • 当执行遇到此关键字时,当前线程会尝试"锁定/获取/拥有"(选择)锁定对象,并在获取锁定后执行相关的代码块.

  • 对同步代码块内的变量的任何写入都保证对于使用相同锁对象在类似地执行同步代码块内的代码的每个其他线程可见.

  • 一次只有一个线程可以保持锁定,在此期间,尝试获取相同锁定对象的所有其他线程将等待(暂停执行).执行退出同步代码块时将释放锁定.

同步方法:

synchronized方法定义添加关键字等于整个方法体被包装在同步代码块中,锁定对象this (例如方法)ClassInQuestion.getClass() (对于类方法).

- 实例方法是一种没有static关键字的方法.
- 类方法是一种具有static关键字的方法.

技术

如果没有同步,则无法保证读取和写入的顺序发生,可能会使变量带有垃圾.
(例如,一个变量可能最终由一个线程写入的一半位和另一个线程写入的一半位,使变量处于两个线程都没有尝试写入的状态,而是两者的组合混乱.)

在另一个线程读取它之前(挂钟时间)完成一个线程中的写操作是不够的,因为硬件可能已经缓存了变量的值,并且读取线程将看到缓存的值而不是写入的值它.

结论

因此,在Java的情况下,您必须遵循Java内存模型以确保不会发生线程错误.
换句话说:使用同步,原子操作或在引擎盖下使用它们的类.

来源

http://docs.oracle.com/javase/specs/jls/se8/html/index.htmlJava®
语言规范,2015-02-13


pau*_*aul 21

把它想象成你可能在足球场上发现的一种旋转门.人们想要进入平行的蒸汽,但在旋转门处他们是"同步的".一次只能有一个人通过.所有想要通过的人都会这样做,但他们可能要等到他们能够完成.


Rav*_*abu 17

什么是synchronized关键字?

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

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

方法何时同步?

添加synchronized到方法定义或声明时,方法会同步.您还可以使用方法同步特定的代码块.

什么是语法和逻辑上的意思?

这意味着只有一个线程可以通过获取锁来访问关键部分.除非此线程释放此锁,否则所有其他线程将不得不等待获取锁.他们无权进入关键部分而无法获得锁定.

这不能用魔法来完成.程序员有责任识别应用中的关键部分并相应地保护它.Java提供了一个框架来保护您的应用程序,但是保护所有部分的位置和内容是程序员的责任.

来自java文档页面的更多细节

内在锁定和同步:

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

每个对象都有一个与之关联的内在锁.按照惯例,需要对对象字段进行独占和一致访问的线程必须在访问对象之前获取对象的内部锁,然后在完成它们时释放内部锁.

据说一个线程在获得锁定和释放锁定之间拥有内在锁定.只要一个线程拥有一个内部锁,没有其他线程可以获得相同的锁.另一个线程在尝试获取锁时将阻塞.

当线程释放内部锁时,在该操作与同一锁的任何后续获取之间建立先发生关系.

使方法同步有两个影响:

首先,对同一对象的两个同步方法的调用不可能进行交错.

当一个线程正在为对象执行同步方法时,所有其他线程调用同一对象的同步方法(暂停执行)直到第一个线程完成对象.

其次,当同步方法退出时,它会自动与同一对象的同步方法的任何后续调用建立先发生关系.

这可以保证对所有线程都可以看到对象状态的更改.

寻找其他同步替代方案:

避免在Java中同步(this)?


Sha*_*ghi 12

以下是The Java Tutorials的解释.

请考虑以下代码:

public class SynchronizedCounter {
    private int c = 0;

    public synchronized void increment() {
        c++;
    }

    public synchronized void decrement() {
        c--;
    }

    public synchronized int value() {
        return c;
    }
}
Run Code Online (Sandbox Code Playgroud)

if count是一个实例SynchronizedCounter,然后使这些方法同步有两个效果:

  • 首先,对同一对象的两个同步方法的调用不可能进行交错.当一个线程正在为对象执行同步方法时,所有其他线程调用同一对象的同步方法(暂停执行)直到第一个线程完成对象.
  • 其次,当同步方法退出时,它会自动与同一对象的同步方法的任何后续调用建立先发生关系.这可以保证对所有线程都可以看到对象状态的更改.


Spe*_*nce 10

据我所知,synchronized基本上意味着编译器在你的方法周围写一个monitor.enter和monitor.exit.因此它可能是线程安全的,具体取决于它的使用方式(我的意思是你可以使用同步方法编写一个不是线程安全的对象,具体取决于你的类所做的事情).


Pha*_*inh 10

Synchronized normal method相当于 Synchronized statement(使用此)

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

    equivalent to

    public void methodA() {
        synchronized(this) {
             // all function code
        }
    } 
}
Run Code Online (Sandbox Code Playgroud)

Synchronized static method相当于Synchronized statement(使用类)

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

    equivalent to

    public void methodA() {
        synchronized(A.class) {
             // all function code
        }
    } 
}
Run Code Online (Sandbox Code Playgroud)

同步语句(使用变量)

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

    public void methodA() {
        synchronized(lock1 ) {
             // all function code
        }
    } 
}
Run Code Online (Sandbox Code Playgroud)

因为synchronized,我们有Synchronized MethodsSynchronized Statements.但是,Synchronized Methods类似于Synchronized Statements我们只需要了解Synchronized Statements.

=>基本上,我们会有

synchronized(object or class) { // object/class use to provides the intrinsic lock
   // code 
}
Run Code Online (Sandbox Code Playgroud)

这是2认为有助于理解 synchronized

  • 每个对象/类都intrinsic lock与它相关联.
  • 当一个线程调用synchronized statement时,它自动获取intrinsic locksynchronized statement's对象并将其释放的方法返回时.只要一个线程拥有一个intrinsic lock,NO其他线程就可以获得SAME lock => thread safe.

=>当一个thread Ainvokes synchronized(this){// code 1}=>所有的块代码(内部类),其中has synchronized(this)和all synchronized normal method(内部类)被锁定,因为SAME锁定.它将在thread A解锁后执行("// code 1"完成).

此行为类似于synchronized(a variable){// code 1}synchronized(class).

SAME LOCK => lock(不依赖于哪种方法?或哪些语句?)

使用synchronized方法或synchronized语句?

我更喜欢,synchronized statements因为它更具扩展性.例如,将来,您只需要同步一部分方法.例如,你有2个同步方法,它没有任何相关的方法,但是当一个线程运行一个方法时,它将阻止另一个方法(它可以防止使用synchronized(a variable)).

但是,应用synchronized方法很简单,代码看起来很简单.对于某些类,只有1个同步方法,或者类中所有同步方法相互关联=>我们可以使用synchronized method更简单易懂的代码

注意

(它与多少无关synchronized,它是对象和类之间或非静态和静态的不同).

  • 当您使用synchronized或普通方法或synchronized(this)synchronized(non-static variable)它将基于每个对象实例同步.
  • 当您使用synchronized或静态方法或synchronized(class)synchronized(static variable)它将基于类同步

参考

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

希望它有所帮助


use*_*755 6

其他答案遗漏的一个重要方面是:内存障碍。线程同步基本上包括部分:序列化和可见性。我建议每个人都在Google上寻找“ jvm内存屏障”,因为这是一个不重要且极为重要的主题(如果您修改了多个线程访问的共享数据)。完成此操作后,我建议查看java.util.concurrent包的类,这些类有助于避免使用显式同步,这反过来又有助于使程序保持简单高效,甚至可以防止死锁。

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

简而言之:当启动线程,线程结束,读取易失性变量,解锁监视器(保留同步的块/功能)等时,“内存同步”会隐式发生。这种“同步”会(在某种意义上说“刷新” “)在该特定操作之前完成的所有写入。对于上述ConcurrentLinkedDeque,文档“说”:

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

这种隐式行为在某种程度上是一个有害的方面,因为大多数没有经验的Java程序员都会因此而付出很多。然后,在Java没有执行工作负载不同的生产中应该做的事情之后,突然在该线程上绊倒了,这很难测试并发问题。