“同步”是什么意思?

我有一些关于synchronized关键字的用法和重要性的问题。

  • synchronized关键字的意义是什么?
  • 什么时候应该synchronized方法?
  • 这是什么意思编程和逻辑?

synchronized关键字是关于读取和写入相同variables,对象和资源的不同线程。 这在Java并不是一个小问题,但是这里是Sun的一句话:

synchronized方法可以实现一个简单的策略来防止线程干扰和内存一致性错误:如果一个对象对多个线程可见,则所有对该对象variables的读写操作都是通过同步方法完成的。

在非常非常小的情况下:如果有两个线程正在读取和写入相同的“资源”,请说一个名为foo的variables,则需要确保这些线程以primefaces方式访问该variables。 如果没有synchronized关键字,你的线程1可能看不到foo的变化线程2,或者更糟的是,它可能只有一半被改变。 这不会是你所期望的。

再次,这是Java一个不平凡的话题。 要了解更多信息,请在此处探索SO和Interwebs上的主题:

  • 并发
  • Java内存模型

继续探索这些话题,直到“Brian Goetz”这个名字与大脑中的“并发”这个术语永久相关。

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

 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); } } 

注意:只要前一个线程的执行没有完成, synchronized阻塞下一个线程对方法test()的调用。 线程一次可以访问这个方法。 如果没有synchronized所有的线程都可以同时访

当一个线程调用对象的同步方法'test'(这里的对象是'TheDemo'类的一个实例)时,它获得该对象的锁,任何新线程只要前一个线程不能调用同一对象的任何同步方法已获得锁的锁不释放。

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

输出与同步

 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 

输出没有同步

 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 

synchronized关键字阻止多个线程同时访问一个代码或对象块。 默认情况下, Hashtablesynchronized ,所以一次只能有一个线程访问表。

在使用non-synchronized构造(如HashMap ,必须在代码中构build线程安全function以防止内存一致性错误。

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

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

如果你的应用程序是单线程, synchronized块不提供好处。

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

这经常被称为使类是线程安全的,但我可以说这是一种委婉的说法。 虽然同步确保Vector的内部状态不被损坏,但这通常不会帮助Vector的用户。

考虑这个:

  if (vector.isEmpty()){ vector.add(data); } 

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

所以实际上,你必须同步你的应用程序代码。

因为方法级别的同步是a)当你不需要它的时候是昂贵的,b)当你需要同步的时候,不够用,所以现在有了非同步的replace(在Vector的情况下是ArrayList)。

最近,并发包已经发布,有许多巧妙的工具可以处理multithreading问题。

概观

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

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

更深

语法明智的是, synchronized关键字将一个Object作为它的参数(称为锁对象 ),然后是一个{ block of code }

  • 当执行遇到这个关键字时,当前线程试图“locking/获取/拥有”(select) 锁对象,并在锁获取后执行相关的代码块。

  • 任何对同步代码块中的variables的写入操作都可以保证对于其他每个使用同一个锁对象在同步代码块中执行代码的线程都可见。

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

同步方法:

synchronized关键字添加到方法定义中相当于将整个方法体封装在一个locking对象this (例如methods)ClassInQuestion.getClass() (用于类方法)的同步代码块中。

– 实例方法是没有static关键字的方法。
– 类方法是一个有static关键字的方法。

技术

没有同步,不能保证读取和写入的顺序发生,可能会留下垃圾。
(例如,一个variables可能以一个线程写入的一半位和另一个线程写入的一半位结束,使得variables处于两个线程均未尝试写入的状态,但两者混合在一起)。

在另一个线程读取之前(挂钟时间)完成写入操作是不够的,因为硬件可能已经caching了该variables的值,并且读取线程将看到caching的值而不是写入的内容它。

结论

因此在Java的情况下,您必须遵循Java内存模型来确保线程错误不会发生。
换句话说:使用同步,primefaces操作或类在他们之下使用它们。

来源

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

把它想象成一种像你在足球场上可以find的旋转门一样。 有些人想要进入,但在旋转门,他们是“同步”的平行蒸汽。 一次只能有一个人可以通过。 所有想要通过的人都会做,但是他们可能要等到他们能够通过。

什么是synchronized关键字?

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

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

什么时候同步方法?

方法在同步到方法定义或声明时synchronized 。 你也可以在一个方法中同步一个特定的代码块。

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

这意味着只有一个线程可以通过获取锁来访问临界区 。 除非这个线程释放这个锁,否则所有其他线程将不得不等待获取一个锁。 他们没有获得进入关键部分没有获得locking。

这不能用魔法来完成。 程序员有责任确定应用程序中的关键部分 ,并相应地加以保护。 Java提供了一个框架来保护你的应用程序,但是在哪里以及什么部分被守护是程序员的责任。

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

内部锁和同步:

同步是围绕被称为固有locking或监视器locking的内部实体构build的。 内在锁在同步的两个方面发挥作用:强制对对象状态的独占访问和build立发生 – 在对可见性至关重要的关系之前。

每个对象都有一个与之相关的固有锁 。 按照惯例,需要独占且一致地访问对象字段的线程在访问对象之前必须先获取对象的内部锁,然后在完成内部锁时释放内部锁。

一个线程被认为拥有获取锁和释放锁之间的固有锁。 只要一个线程拥有一个内部锁,其他线程就不能获得同一个锁。 另一个线程在尝试获取锁时会阻塞。

