如何在 Java 中正确同步方法访问
How to correctly synchronize method access in Java
我有一个 class Library
这是一个第三方库,我无法访问源代码。这个class在我项目的不同class中使用,比如
public class MyOwnClass1 {
private Library lib;
public void doTask () {
String res = lib.libMethod1();
... ... ...
}
}
然而,事实证明 class Library
不是 线程安全的 ,例如,当方法 libMethod1
是在不同的线程中同时调用,会导致奇怪的问题。
因此,我必须实现自己的线程安全机制,第一个是将 Library
变量封装到另一个 class.
public class SafeLibrary {
private Library lib;
private Object mutex = new Object();
... ... ...
public String doTask () {
synchronized(this.mutex) {
return this.lib.libMethod1();
}
}
... ... ...
}
不过正如我所说,Library
class是用在不同的方法class。如果我必须把所有相关的方法都放在新的SafeLibrary
class中,那将花费大量的代码修改。
所以这是第二个想法:
public class SafeLibrary {
private Library lib;
private Object mutex = new Object();
public Object getMutex() {
return this.mutex;
}
public Library getLib() {
return this.lib;
}
}
然后我自己同步方法访问class:
public class MyOwnClass1 {
private SafeLibrary lib;
public void doTask () {
synchronized(lib.getMutext()) {
String res = lib.getLib().libMethod1();
... ... ...
}
}
}
采用方案二,我只需要在相关方法上做一些小的修改即可。但是 getMutex()
似乎是一种不正确的方式。
我想知道哪种解决方案是正确的,或者是否有其他更好的解决方案?谢谢。
您有两个选择,您可以同步您的 class 或同步特定方法。您所做的是同步 class。下面是一个同步 class 的例子:
https://docs.oracle.com/javase/tutorial/essential/concurrency/syncrgb.html
下面是一个同步方法的例子:
https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html
基本上只需在 "public" 之后和 return 值之前添加单词 "synchronized"。可以把它想象成将 "final" 添加到方法中。
最佳解决方案将取决于您的软件架构。如果您担心的只是那个方法,并且该方法是您正在创建的对象的特性,那么只需同步该方法即可。如果您要创建其他 objects/threads 需要的独立对象,请同步该对象。
如果库和您要使用的方法不是最终的,并且您自己创建了库对象(而不是从库本身的静态方法中获取它),那么您可以创建自己的 class :
public class SynchronizedLibrary extends Library {
public synchronized String libMethod1() {
super.libMethod1();
}
}
然后你所要做的就是替换构造函数调用,甚至可以将声明的类型保留为普通的旧库(尽管你可能不想这样做)。
我有一个 class Library
这是一个第三方库,我无法访问源代码。这个class在我项目的不同class中使用,比如
public class MyOwnClass1 {
private Library lib;
public void doTask () {
String res = lib.libMethod1();
... ... ...
}
}
然而,事实证明 class Library
不是 线程安全的 ,例如,当方法 libMethod1
是在不同的线程中同时调用,会导致奇怪的问题。
因此,我必须实现自己的线程安全机制,第一个是将 Library
变量封装到另一个 class.
public class SafeLibrary {
private Library lib;
private Object mutex = new Object();
... ... ...
public String doTask () {
synchronized(this.mutex) {
return this.lib.libMethod1();
}
}
... ... ...
}
不过正如我所说,Library
class是用在不同的方法class。如果我必须把所有相关的方法都放在新的SafeLibrary
class中,那将花费大量的代码修改。
所以这是第二个想法:
public class SafeLibrary {
private Library lib;
private Object mutex = new Object();
public Object getMutex() {
return this.mutex;
}
public Library getLib() {
return this.lib;
}
}
然后我自己同步方法访问class:
public class MyOwnClass1 {
private SafeLibrary lib;
public void doTask () {
synchronized(lib.getMutext()) {
String res = lib.getLib().libMethod1();
... ... ...
}
}
}
采用方案二,我只需要在相关方法上做一些小的修改即可。但是 getMutex()
似乎是一种不正确的方式。
我想知道哪种解决方案是正确的,或者是否有其他更好的解决方案?谢谢。
您有两个选择,您可以同步您的 class 或同步特定方法。您所做的是同步 class。下面是一个同步 class 的例子: https://docs.oracle.com/javase/tutorial/essential/concurrency/syncrgb.html
下面是一个同步方法的例子: https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html
基本上只需在 "public" 之后和 return 值之前添加单词 "synchronized"。可以把它想象成将 "final" 添加到方法中。
最佳解决方案将取决于您的软件架构。如果您担心的只是那个方法,并且该方法是您正在创建的对象的特性,那么只需同步该方法即可。如果您要创建其他 objects/threads 需要的独立对象,请同步该对象。
如果库和您要使用的方法不是最终的,并且您自己创建了库对象(而不是从库本身的静态方法中获取它),那么您可以创建自己的 class :
public class SynchronizedLibrary extends Library {
public synchronized String libMethod1() {
super.libMethod1();
}
}
然后你所要做的就是替换构造函数调用,甚至可以将声明的类型保留为普通的旧库(尽管你可能不想这样做)。