【问题标题】:Java: Framework for thread shared dataJava:线程共享数据的框架
【发布时间】: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


【解决方案1】:

你的意思是this? (可以通过findbugs 等工具强制执行。)

【讨论】:

  • 是的,类似的,谢谢。使用我自己的我不需要代码分析工具来完成它,编译器就足够了,虽然 net.jcip.annotations 看起来很不错:)。在我决定是使用它还是我自己的代码之前,我必须阅读它的合同是如何工作的。
【解决方案2】:

如果您有应该共享的值,最好的方法是将其封装在类中。这样,调用者确实需要知道您使用的是什么线程模型。如果您想知道内部使用的是什么模型,您可以阅读源代码,但是调用者不能忘记正确访问 ConcurrentMap(例如),因为它的所有方法都是线程安全的。

【讨论】:

  • 问题是有时需要同步完成的事情比从预定义的同步集合中添加/删除更多的事情。假设您有地图、旗帜和时间戳。 public void foo() { if (flag == ...) { modify(map);设置时间戳();标志 = 新值;我的想法是将所有这些共享变量和整个操作封装为同步的。并强制同步,所以我不会忘记这样做,即使在封装类中也是如此。同步方法是不够的,你可以忘记。
  • 另外,如果你实现封装类的新行为,或者扩展它,很容易忘记保持线程安全行为,除非它在编译时以某种方式强制执行。我并不是说我的解决方案是最好的,远非如此,但鉴于它(参见 jtahlborn 和 checkthread)以前以非常相似的方式完成,我认为这不是完全错误的。不过,我可能会尝试重新设计我的应用程序,这样我就不需要这些东西了。毕竟消息传递可能更好。感谢大家的所有意见。我现在需要弄清楚如何结束这个问题:)。
  • 如果您有一个组件使用许多其他组件,这些组件都必须以线程安全的方式进行,您仍然可以使用封装来集中管理所有同步需求。这简化了代码的维护。暴露你的锁定更强大但更危险,这就是为什么许多并发库故意隐藏这种锁定。
猜你喜欢
  • 2011-07-08
  • 1970-01-01
  • 2012-06-28
  • 1970-01-01
  • 1970-01-01
  • 2018-11-01
  • 1970-01-01
  • 2014-10-17
  • 2011-08-29
相关资源
最近更新 更多