我有一个班级
public class ThreadExample extends Thread{
static int count = 0;
public static synchronized int increment(){
return count++;
}
public synchronized int decrement(){
return count--;
}
}
这里我有一个静态方法和一个非静态方法。
第一个thread1调用了方法increment(),它是synchronized.It在类级别上获取锁定。 这里我的问题是,如果另一个thread2正在调用decrement()方法,那么thread2将获得对decrement()的锁定以及它是如何工作的?
答案 0 :(得分:3)
synchronized
关键字有两种可能的用途。它可以用作方法的修饰符,它可以用作语句。此外,synchronized
修饰符可以与static
结合使用,在这种情况下,目标对象将是封闭的类,而不是封闭的实例
Scope | modifiers | corresponding statement
---------+---------------------+------------------------
static | synchronized static | synchronized (X.class)
instance | synchronized | synchronized (this)
如果方法为static synchronized
,则在class
的情况下,在封闭式class
的{{1}}对象上获取锁定。
虽然它们被编译成不同的字节代码,但以下两种方法是等效的:
ThreadExample.class
如果方法是public class Foo {
// static method with synchronized modifer
public static synchronized void foo1() {
// ...
}
// equivalent synchronized statement
public static void foo2() {
synchronized (Foo.class) {
// ...
}
}
}
(没有静态),则在实例本身上获取锁。虽然它们被编译成不同的字节代码,但以下两种方法是等效的:
synchronized
因此,public class Foo {
// instance method with synchronized modifier
public synchronized void foo3() {
// ...
}
// equivalent synchronized statement
public void foo4() {
synchronized (this) {
// ...
}
}
}
和increment()
的{{1}}不同,并且可能存在竞争条件。
因此,变量decrement()
未得到充分保护,不会受到并发更新的影响。
synchronized
和count
本身不能是原子的,因为递增或递减值需要读取 - 更新 - 写入周期。从技术上讲,它可能是原子的,因为一些CPU通过提供相应的指令为其提供原子性,这些指令将保持为自己获得的总线/地址,直到执行操作为止。但是JVM并不依赖于这些东西。
如果您需要 atomic int ,您可能需要查看++
。
--
使用VM环境方法java.util.concurrent.atomic.AtomicInteger
和synchronized
实现 synchronized
。
当您使用MonitorEnter()
修饰符时,您可以同步一些或多或少MonitorExit()
的内容,即对其他对象和类也可见。 synchronized
的监视器功能为public
java.lang.Object
提供基础设施,以及原生函数synchronized
/ public
}和等待池方法MonitorEnter()
,MonitorExit()
和wait()
。如果“其他人”也使用“您的对象/您的类”进行同步,这可能会导致意外错误和死锁。
因此,它已成为实际上不使用notify()
修饰符而是在notifyAll()
锁定对象上使用synchronized
语句的模式,如下所示:
synchronized
现在private
不再被其他人同步干扰或阻止。你可能认为可能有一个合理的用例,但我认为如果你有一个用于锁定对象/类边界的用例,那么设计中可能存在一个很大的缺陷 - 事情并不是自足的。
如果您需要类锁而不是实例锁,只需将变量设为静态。
请注意,在进行序列化时,您必须处理锁定对象。最简单的方法是实际上不使用Object,但是:
public class Foo {
private final Object lock = new Object();
public void foo() {
synchronized (lock) {
// ...
}
}
}
如果要保存序列化存储,可以声明锁Foo
并在反序列化期间重新创建锁定,但要小心public class Lock implements Serializable {}
,您需要反思或transient
对他们而言,但这是一个不同的故事。
答案 1 :(得分:1)
调用同步静态方法会尝试获取类对象的锁定。 (在您的情况下为ThreadExample
),而调用同步非静态方法则尝试获取特定实例对象的锁。所以基本上你获得了2个不同的锁,因此你的代码不是线程安全的。由于竞争条件,数据count
可能已损坏
答案 2 :(得分:1)
Oracle教程:
您可能想知道静态同步方法会发生什么 调用,因为静态方法与类关联,而不是 宾语。在这种情况下,线程获取内部锁 与类关联的类对象。因此可以访问类的静态 字段由锁定控制,该锁定与任何锁定不同 班级的实例。
答案 3 :(得分:0)
静态和实例级别锁是两个不同的锁,它们彼此独立。
关于有问题的场景“第一个thread1调用了方法increment(),它是synchronized.It获取类级别的锁。这里我的问题是,如果另一个thread2正在调用decrement()方法,那么thread2将获得锁定递减()及其工作方式“
即使thread1持有(类级别)锁定增量方法而线程2调用递减方法,它也应该获取(实例级别)锁定。
问题不在于在尝试更新共享变量时可能会启动锁定问题。如果两个线程同时尝试访问count变量(一个通过静态锁定,另一个通过实例级别锁定),那么那里可能是两个线程之间的 RACE 条件。
正如 Christian 所建议的那样,你可以通过使用 java.util.concurrent.atomic.AtomicInteger 代替 int
来解决这个问题。有关详细信息,请访问URL
答案 4 :(得分:0)
静态同步方法和非静态同步方法不会相互阻塞。原因是Class实例上的静态方法锁定,而非静态方法锁定此实例 - 两个操作都不会相互干扰。
在您的代码中,一个方法是静态同步的,另一个方法是同步的。您的代码不是线程安全的,即使两个线程在同一个对象上执行两个方法,一个线程(执行非静态方法)获取对象级别锁定并继续。执行静态方法的另一个线程获取类级锁(即ThreadExample.class)并继续。请记住,静态方法总是类级别。
要使代码线程安全,您需要在示例中进行以下更改。
public static synchronized int decrement(){
return count--;
}