在java中同步 – 正确使用

我正在构建一个在多进程(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=0 && p<size()) return _data[p]; else return null; } 

使类对multithreading访问安全是一个复杂的主题。 如果您没有这样做以了解线程,您应该尝试找到一个可以帮助您的库。

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

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

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

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

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

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

我给你的建议是先阅读Oracle的并发教程 。

一些评论:

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

对于所有读过教程的人来说都是+1,但无论如何这里都是摘要。

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

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

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


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

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

 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; } } 

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