代码之家  ›  专栏  ›  技术社区  ›  CPerkins

将ThreadLocal与包含静态成员的现有类一起使用

  •  3
  • CPerkins  · 技术社区  · 15 年前

    我想用 ThreadLocal 为预先存在的非线程安全类提供线程安全,但遇到问题。似乎没有执行隔离——线程仍然共享静态,而不是每个线程的本地。

    我相信我的用法几乎与 SimpleDateFormatter 描述于 this StackOverflow question 但这并不是我希望的那样。

    我所希望的是,有人使用了这个工具,他会指出我必须犯的极其愚蠢的错误…所以我想我的问题是:你能发现我在这里做错了什么吗?

    这是我的简单课程:

    public class SimpleClassWithStaticMembers {
        private static String theStaticString =
            "StaticStringInClassWithStaticMember";
        public void setTheStaticString (String val) {
            SimpleClassWithStaticMembers.theStaticString = val; 
        }
        public String getTheStaticString () {
            return SimpleClassWithStaticMembers.theStaticString;
        }
    }
    

    这是线程类,它创建 SimpleClassWithStaticMembers :

    public class SimpleTesterThread extends Thread {
        private void showMsg (String msg) {  
            System.out.println (msg); 
            System.out.flush(); 
        }
        public SimpleTesterThread (String threadId) {
            super(threadId);
        }
        public void run() {
            try { Thread.sleep(2000); } catch (InterruptedException ex) { }
            ThreadLocal<SimpleClassWithStaticMembers> localizedClass =
                new ThreadLocal<SimpleClassWithStaticMembers>();
            localizedClass.set(new SimpleClassWithStaticMembers());
            // repeating here to be sure we overlap all with all
            for (int ii=0; ii < 3; ii++) { 
                localizedClass.get().setTheStaticString ("Setby_" + this.getName());
                try { Thread.sleep(2000); } catch (InterruptedException ex) { }
                showMsg("            Thread [" + this.getName() + "] - " 
                    + localizedClass.get().getTheStaticString() + "'.");
            }
            showMsg ("Thread [" + this.getName() 
                + "] complete. Our 'threadlocal' string is now - " 
                + localizedClass.get().getTheStaticString() + "'.");
            localizedClass.remove();
        }
    }
    

    SimpleTesterThread 创建(为它们提供不同的线程名称,如“aaaaaaa”、“bbbbbbb”等),然后启动,输出清楚地显示它们共享实例。日志输出包括:

    ...
                Thread [JJJJJJJJJJ] - Setby_CCCCCCCCCC'.
                Thread [DDDDDDDDDD] - Setby_JJJJJJJJJJ'.
                Thread [IIIIIIIIII] - Setby_DDDDDDDDDD'.
                Thread [GGGGGGGGGG] - Setby_IIIIIIIIII'.
                Thread [EEEEEEEEEE] - Setby_GGGGGGGGGG'.
    Thread [EEEEEEEEEE] complete. Our 'threadlocal' string is now - Setby_GGGGGGGGGG'.
                Thread [HHHHHHHHHH] - Setby_GGGGGGGGGG'.
                Thread [BBBBBBBBBB] - Setby_GGGGGGGGGG'.
                Thread [FFFFFFFFFF] - Setby_GGGGGGGGGG'.
    Thread [FFFFFFFFFF] complete. Our 'threadlocal' string is now - Setby_GGGGGGGGGG'.
    ...
                Thread [JJJJJJJJJJ] - Setby_GGGGGGGGGG'.
    Thread [JJJJJJJJJJ] complete. Our 'threadlocal' string is now - Setby_GGGGGGGGGG'.
    Thread [HHHHHHHHHH] complete. Our 'threadlocal' string is now - Setby_GGGGGGGGGG'.
                Thread [GGGGGGGGGG] - Setby_GGGGGGGGGG'.
    Thread [GGGGGGGGGG] complete. Our 'threadlocal' string is now - Setby_GGGGGGGGGG'.
                Thread [IIIIIIIIII] - Setby_GGGGGGGGGG'.
                Thread [CCCCCCCCCC] - Setby_GGGGGGGGGG'.
    Thread [CCCCCCCCCC] complete. Our 'threadlocal' string is now - Setby_GGGGGGGGGG'.
    Thread [AAAAAAAAAA] complete. Our 'threadlocal' string is now - Setby_GGGGGGGGGG'.
                Thread [DDDDDDDDDD] - Setby_GGGGGGGGGG'.
    Thread [DDDDDDDDDD] complete. Our 'threadlocal' string is now - Setby_GGGGGGGGGG'.
    Thread [IIIIIIIIII] complete. Our 'threadlocal' string is now - Setby_GGGGGGGGGG'.
    ============== all threads complete.
    

    我还没有包括创建、启动和连接线程的类——如果觉得这有帮助,我很乐意编辑来添加它。

    2 回复  |  直到 15 年前
        1
  •  5
  •   Tom Hawtin - tackline    15 年前

    拥有单独的实例没有帮助,因为所有实例都是相同的静态字段。可变静态是邪恶的。

    如果您真的不能更改类,那么您可能只想使用一个锁,这样每个客户机就可以一次使用一个静态字段。如果您想拥有静态字段的不同实例,那么您可能需要使用类加载器(另一个明显的解决方案是重写字节码,这样做更不容易)。

        2
  •  2
  •   Vineet Reynolds    15 年前

    ThreadLocal类不会更改静态字段修饰符的行为。静态字段将继续在类的多个实例中有一个体现,因为它们是在类初始化时创建和初始化的。

    为了更清楚地说明这一点,线程本地成员在内部使用每个线程基础上存在的映射进行管理。get()和set()调用在此映射上操作。在threadlocal中没有“魔力”,即静态成员失去了拥有单一化身的属性。将静态成员设置为线程局部变量时,只需将其添加到映射中,以供线程执行的代码的另一部分引用。这不会阻止第二个线程获取对静态成员的引用,并对成员字段执行操作。

    正是因为这个原因,汤姆·霍丁的陈述应该被认真对待——可变的静力学根本不构成好的设计。

    PS:看看线程的实现,threadlocal和threadlocal.threadlocalmap类将有助于消除对threadlocal对象行为的任何误解。