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

对包含静态成员的现有类使用ThreadLocal

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

    我正在尝试使用 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 创建(给它们不同的线程名称,如“AAAAAAAAAA”、“BBBBBBBBB”等),然后启动,输出清楚地表明它们正在共享实例。日志输出包括:

    ...
                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 回复  |  直到 9 年前
        1
  •  5
  •   Tom Hawtin - tackline    16 年前

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

    如果你真的无法更改类,你可能只想使用一个锁,这样每个客户端都可以一次使用一个静态字段。如果你想有不同的静态字段实例,那么你可能需要使用类加载器(另一个明显的解决方案是重写字节码,这甚至不那么令人愉快)。

        2
  •  2
  •   Vineet Reynolds    16 年前

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

    为了更深入地了解这一点,ThreadLocal成员在内部使用基于每个线程的Map进行管理。get()和set()调用在此Map上操作。ThreadLocal中没有“魔法”,即静态成员失去其具有单一化身的属性。当静态成员设置为ThreadLocal变量时,只需将其添加到Map中,以便在线程执行的代码的另一部分中引用。这不会阻止第二个线程获取对静态成员的引用,并对成员字段执行操作。

    正是出于这个原因,Tom Hawtin的陈述应该被认真对待——可变静态根本不构成好的设计。