java 线程间共享资源,不同java版本的行为不同
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13082037/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Sharing a resource among Threads, different behavior in different java versions
提问by phanin
This is the first time I've encountered something like below.
这是我第一次遇到类似下面的情况。
Multiple Threads (Inner classes implementing Runnable) sharing a Data Structure (instance variable of the upper class).
Working: took classes from Eclipse project's bin folder, ran on a Unix machine.
NOT WORKING: directly compiled the src on Unix machine and used those class files. Code compiles and then runs with no errors/warnings, but one thread is not able to access shared resource properly.
PROBLEM: One thread adds elements to the above common DS. Second thread does the following...
while(true){ if(myArrayList.size() > 0){ //do stuff }
}
The Log shows that the size is updated in Thread 1.
For some mystic reason, the workflow is not enetering if() ...
多个线程(实现 Runnable 的内部类)共享一个数据结构(上层类的实例变量)。
工作:从 Eclipse 项目的 bin 文件夹中获取类,在 Unix 机器上运行。
不工作:直接在 Unix 机器上编译 src 并使用这些类文件。代码编译后运行,没有错误/警告,但一个线程无法正确访问共享资源。
问题:一个线程向上述常见 DS 添加元素。第二个线程执行以下操作...
while(true){ if(myArrayList.size() > 0){ //do stuff }
}
日志显示大小在线程 1 中更新。
由于一些神秘的原因,工作流程没有进入 if() ...
Same exact code runs perfectly if I directly paste the class files from Eclipse's bin folder.
如果我直接粘贴 Eclipse 的 bin 文件夹中的类文件,则完全相同的代码可以完美运行。
I apologize if I missed anything obvious.
如果我错过了任何明显的东西,我深表歉意。
Code:
代码:
ArrayList<CSRequest> newCSRequests = new ArrayList<CSRequest>();
//Thread 1
//线程1
private class ListeningSocketThread implements Runnable {
ServerSocket listeningSocket;
public void run() {
try {
LogUtil.log("Initiating...");
init(); // creates socket
processIncomongMessages();
listeningSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private void processIncomongMessages() throws IOException {
while (true) {
try {
processMessage(listeningSocket.accept());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
private void processMessage(Socket s) throws IOException, ClassNotFoundException {
// read message
ObjectInputStream ois = new ObjectInputStream(s.getInputStream());
Object message = ois.readObject();
LogUtil.log("adding...: before size: " + newCSRequests.size());
synchronized (newCSRequests) {
newCSRequests.add((CSRequest) message);
}
LogUtil.log("adding...: after size: " + newCSRequests.size()); // YES, THE SIZE IS UPDATED TO > 0
//closing....
}
........
}
}
//Thread 2
private class CSRequestResponder implements Runnable {
public void run() {
LogUtil.log("Initiating..."); // REACHES..
while (true) {
// LogUtil.log("inside while..."); // IF NOT COMMENTED, FLOODS THE CONSOLE WITH THIS MSG...
if (newCSRequests.size() > 0) { // DOES NOT PASS
LogUtil.log("inside if size > 0..."); // NEVER REACHES....
try {
handleNewCSRequests();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
....
}
UPDATEThanks a lot @maasg ...
更新非常感谢@maasg ...
Solution was to add synchronized(myArrayList) before I check the size in the Thread 2.
解决方案是在检查线程 2 中的大小之前添加 synchronized(myArrayList)。
回答by maasg
To access a shared structure in a multi-threaded environment, you should use implicit or explicit locking to ensure safe publication and access among threads. Using the code above, it should look like this:
要在多线程环境中访问共享结构,您应该使用隐式或显式锁定来确保线程间的安全发布和访问。使用上面的代码,它应该是这样的:
while(true){
synchronized (myArrayList) {
if(myArrayList.size() > 0){
//do stuff
}
}
//sleep(...) // outside the lock!
}
Note: This pattern looks much like a producer-consumerand is better implemented using a queue. LinkedBlockingQueueis a good option for that and provides built-in concurrency control capabilities. It's a good structure for safe publishing of data among threads. Using a concurrent data structure lets you get rid of the synchronized block:
注意:这种模式看起来很像生产者-消费者,最好使用队列来实现。LinkedBlockingQueue是一个很好的选择,它提供了内置的并发控制功能。这是在线程之间安全发布数据的好结构。使用并发数据结构可以摆脱同步块:
Queue queue = new LinkedBlockingQueue(...)
...
while(true){
Data data = queue.take(); // this will wait until there's data in the queue
doStuff(data);
}
回答by dreamcrash
Every time you modify a given shared variable
inside a parallel region
(a region with multiple threads running in parallel) you must ensure mutual exclusion
. You can guarantee mutual exclusion
in Java by using synchronized
or locks
, normally you use locks when you want a finer grain synchronization.
每次修改 a (具有多个并行运行的线程的区域)shared variable
内的给定时parallel region
,您必须确保mutual exclusion
. 您可以mutual exclusion
在 Java 中使用synchronized
或来保证locks
,通常在需要更细粒度的同步时使用锁。
If the program only performance reads on a given shared variable there is no need for synchronized/lock the accesses to this variable.
如果程序仅对给定的共享变量进行性能读取,则不需要同步/锁定对该变量的访问。
Since you are new in this subject I recommend you this tutorial
由于您是该主题的新手,我向您推荐本教程
回答by drone.ah
Check the return of
检查返回
newCSRequests.add((CSRequest) message);
I am guessing its possible that it didn't get added for some reason. If it was a HashSet or similar, it could have been because the hashcode for multiple objects return the same value. What is the equals implementation of the message object?
我猜它可能由于某种原因没有被添加。如果它是一个 HashSet 或类似的,那可能是因为多个对象的哈希码返回相同的值。消息对象的equals实现是什么?
You could also use
你也可以使用
List list = Collections.synchronizedList(new ArrayList(...));
to ensure the arraylist is always synchronised correctly.
以确保 arraylist 始终正确同步。
HTH
HTH
回答by jumping-Hyman
If I got this right.. There are at least 2 threads that work with the same, shared, datastructure. The array you mentioned.. One thread adds values to the array and the second thread "does stuff" if the size of the array > 0. There is a chance that the thread scheduler ran the second thread (that checks if the collection is > 0), before the first thread got a chance to run and add a value. Running the classes from bin or recompiling them has nothing to do. If you were to run the application over again from the bin directory, you might seen the issue again. How many times did you ran the app? It might not reproduce consistently but at one point you might see the issue again.
如果我做对了……至少有 2 个线程使用相同的共享数据结构。你提到的数组..一个线程向数组添加值,如果数组的大小> 0,第二个线程“做事”。线程调度程序有可能运行第二个线程(检查集合是否> 0),在第一个线程有机会运行并添加值之前。从 bin 运行类或重新编译它们无关。如果您要从 bin 目录再次运行该应用程序,您可能会再次看到该问题。你运行了多少次应用程序?它可能不会始终如一地重现,但在某一时刻您可能会再次看到该问题。
You could access the datastruce in a serial fashion, allowing only one thread at a time to access the array. Still that does not guarantee that the first thread will run and only then the second one will check if the size > 0.
您可以以串行方式访问数据结构,一次只允许一个线程访问数组。这仍然不能保证第一个线程会运行,只有第二个线程才会检查大小是否> 0。
Depending on what you need to accomplish, there might be better / other ways to achieve that. Not necessarily using a array to coordinate the threads..
根据您需要完成的工作,可能有更好的/其他方法来实现这一目标。不一定使用数组来协调线程..