我有一些关于同步关键字的用法和意义的问题。
synchronized关键字有什么意义? 什么时候应该同步方法? 它在程序上和逻辑上意味着什么?
我有一些关于同步关键字的用法和意义的问题。
synchronized关键字有什么意义? 什么时候应该同步方法? 它在程序上和逻辑上意味着什么?
当前回答
synchronized关键字是什么?
线程主要通过共享对字段和引用字段所引用的对象的访问进行通信。这种通信形式非常高效,但可能会产生两种错误:线程干扰和内存一致性错误。防止这些错误所需的工具是同步。
同步块或方法可以防止线程干扰,并确保数据是一致的。在任何时候,只有一个线程可以通过获得锁来访问同步块或方法(临界区)。其他线程将等待锁释放来访问临界区。
方法何时同步?
将synchronized添加到方法定义或声明时,方法是同步的。您还可以在方法中同步特定的代码块。
它在语法上和逻辑上意味着什么?
这意味着只有一个线程可以通过获得锁来访问临界区。除非这个线程释放这个锁,否则所有其他线程将不得不等待获得一个锁。他们不能在没有锁的情况下进入临界区。
这不是魔法能办到的。程序员的职责是识别应用程序中的关键部分并相应地保护它。Java提供了一个框架来保护你的应用程序,但是在哪里以及保护哪些部分是程序员的责任。
更多细节,请参阅java文档页
内在锁和同步:
同步是围绕一个称为内在锁或监视器锁的内部实体构建的。内在锁在同步的两个方面都发挥作用:强制对对象状态的独占访问,并建立对可见性至关重要的happens-before关系。
每个对象都有一个与之相关的内在锁。按照惯例,需要独占和一致访问对象字段的线程必须在访问对象字段之前获得对象的内在锁,然后在使用完对象字段时释放内在锁。
线程在获得锁和释放锁之间拥有内在锁。只要一个线程拥有一个内在锁,其他线程就不能获得相同的锁。另一个线程在试图获取锁时将阻塞。
当线程释放一个内在锁时,在该操作和后续获得的任何相同锁之间建立happens-before关系。
使方法同步有两个效果:
首先,对同一对象的同步方法的两次调用不可能交织。
当一个线程正在为一个对象执行同步方法时,所有为同一对象调用同步方法的其他线程将暂停执行,直到第一个线程处理完该对象。
其次,当同步方法退出时,它自动与同一对象的同步方法的任何后续调用建立happens-before关系。
这保证了对对象状态的更改对于所有线程都是可见的。
寻找同步的其他替代方案:
避免在Java中同步(这)?
其他回答
在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
Java同步
volatile[About] => synchronized
synchronized块在Java中是多线程中的监视器。同一对象/类的同步块只能由单个线程执行,其他的都在等待。它可以帮助解决竞赛状况。
Java 5通过支持happens-before扩展了同步[关于]
监视器的解锁(同步块或方法退出)发生在同一监视器的每个后续锁定(同步块或方法进入)之前。
下一步是java.util.concurrent
同步法等效于正常方法 同步语句(使用此语句)
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关键字是关于不同的线程读写相同的变量、对象和资源。这在Java中不是一个微不足道的话题,但这里引用Sun的一段话:
同步方法可以实现简单的 防止线程的策略 干扰和记忆一致性 错误:如果对象是可见的 多于一个线程,全部读取或 写入该对象的变量为 通过同步方法完成。
简而言之:当你有两个线程读写同一个“资源”时,比如说一个名为foo的变量,你需要确保这些线程以原子的方式访问变量。如果没有synchronized关键字,线程1可能看不到线程2对foo所做的更改,或者更糟的是,它可能只改变了一半。这不是你逻辑上所期望的。
同样,这在Java中是一个重要的主题。要了解更多,请在这里探索关于SO和互联网的主题:
并发性 Java内存模型
继续探索这些话题,直到“Brian Goetz”这个名字在你的脑海中与“并发性”这个术语永久地联系在一起。