Java 基于静态和实例方法的同步
我对同步实例方法和静态方法感到困惑。 我想编写一个线程安全类,如下所示:Java 基于静态和实例方法的同步,java,thread-safety,Java,Thread Safety,我对同步实例方法和静态方法感到困惑。 我想编写一个线程安全类,如下所示: public class safe { private final static ConcurrentLinkedQueue<Object> objectList= new ConcurrentLinkedQueue<Object>(); /** * retrieves the head of the object and prints it */ pub
public class safe {
private final static ConcurrentLinkedQueue<Object> objectList=
new ConcurrentLinkedQueue<Object>();
/**
* retrieves the head of the object and prints it
*/
public synchronized static void getHeadObject() {
System.out.println(objectList.peek().toString());
}
/**
* creates a new object and stores in the list.
*/
public synchronized void addObject() {
Object obj=new Object();
objectList.add(obj);
}
}
公共类安全{
私有最终静态ConcurrentLinkedQueue对象列表=
新建ConcurrentLinkedQueue();
/**
*检索对象的头部并打印它
*/
公共同步静态void getHeadObject(){
System.out.println(objectList.peek().toString());
}
/**
*创建新对象并存储在列表中。
*/
公共同步的void addObject(){
Object obj=新对象();
objectList.add(obj);
}
}
在静态方法上同步将锁定safe.class lock,在实例方法上同步将锁定safe.class lock,因此将达到不一致的状态
如果我想为下面的代码片段实现一致的状态,那么该如何实现呢?编辑:我假设您的意思是
队列对象列表
,而不是ConcurrentLinkedQueue对象列表
ConcurrentLinkedQueue
已经为您实现了所有线程安全,这意味着您可以调用objectList.peek()
所有您想要的,而不必担心争用条件。如果您正在开发多线程程序,这是非常好的,但对于学习线程安全性来说却不是那么好
假设一次有一个线程在对象的一个实例上运行,那么您的方法不需要进行同步
,但是如果您需要有多个引用相同静态类变量的类实例,则需要对类变量进行同步
,如下所示:
public static void getHeadObject() {
synchronized(safe.objectList) {
System.out.println(objectList.peek().toString());
}
}
这将锁定对象列表
,并且一旦程序进入同步块,就不允许在任何其他线程中读取或写入它。对要同步的所有其他方法执行相同的操作
注意:
但是,由于您只执行一个简单的get操作List.peek()
,因此实际上不需要通过objectList
进行同步,因为在竞争条件下,它将获取列表的一个值或另一个值。竞态条件的问题是当执行多个复杂的读/写操作时,值在它们之间变化
例如,如果您有一个类PairInt
,其中包含PairInt.x
和PairInt.y
字段,约束条件是x=2y
,并且您希望执行此操作
System.out.println(myIntPair.x.toString() + ", " + myIntPair.y.toString());
另一个线程同时更新x
和y
的值
myIntPair.y = y + 3;
myIntPair.x = 2 * y;
在读取线程的myIntPair.x.toString()
和myIntPair.y.toString()
之间修改了写入线程的myIntPair
,您可能会得到一个类似(10,8)
的输出,这意味着如果您在假设x==2*y
的情况下操作,程序可能会崩溃
在这种情况下,您的读取需要使用synchronized
,但对于在队列中添加或删除而未修改的简单对象上更简单的peek()
,在大多数情况下可以删除synchronized
。事实上,对于string
、int
、bool
等,应该删除用于简单读取的同步
条件
但是,对于非显式线程安全的操作,即已经由java处理的操作,写入应该始终是同步的。一旦您获取了多个资源,或者要求您的资源在整个操作过程中保持不变,就像您对其执行多行逻辑一样,那么您必须首先使用同步的,ConcurrentLinkedQueue不需要显式同步。看
其次,您始终可以同步正在访问的对象:
public class safe {
private final static ConcurrentLinkedQueue<Object> objectList=
new ConcurrentLinkedQueue<Object>();
/**
* retrieves the head of the object and prints it
*/
public static void getHeadObject() {
synchronized(objectList){
System.out.println(objectList.peek().toString());
}
}
/**
* creates a new object and stores in the list.
*/
public void addObject() {
Object obj=new Object();
synchronized(objectList){
objectList.add(obj);
}
}
}
公共类安全{
私有最终静态ConcurrentLinkedQueue对象列表=
新建ConcurrentLinkedQueue();
/**
*检索对象的头部并打印它
*/
公共静态void getHeadObject(){
已同步(对象列表){
System.out.println(objectList.peek().toString());
}
}
/**
*创建新对象并存储在列表中。
*/
public void addObject(){
Object obj=新对象();
已同步(对象列表){
objectList.add(obj);
}
}
}
一些评论:
- Java约定:
- 类名应为CamelCase(即调用您的类
Safe
,而不是Safe
)
- 在方法声明中,
static
位于synchronized
之前
静态
在字段声明中位于最终
之前
- 正如其他人已经说过的,
ConcurrentLinkedQueue
已经是线程安全的,因此在您给出的示例中不需要同步
- 将静态方法和非静态方法混合使用的方式看起来很奇怪
- 假设您的实际用例更复杂,并且您需要一个方法来运行原子操作,那么您的代码就不能工作了,正如您所指出的,因为两个同步的方法不在同一个监视器上同步:
为了回答您的具体问题,您可以使用单独的静态对象作为锁:
public class Safe {
private static final ConcurrentLinkedQueue<Object> objectList =
new ConcurrentLinkedQueue<Object>();
// lock must be used to synchronize all the operations on objectList
private static final Object lock = new Object();
/**
* retrieves the head of the object and prints it
*/
public static void getHeadObject() {
synchronized (lock) {
System.out.println(objectList.peek().toString());
}
}
/**
* creates a new object and stores in the list.
*/
public void addObject() {
synchronized (lock) {
Object obj = new Object();
objectList.add(obj);
}
}
}
公共类安全{
私有静态最终ConcurrentLinkedQueue对象列表=
新建ConcurrentLinkedQueue();
//锁必须用于同步objectList上的所有操作
私有静态最终对象锁=新对象();
/**
*检索对象的头部并打印它
*/
公共静态void getHeadObject(){
已同步(锁定){
System.out.println(objectList.peek().toString());
}
}
/**
*创建新对象并存储在列表中。
*/
普布利
public class Safe {
private static final ConcurrentLinkedQueue<Object> objectList =
new ConcurrentLinkedQueue<Object>();
// lock must be used to synchronize all the operations on objectList
private static final Object lock = new Object();
/**
* retrieves the head of the object and prints it
*/
public static void getHeadObject() {
synchronized (lock) {
System.out.println(objectList.peek().toString());
}
}
/**
* creates a new object and stores in the list.
*/
public void addObject() {
synchronized (lock) {
Object obj = new Object();
objectList.add(obj);
}
}
}