当一个线程释放一个固有的locking时,在该动作和任何后续的同一个locking获取之间build立一个发生之前的关系。

使方法同步有两个作用 :

首先,同一个对象上的两个同步方法的调用是不可能交错的。

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

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

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

在以下方面查找其他同步替代方法:

避免在Java中同步(this)?

我的理解同步基本上意味着编译器写你的方法周围的monitor.enter和monitor.exit。 因此,它可能是线程安全的,这取决于它是如何使用的(我的意思是你可以使用同步的方法编写一个不依赖于你的类的线程安全的对象)。

我知道你已经得到了你的答案。
我写这个,只是为了帮助那些有同样问题的人,并且正在寻找这个页面来寻求答案。
这里是来自java文档的解释:

考虑下面的代码:

 public class SynchronizedCounter { private int c = 0; public synchronized void increment() { c++; } public synchronized void decrement() { c--; } public synchronized int value() { return c; } } 

如果countSynchronizedCounter一个实例,那么使这些方法同步有两个作用:

  • 首先,同一个对象上的两个同步方法的调用是不可能交错的。 当一个线程正在执行一个对象的同步方法时,所有其他调用同一对象的同步方法的线程将阻塞(挂起执行),直到第一个线程完成对象。
  • 其次,当一个同步的方法退出时,它会自动build立一个与先前同步对象的任何后续调用同步方法的before-before关系。 这保证了对所有线程都可见的对象状态的改变。

同步简单意味着没有两个线程可以同时访问块/方法。 当我们说一个类的任何块/方法是同步的时,意味着一次只有一个线程可以访问它们。 在内部,尝试访问它的线程首先对该对象进行locking,只要该locking不可用,其他线程就不能访问该类实例的任何同步方法/块。

注意另一个线程可以访问同一个没有被定义为同步的对象的方法。 线程可以通过调用来释放锁

 Object.wait() 

简单同步意味着多个线程(如果与单个对象关联)可以防止脏读取和写入(如果在特定对象上使用同步块)。 为了让你更清楚,让我们举个例子:

 class MyRunnable implements Runnable { int var = 10; @Override public void run() { call(); } public void call() { synchronized (this) { for (int i = 0; i < 4; i++) { var++; System.out.println("Current Thread " + Thread.currentThread().getName() + " var value "+var); } } } } public class MutlipleThreadsRunnable { public static void main(String[] args) { MyRunnable runnable1 = new MyRunnable(); MyRunnable runnable2 = new MyRunnable(); Thread t1 = new Thread(runnable1); t1.setName("Thread -1"); Thread t2 = new Thread(runnable2); t2.setName("Thread -2"); Thread t3 = new Thread(runnable1); t3.setName("Thread -3"); t1.start(); t2.start(); t3.start(); } } 

我们创build了两个MyRunnable类对象,runnable1与线程1共享,而线程3和runnable2仅与线程2共享。 现在当t1和t3启动时没有同步被使用,PFB输出表明线程1和线程3同时影响var值,在线程2中,var有自己的内存。

 Without Synchronized keyword Current Thread Thread -1 var value 11 Current Thread Thread -2 var value 11 Current Thread Thread -2 var value 12 Current Thread Thread -2 var value 13 Current Thread Thread -2 var value 14 Current Thread Thread -1 var value 12 Current Thread Thread -3 var value 13 Current Thread Thread -3 var value 15 Current Thread Thread -1 var value 14 Current Thread Thread -1 var value 17 Current Thread Thread -3 var value 16 Current Thread Thread -3 var value 18 

使用Synchronzied,线程3等待线程1在所有情况下完成。 有两个锁获取,一个在线程1和线程3共享的runnable1上,另一个在线程2共享的runnable2上共享。

 Current Thread Thread -1 var value 11 Current Thread Thread -2 var value 11 Current Thread Thread -1 var value 12 Current Thread Thread -2 var value 12 Current Thread Thread -1 var value 13 Current Thread Thread -2 var value 13 Current Thread Thread -1 var value 14 Current Thread Thread -2 var value 14 Current Thread Thread -3 var value 15 Current Thread Thread -3 var value 16 Current Thread Thread -3 var value 17 Current Thread Thread -3 var value 18 

其他答案缺失的是一个重要的方面: 记忆障碍 。 线程同步基本上由部分组成:序列化和可见性。 我build议大家到谷歌“jvm内存障碍”,因为它是一个不平凡的,非常重要的话题(如果你修改multithreading访问共享数据)。 完成之后,我build议查看java.util.concurrent包的类,以避免使用显式同步,这又有助于保持程序简单高效,甚至可以防止死锁。

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

简而言之,当你启动一个线程,一个线程结束,你读取一个volatilevariables,一个监视器解锁(离开一个同步块/函数)等等时,“内存同步”会隐式地发生。这个“同步”会影响(某种意义上的“冲刷“)在特定行动之前完成的所有写作。 在前面提到的ConcurrentLinkedDeque的情况下,文档“说”:

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

这种隐含的行为是一个有点有害的方面,因为大多数没有太多经验的Java程序员将因为它而给予很多的东西。 然后突然之间,这个线程绊倒了Java没有做什么是“应该”做生产中有不同的工作负荷 – 这是很难testing并发问题。

同步是Java中的一个关键字,用于在multithreading环境下进行关联,以避免内存不一致和线程干扰错误。