为什么synchronize关键字仅限于非原始字段?

问题描述 投票:0回答:6

在java中,有一个

synchronize
关键字,用于避免在某个时刻多线程访问一个方法或某个代码块。

我的问题是为什么

synchronize
关键字不能与原始变量一起使用?我知道java不允许这样做。但是为什么不允许这样做的逻辑是什么,因为java中的所有变量都是
Object
,包括像
int
float
等基本类型

例如

int a=1;
synchronized(a){}//why is this forbidden 
java synchronized
6个回答
2
投票

变量本身并不独立。它需要一个类来提供一些上下文。这就是为什么锁位于类或对象上而不是变量上。


1
投票

我想通过变量,你指的是类字段。但这不是 Java 中同步的工作原理。

synchronized
块或方法的目的是防止多个线程同时更改类变量的状态。
Java
中的同步通过拥有锁来工作 - 如果您有同步方法,则您正在使用对象的内部锁,否则您必须提供锁。如果将字段声明为
synchronized
,那么或多或少会强制该类中使用该字段的所有方法通过拥有锁以同步方式工作,这会降低性能。

如果您正在寻找字段级别的同步保证而不必同步方法,请考虑使用原子变量,例如

java.util.concurrent.atomic.AtomicInteger


0
投票

您也可以同步变量。

synchronized(myvariable) {
.......
}

您还应该检查“易失性”关键字。 http://en.wikipedia.org/wiki/Volatile_variable#In_Java


0
投票

你的意思是说你需要同步变量上的操作?就像您想保护变量的操作一样,例如递增、递减 ? java 有原子 API,如

java.util.concurrent.AtomicBoolean
等..

使用原子并发类的好处是,我们不需要担心处理整数的每个地方的同步,并且假设它比涉及锁定资源的同步更有效。


0
投票

使用引用类型对象作为

synchronized(..)
参数的限制来自封装的思想,当您且只有您控制谁以及如何访问您的
lock
(引用或值)时。

想象一下这样的情况:当您想要锁定值类型(原始)锁

int x = 5
时,然后在
synchronized
内部阻塞
int y = 0
上的另一个锁。同时,项目的其他部分(或第 3 方库)也以相反的顺序获取这些锁 - 首先
int a = 0
,然后
int b = 5

结果?僵局。为什么?由于缺乏封装 - 您无法控制值类型(原始)变量的可见性。它们本质上总是公开的。


0
投票

简单(也是真实)的原因是锁需要额外的存储空间,而原始类型没有用于这些锁位的“空间”,因为它们就是它们本身,未装箱的类型。像

java.lang.Integer
等盒装版本确实有空间放置锁,所以他们有它们。

人们可能还记得这里大多数原始访问(意味着读取和写入)在Java中保证是原子的,直到“32位”,因此除了

long
double
之外,访问其中可能被实现为两个操作,因此不保证原子性。除了(而且)由于有点复杂的Java内存模型,即使32位写入是原子的(所以你看不到这些不一致的半字),更新可能不会立即传播到其他线程! OTOH,一旦您创建了一些原始类型volatile
,就可以保证其他线程将“尽快”看到更改
并且即使对于 64 位数量,它也将是(线程间)原子的。因此,就读取和写入而言(设置和获取)
java.util.concurrent.AtomicLong
仅声明一个
volatile long
(并且在其
synchronized
set
周围不使用
get
)。

一个答案(已经)提到了

java.util.concurrent
,但这不仅仅是提供原子读取和写入,例如
Booleans
,但也提供更复杂的原子操作(在这些操作上),例如
compareAndSet
。但对于 64 位类型,
java.util.concurrent
还提供原子读取和写入,尽管是简单的
volatile
方式。

© www.soinside.com 2019 - 2024. All rights reserved.