Java:线程共享数据的框架
我已经编写了一些多线程爱好程序,并且在我之前的(工程/物理)研究中也编写了一些程序,因此我认为自己在同步/线程安全和原语领域拥有高于初学者的知识,这是普通用户发现的 。
我发现我需要什么,并且没有正确的方法将类的实例或静态成员标记为由不同线程共享 想想看,我们有诸如 private/protected/public 之类的访问规则,以及如何命名 getter/setter 的约定以及很多东西。
但是线程呢?如果我想将变量标记为线程共享并让它遵循某些规则怎么办?易失性/原子引用可能可以完成这项工作,但有时您确实需要使用互斥体。当你手动必须记住使用某些东西时......你会忘记它:) - 在某些时候。
所以我有了一个想法,我发现我不是第一个,我还查看了 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;
}
要访问标记为线程共享的变量,您必须向共享对象发送一个类似 runnable 的函子:
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);
}
共享 runnable 的定义如下:
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();
}
}
这只是我自己的实验,所以它还没有真正完成不管怎样,但我现在有一个不错的项目正在使用它,它确实帮了很多忙。
I've written a few multithreaded hobby programs and some in my previous(engineering/physics) studies as well, so I consider myself to have an above-beginner knowledge in the area of synchronization/thread safety and primitives, what the average user finds to be challanging with the JMM and multiple threads etc.
What I find that I need and there is no proper method of marking instance or static members of classes as shared by different threads. Think about it, we have access rules such as private/protected/public and conventions on how to name getters/setters and a lot of things.
But what about threading? What if I want to mark a variable as thread shared and have it follow certain rules? Volatile/Atomic refs might do the job, but sometimes you just do need to use mutexes. And when you manually have to remember to use something...you will forget about it :) - At some point.
So I had an idea, and I see I am not the first, I also checked out http://checkthread.org/example-threadsafe.html - They seem to have a pretty decent code analyzer which I might try later which sort of lets me do some of the things I want.
But coming back to the initial problem. Let's say we need something a little more low level than a message passing framework and we need something a little more high level than primitive mutexes... What do we have...wel...nothing?
So basically, what I've made is a sort of pure java super-simple framework for threading that lets you declare class members as shared or non-shared...well sort of :).
Below is an example of how it could be used:
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;
}
And to access the variables marked as thread shared you must send a runnable-like functor to the Shared object:
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);
}
Where a shared runnable is defined like:
public interface SharedRunnable<SHARED_TYPE, INPUT, OUTPUT> {
OUTPUT run(final SHARED_TYPE s, final INPUT input);
}
Where is this going?
Well this gives me the help (yes you can leak out and break it but far less likely) that I can mark variable sets (not just variables) as thread shared, and once that is done, have it guaranteed in compile time ( I cannot forget to synchronize some method). It also allows me to standardize and perform tests to look for possible deadlocks also in compile time (Though atm I only implemented it in runtime cause doing it in compile time with the above framework will probably require more than just the java compiler).
Basically this is extremely useful to me and I'm wondering if I'm just reinventing the wheel here or of this could be some anti-pattern I don't know of. And I really don't know who to ask. (Oh yeah and Shared.run(SharedRunnable r, INPUT input) works just like
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();
}
}
This is just my own experimentation so it's not really finished by any means, but I have one decent project using it right now and it's really helping out a lot.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
你的意思是像这个? (可以通过 findbugs 等工具强制执行。)
You mean something like this? (which can be enforced by tools like findbugs.)
如果您有应该共享的值,最好的方法是将其封装在类中。这样调用者就不需要知道你正在使用什么线程模型。如果你想知道内部使用什么模型,你可以阅读源代码,但是调用者不能忘记正确访问ConcurrentMap(例如),因为它的所有方法都是线程安全的。
If you have values which should be shared, the best approach is encapsulate this within the class. This way the caller does need to know what thread model you are using. If you want to know what model is used internally, you can read the source, however the caller cannot forget to access a ConcurrentMap (for example) correctly because all its method are thread safe.