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"与您大脑中的"并发"一词永久相关.
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)
jmo*_*253 112
该synchronized
关键字可防止多个线程对代码块或对象的并发访问.默认情况下,a Hashtable
是synchronized
,因此一次只能有一个线程访问该表.
在使用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
关键字采用Object
as的参数(称为锁定对象),然后是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文档页面的更多细节
内在锁定和同步:
同步是围绕称为内部锁或监视器锁的内部实体构建的.内部锁在同步的两个方面都起作用:强制对对象状态进行独占访问,并建立对可见性至关重要的先发生关系.
每个对象都有一个与之关联的内在锁.按照惯例,需要对对象字段进行独占和一致访问的线程必须在访问对象之前获取对象的内部锁,然后在完成它们时释放内部锁.
据说一个线程在获得锁定和释放锁定之间拥有内在锁定.只要一个线程拥有一个内部锁,没有其他线程可以获得相同的锁.另一个线程在尝试获取锁时将阻塞.
当线程释放内部锁时,在该操作与同一锁的任何后续获取之间建立先发生关系.
使方法同步有两个影响:
首先,对同一对象的两个同步方法的调用不可能进行交错.
当一个线程正在为对象执行同步方法时,所有其他线程调用同一对象的同步方法(暂停执行)直到第一个线程完成对象.
其次,当同步方法退出时,它会自动与同一对象的同步方法的任何后续调用建立先发生关系.
这可以保证对所有线程都可以看到对象状态的更改.
寻找其他同步替代方案:
Sha*_*ghi 12
以下是The Java Tutorials的解释.
请考虑以下代码:
Run Code Online (Sandbox Code Playgroud)public class SynchronizedCounter { private int c = 0; public synchronized void increment() { c++; } public synchronized void decrement() { c--; } public synchronized int value() { return c; } }
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 Methods
和Synchronized 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 lock
该synchronized statement's
对象并将其释放的方法返回时.只要一个线程拥有一个intrinsic lock
,NO其他线程就可以获得SAME lock => thread safe.=>当一个thread A
invokes 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 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
希望它有所帮助
其他答案遗漏的一个重要方面是:内存障碍。线程同步基本上包括两部分:序列化和可见性。我建议每个人都在Google上寻找“ jvm内存屏障”,因为这是一个不重要且极为重要的主题(如果您修改了多个线程访问的共享数据)。完成此操作后,我建议查看java.util.concurrent包的类,这些类有助于避免使用显式同步,这反过来又有助于使程序保持简单高效,甚至可以防止死锁。
这样的例子之一就是ConcurrentLinkedDeque。与命令模式一起,它允许通过将命令填充到并发队列中来创建高效的工作线程-不需要显式同步,不需要死锁,不需要显式sleep(),只需通过调用take()轮询队列即可。
简而言之:当启动线程,线程结束,读取易失性变量,解锁监视器(保留同步的块/功能)等时,“内存同步”会隐式发生。这种“同步”会(在某种意义上说“刷新” “)在该特定操作之前完成的所有写入。对于上述ConcurrentLinkedDeque,文档“说”:
内存一致性影响:与其他并发集合一样,在将对象放入ConcurrentLinkedDeque中之前,线程中的操作发生在访问或从另一个线程中的ConcurrentLinkedDeque中删除该元素之后的操作之前。
这种隐式行为在某种程度上是一个有害的方面,因为大多数没有经验的Java程序员都会因此而付出很多。然后,在Java没有执行工作负载不同的生产中应该做的事情之后,突然在该线程上绊倒了,这很难测试并发问题。
归档时间: |
|
查看次数: |
475138 次 |
最近记录: |