在线程之间共享资源,在不同的Java版本中使用不同的行为
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在线程之间共享资源,在不同的Java版本中使用不同的行为相关的知识,希望对你有一定的参考价值。
这是我第一次遇到类似下面的内容。
- 多个线程(实现Runnable的内部类)共享数据结构(上层的实例变量)。
- 工作:从Eclipse项目bin文件夹中取出类,在Unix机器上运行。
- 不工作:直接在Unix机器上编译src并使用那些类文件。代码编译然后运行时没有错误/警告,但是一个线程无法正确访问共享资源。
- 问题:一个线程将元素添加到上面的公共DS中。第二个线程执行以下操作...
while(true){ if(myArrayList.size() > 0){ //do stuff }
} - 日志显示大小在线程1中更新。
- 出于某些神秘的原因,工作流程没有输入if()...
如果我直接粘贴Eclipse的bin文件夹中的类文件,则完全相同的代码运行完美。
如果我错过任何明显的事,我道歉。
码:
ArrayList<CSRequest> newCSRequests = new ArrayList<CSRequest>();
//线程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();
}
}
}
}
....
}
更新非常感谢@maasg ...
解决方法是在检查线程2中的大小之前添加synchronized(myArrayList)。
要在多线程环境中访问共享结构,您应该使用隐式或显式锁定来确保线程之间的安全发布和访问。使用上面的代码,它应该如下所示:
while(true){
synchronized (myArrayList) {
if(myArrayList.size() > 0){
//do stuff
}
}
//sleep(...) // outside the lock!
}
注意:此模式看起来很像producer-consumer,使用队列可以更好地实现。 LinkedBlockingQueue是一个很好的选择,并提供内置的并发控制功能。它是在线程之间安全发布数据的良好结构。使用并发数据结构可以摆脱synchronized块:
Queue queue = new LinkedBlockingQueue(...)
...
while(true){
Data data = queue.take(); // this will wait until there's data in the queue
doStuff(data);
}
每当你在shared variable
(一个多个线程并行运行的区域)内修改给定的parallel region
时,你必须确保mutual exclusion
。您可以使用mutual exclusion
或synchronized
保证Java中的locks
,通常在需要更精细的粒度同步时使用锁定。
如果程序只对性能读取给定的共享变量,则不需要同步/锁定对此变量的访问。
由于你是这个主题的新手,我推荐你这个tutorial
如果我做对了..至少有2个线程可以使用相同的共享数据结构。您提到的数组。如果数组的大小> 0,则一个线程向数组添加值,第二个线程“填充”。线程调度程序有可能运行第二个线程(检查集合是否> 0),在第一个线程有机会运行并添加一个值之前。从bin运行类或重新编译它们无关。如果您要从bin目录重新运行该应用程序,您可能会再次看到该问题。你有多少次运行应用程序?它可能无法一致地重现,但有一次你可能会再次看到这个问题。
您可以以串行方式访问数据表,一次只允许一个线程访问该数组。仍然不保证第一个线程将运行,然后第二个线程将检查大小是否> 0。
根据您需要完成的工作,可能有更好的/其他方法来实现这一目标。不一定使用数组来协调线程..
检查返回
newCSRequests.add((CSRequest) message);
我猜它可能由于某种原因没有添加。如果它是HashSet或类似的,可能是因为多个对象的哈希码返回相同的值。什么是消息对象的等于实现?
你也可以用
List list = Collections.synchronizedList(new ArrayList(...));
确保arraylist始终正确同步。
HTH
以上是关于在线程之间共享资源,在不同的Java版本中使用不同的行为的主要内容,如果未能解决你的问题,请参考以下文章