【发布时间】:2011-08-24 17:34:54
【问题描述】:
我已经编写了一些多线程爱好程序,并且在我之前的(工程/物理)研究中也写过一些,所以我认为自己在同步/线程安全和原语领域拥有高于初学者的知识,什么是普通用户发现对 JMM 和多线程等具有挑战性。
我发现我需要什么并且没有适当的方法将类的实例或静态成员标记为由不同线程共享。想想看,我们有私有/受保护/公共等访问规则以及如何命名 getter/setter 的约定等等。
但是线程呢?如果我想将变量标记为线程共享并让它遵循某些规则怎么办? Volatile/Atomic refs 可能会完成这项工作,但有时您只需要使用互斥锁。而当你必须手动记住使用某些东西时......你会忘记它:) - 在某些时候。
所以我有一个想法,我知道我不是第一个,我还查看了http://checkthread.org/example-threadsafe.html - 他们似乎有一个相当不错的代码分析器,我以后可能会尝试它让我做一些事情我想。
但是回到最初的问题。假设我们需要比消息传递框架更底层的东西,我们需要比原始互斥体更高层次的东西......我们有什么......嗯......什么都没有?
所以基本上,我所做的是一种用于线程的纯 java 超简单框架,它允许您将类成员声明为共享或非共享......嗯,有点:)。
以下是如何使用它的示例:
public class SimClient extends AbstractLooper {
private static final int DEFAULT_HEARTBEAT_TIMEOUT_MILLIS = 2000;
// Accessed by single threads only
private final SocketAddress socketAddress;
private final Parser parser;
private final Callback cb;
private final Heart heart;
private boolean lookingForFirstMsg = true;
private BufferedInputStream is;
// May be accessed by several threads (T*)
private final Shared<AllThreadsVars> shared = new Shared<>(new AllThreadsVars());
.
.
.
.
static class AllThreadsVars {
public boolean connected = false;
public Socket socket = new Socket();
public BufferedOutputStream os = null;
public long lastMessageAt = 0;
}
要访问标记为线程共享的变量,您必须向共享对象发送一个类似可运行的函子:
public final void transmit(final byte[] data) {
shared.run(new SharedRunnable<AllThreadsVars, Object, Object>() {
@Override
public Object run(final AllThreadsVars sharedVariable, final Object input) {
try {
if (sharedVariable.socket.isConnected() && sharedVariable.os != null) {
sharedVariable.os.write(data);
sharedVariable.os.flush();
}
} catch (final Exception e) { // Disconnected
setLastMessageAt(0);
}
return null;
}
}, null);
}
共享可运行对象的定义如下:
public interface SharedRunnable<SHARED_TYPE, INPUT, OUTPUT> {
OUTPUT run(final SHARED_TYPE s, final INPUT input);
}
这是要去哪里? 好吧,这给了我帮助(是的,您可以泄漏并破坏它,但可能性很小)我可以将变量集(不仅仅是变量)标记为线程共享,一旦完成,在编译时保证它(我不能忘记同步一些方法)。它还允许我在编译时标准化和执行测试以查找可能的死锁(虽然 atm 我只在运行时实现它,因为在编译时使用上述框架执行它可能需要的不仅仅是 java 编译器)。
基本上这对我非常有用,我想知道我是否只是在这里重新发明轮子,或者这可能是我不知道的一些反模式。而且我真的不知道该问谁。 (哦,是的,Shared.run(SharedRunnable r, INPUT input) 就像
private final <OUTPUT, INPUT> OUTPUT run(final SharedRunnable<SHARED_TYPE, INPUT, OUTPUT> r, final INPUT input) {
try {
lock.lock();
return r.run(sharedVariable, input);
} finally {
lock.unlock();
}
}
这只是我自己的实验,所以它还没有真正完成,但我现在有一个不错的项目正在使用它,它真的很有帮助。
【问题讨论】:
-
我不认为我理解您的示例,但 共享变量 正是您在处理并发时不想拥有的东西。带锁的共享变量会让你移动得更慢,浪费更多时间等待锁,通常反应迟钝并陷入死锁。实现真正并发的最佳方式是完全没有共享状态。
-
是的,但是在您的应用程序中的某些时候,如果它需要多个线程,它将需要一些共享变量,无论是消息队列还是其他东西。我的想法是,有一种方法可以构造任意的类似原子的操作(重要的是:在共享标记状态的编译时强制使用它们)。我也可以毫不费力地为此编写一个死锁分析工具,因为我所有的线程都使用标准化的框架。我已经写了一个相当简化的变体,并且可以在我的代码中检测到一些潜在的死锁位置。
-
就个人而言,我会先使用 java.util.concurrent 包中提供的内容,然后再诉诸于此。
-
如果我能在那里找到有用的东西,绝对是:)。到目前为止,我还没有能够为这个特定的应用程序,但也许我会。不过,我将它们用于许多其他目的。
标签: java multithreading thread-safety