简体   繁体   English

Java - 线程安全程序

[英]Java - Thread safe program

Consider the following piece of code:考虑以下代码:

public class Test
    {
        private List<Object> list;

        public Object get(int id)
        {
            return list.get(id);
        }

        public void add(Object el)
        {
            list.add(el);
        }

        public List<Object> getList()
        {
            return list;
        }
    }

I have to make sure that it is thread safe (there will be no synchronization errors).我必须确保它是线程安全的(不会有同步错误)。 I am pretty new to this thing, so my guesses are:我对这件事很陌生,所以我的猜测是:

  1. We add synchronized to the add() and get() methods and we add synchronized(Test.class) to the getList() method;我们将synchronized添加到add()get()方法,并将synchronized(Test.class)添加到getList()方法;

  2. We make list static and all the methods static, then we add synchronized ;我们将list和所有方法设为静态,然后添加synchronized

This is what I came up with, it's possible that neither of them are right though.这是我想出的,但它们可能都不对。

Your point #1 seems valid.您的第 1 点似乎有效。 I would put synchronized on get() and add() and getList() , but I would not put anything static, rather return a copy of the list when getList() is called.我会在get()add()getList()上放置synchronized ,但我不会放置任何静态内容,而是在getList()时返回列表的副本。

Since you do not specify which type, I wrote the snippet with ArrayList as example.由于您没有指定哪种类型,我以ArrayList为例编写了代码段。

  public synchronized List<Object> getList()
  {
      return new ArrayList<Object>(list);
  }

If you want to emphasise good practices, you could return some sort of immutable collection in order to prevent the "user" of this class from editing the list, believing it may affect the state.如果你想强调好的做法,你可以返回某种不可变的集合,以防止此类的“用户”编辑列表,认为它可能会影响状态。

public synchronized List<Object> getList()
{
    return Collections.unmodifiableList(list);
}

synchronize add , get and getList , additionally in getList return UnmodifiableList :同步addgetgetList ,另外在getList返回UnmodifiableList

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

public class Test {
    private List<Object> list = new LinkedList<>();

    public synchronized Object get(int id) {
        return list.get(id);
    }

    public synchronized void add(Object el) {
        list.add(el);
    }

    public synchronized List<Object> getList() {
        return Collections.unmodifiableList(list);
    }
}

You need to also synchronize the getList , because Collections.unmodifiableList uses Iterator of the supplied list internally to copy it, so it could give ConcurrentModificationException if not synchronized with the add method.您还需要同步getList ,因为Collections.unmodifiableList在内部使用提供的列表的Iterator来复制它,因此如果与add方法不synchronized ,它可能会给出ConcurrentModificationException

Using a static modifier means sharing the same list field for all instances of the Test class.使用静态修饰符意味着为 Test 类的所有实例共享相同的list字段。
Consequently, your class should be a singleton and it seems not designed to be it.因此,您的班级应该是一个单身人士,而它似乎并非设计为这样。
So, using static doesn't seem necessarily the best way to address your need.因此,使用静态似乎不一定是满足您需求的最佳方式。

To make your class thread safe, you have to limit the possibility that its state may change concurrently by multiple threads.为了使您的类线程安全,您必须限制多个线程同时更改其状态的可能性。

So this should be changed :所以这应该改变:

    public List<Object> getList()

Otherwise the clients may modify concurrently the List instance that is returned.否则客户端可能会同时修改返回的 List 实例。

You should return a unmodifiable list.您应该返回一个不可修改的列表。

You should also synchronize the methods for adding and removing elements to avoid concurrent add or removal operation.您还应该同步添加和删除元素的方法,以避免并发添加或删除操作。

In this way the class is synchronized :通过这种方式,类是同步的:

public class Test
    {
        private List<Object> list;

        public synchronized Object get(int id)
        {
            return list.get(id);
        }

        public synchronized void add(Object el)
        {
            list.add(el);
        }

        public synchronized List<Object> getList() {
           return Collections.unmodifiableList(list);
        }

    }

