在java中同步 - 正确使用

时间:2014-06-09 21:01:09

标签: java multithreading synchronized

我正构建一个在多进程(Threads)中使用的简单程序。

我的问题还有更多要理解 - 当我必须同步使用保留字时?

我是否需要在任何影响骨骼变量的方法中使用这个词?

我知道我可以把它放在任何非静态的方法上,但我想了解更多。

谢谢你!

这是代码:

public class Container {
// *** data members ***
public static final int INIT_SIZE=10;  // the first (init) size of the set.
public static final int RESCALE=10;   // the re-scale factor of this set.
private int _sp=0;
public Object[] _data;
/************ Constructors ************/
public Container(){
    _sp=0;
    _data = new Object[INIT_SIZE];
}
public Container(Container other) {  // copy constructor
    this();
    for(int i=0;i<other.size();i++) this.add(other.at(i));
}

/** return true is this collection is empty, else return false. */
public synchronized boolean isEmpty() {return _sp==0;}

/** add an Object to this set */
public synchronized void add (Object p){
    if (_sp==_data.length) rescale(RESCALE);
    _data[_sp] = p;  // shellow copy semantic.
    _sp++;
}   

/** returns the actual amount of Objects contained in this collection */
public synchronized int size() {return _sp;}

/** returns true if this container contains an element which is equals to ob */
public synchronized boolean isMember(Object ob) {
    return get(ob)!=-1;
}

/** return the index of the first object which equals ob, if none returns -1 */
public synchronized int get(Object ob) {
    int ans=-1;
    for(int i=0;i<size();i=i+1)
        if(at(i).equals(ob)) return i;
    return ans;
}

/** returns the element located at the ind place in this container (null if out of range) */
public synchronized Object at(int p){
    if (p>=0 && p<size()) return _data[p];
    else return null;
}

5 个答案:

答案 0 :(得分:5)

您可以查看以下有关同步方法的文档:http://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html

通过添加synchronized关键字,可以确保两件事情发生:

  

首先,对同一对象的两个同步方法的调用不可能进行交错。当一个线程正在为对象执行同步方法时,所有其他线程都会调用同一对象的同步方法阻塞(暂停执行),直到第一个线程完成对象为止。

     

其次,当synchronized方法退出时,它会自动与同一对象的同步方法的任何后续调用建立一个before-before关系。这可以保证对所有线程都可以看到对象状态的更改。

因此,只要您需要保证一次只有一个线程访问您的变量来读取/写入它以避免一致性问题,一种方法是使您的方法同步。

答案 1 :(得分:5)

使类对多线程访问安全是一个复杂的主题。如果您没有这样做以了解线程,您应该尝试找到一个为您执行此操作的库。

话虽如此,一个开始的地方是想象两个单独的线程以交替的方式逐行执行一个方法,看看会出现什么问题。例如,上面写的add()方法容易受到数据破坏。想象一下,thread1和thread2同时或多或少地调用add()。如果thread1运行第2行并且在它到达第3行之前,则thread2运行第2行,然后thread2将覆盖thread1的值。因此,您需要一些方法来防止线程像这样交错。另一方面,isEmpty()方法不需要同步,因为只有一条指令将值与0进行比较。再次,很难让这些东西正确。

答案 2 :(得分:3)

我的建议是先阅读Oracle's concurrency tutorial

一些评论:

  • 让所有方法同步会导致瓶颈
  • 将_data变量公开是一种不好的做法,难以并发编程。
  • 您似乎正在重新实现一个集合,更好地使用现有的Java's concurrent collections
  • 变量名最好不要以_
  • 开头
  • 避免在代码中添加注释,并尝试使用声明性方法名称。

答案 3 :(得分:2)

为所有读过教程的人写了+1,但无论如何这里都是摘要。

只要一个线程可能创建一个其他线程不允许看到的临时情况,您就需要互斥(即synchronized块)。假设您将对象存储在搜索树中。向树添加新对象的方法可能必须重新分配多个对象引用,并且在完成其工作之前,树将处于无效状态。如果允许一个线程搜索树而另一个线程在add()方法中,则search()函数可能返回错误的结果,或者更糟(可能使程序崩溃。)

一种解决方案是synchronize add()方法, search()方法,依赖于树结构的任何其他方法。所有必须在同一个对象上同步(树的根节点将是一个明显的选择)。

Java保证在任何给定时间不能在同一对象上同步一个以上的线程。因此,只有一个线程可以同时查看或更改树的内部,并且在add()方法中创建的临时无效状态将是无害的。


我上面的例子解释了互斥的原则,但它是一种保护搜索树的简单而低效的解决方案。更实用的方法是使用读取器/写入器锁,并且仅在树的有趣部分而不是整体上同步。复杂数据结构的实际同步是一个难题,只要有可能,您应该让其他人为您解决。例如,如果您使用java.util.concurrent中的容器类而不是创建自己的数据结构,那么您可能会为自己节省大量工作(并且可能是整批调试)

答案 4 :(得分:0)

您需要保护构成对象状态的变量。如果在静态方法中使用这些变量,则还必须保护它们。但是,要小心,以下示例是错误的:

private static int stateVariable = 0;
//wrong!!!!
public static synchronized void increment() {
  stateVariable++;
}

public synchronized int getValue() {
  return stateVariable;
}

上面似乎安全,但这些方法在不同的锁上运行。以上或多或少对应于以下内容:

private static int stateVariable = 0;
//wrong!!!!
public static void increment() {
  synchronized (YourClassName.class) {
    stateVariable++;
  }
}

public synchronized int getValue() {
  synchronized (this) {
    return stateVariable;
  }
}

请注意,在混合静态和对象方法时会使用不同的锁。