Now if you chain call to these methods, you should also synchronize them since between their call, another thread could change the state of the list.现在,如果您将调用链接到这些方法,您还应该同步它们,因为在它们的调用之间,另一个线程可能会更改列表的状态。

For example this code :例如这个代码:

Test test = ...;
int index = ...;
Object myObject = ...;

if (test.get(index) != myObject){
    test.add(myObject);
}

should be written in this way :应该这样写:

Test test = ...;
int index = ...;
Object myObject = ...;

synchronized(test){
  if (test.get(index) != myObject){
      test.add(myObject);
  }
}

Neither of them is completely right.他们都不是完全正确的。

We add synchronized to the add() and get() methods and we add synchronized(Test.class) to the getList() method;我们将 synchronized 添加到 add() 和 get() 方法,并将 synchronized(Test.class) 添加到 getList() 方法;

Add synchronized to add() , get() and getList() .添加同步到add()get()getList() But adding synchronized(Test.class ) to getList() is wrong since you are adding class lock instead of object level lock to access object variable.但是将synchronized(Test.class ) 添加到getList()是错误的,因为您添加的是类锁而不是对象级锁来访问对象变量。

We make list static and all the methods static, then we add synchronized;我们将列表和所有方法设为静态,然后添加同步;

This is required if you need only one list across all instances of Test => class level member variable instead of object level member variable.如果您只需要跨越Test => 类级别成员变量而不是对象级别成员变量的所有实例的一个列表,则这是必需的。 Otherwise, just proceed with instance method level lock instead of class level lock.否则,只需继续使用实例方法级别的锁而不是类级别的锁。 Follow first approach.遵循第一种方法。

For class level variables, use class level locks.对于类级变量,使用类级锁。 For object level variables, use object level locks.对于对象级变量,使用对象级锁。

Refer to oracle pages of Synchronized Methods and Intrinsic Locks and Synchronization for better understanding of concepts.请参阅同步方法内部锁和同步的oracle 页面以更好地理解概念。

Few notes from documentation pages:文档页面中的一些注释:

Object level lock:对象级锁:

Making these methods synchronized has two effects:使这些方法synchronized有两个效果:

  1. First, it is not possible for two invocations of synchronized methods on the same object to interleave.首先,对同一对象的synchronized方法的两次调用不可能交错。 When one thread is executing a synchronized method for an object, all other threads that invoke synchronized methods for the same object block (suspend execution) until the first thread is done with the object.当一个线程正在为一个对象执行synchronized方法时,所有其他调用synchronized一个对象的synchronized方法的线程都会阻塞(挂起执行),直到第一个线程完成对对象的处理。

  2. Second, when a synchronized method exits, it automatically establishes a happens-before relationship with any subsequent invocation of a synchronized method for the same object.其次,当一个synchronized方法退出时,它会自动建立一个发生在同一个对象的synchronized方法的任何后续调用之前的关系。 This guarantees that changes to the state of the object are visible to all threads这保证了对象状态的变化对所有线程都是可见的

Class level lock:类级锁:

You might wonder what happens when a static synchronized method is invoked, since a static method is associated with a class, not an object.您可能想知道调用静态synchronized方法时会发生什么,因为static方法与类相关联,而不是与对象相关联。 In this case, the thread acquires the intrinsic lock for the Class object associated with the class.在这种情况下,线程获取与Class关联的Class对象的内在锁。 Thus access to class's static fields is controlled by a lock that's distinct from the lock for any instance of the class .因此,对类的静态字段的访问由一个锁控制,该锁与该类的任何实例的锁不同

That piece of code is thread safe already.那段代码已经是线程安全的。 Why ?为什么 ? because the global variable is non-static variable.因为全局变量是非静态变量。 All the thread will create it's own variable List<Object> list;所有线程都会创建它自己的变量List<Object> list; Except if you make List<Object> list;除非你制作List<Object> list; to be a static variable.成为静态变量。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM