`
Mysun
  • 浏览: 270509 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

利用线程隔离简化并发控制

阅读更多
在Java中,为了限制多个不同线程对共享变量或者状态的访问,利用语言提供的同步或者加锁机制是最简单有效的办法。通过加锁或者同步,我们可以控制同一时间只有一个线程能够访问共享变量或者转台,从而保证变量或者状态的在多个线程之间的一致性和完整性。加锁或者同步的方式对于所有需要限制线程对其进行访问的变量或者状态来说都是有效的,但是对于有些场景来说并不是最好的。也就是说,在某些场景下,通过加锁或者同步确实可以保证程序在多线程环境下的正确性,但是也对程序的性能造成了很大的伤害。
下面的代码给出了一个例子。我们定义了一个Cooker类,这个类有一个cook方法,在cook方法中会根据其menu属性执行一些操作。menu中可能包含的信息包括要做什么菜、配料是什么、要不要放辣椒等等。
public class Cooker {
    private Menu menu = null;

    public void setMenu(Menu menu) {
        this.menu = menu;
    }

    public void cook() {
        Menu menu = menu;
        //cook according to the menu
    }
}


对于这个例子来说,每个线程在使用的都可以创建新的Cooker和Menu类,从而避免任何的并发问题。但是,如果Cooker的创建过程非常耗时且需要占用很多系统资源,而其执行过程除了需要获取menu属性中的信息之外,再没有其他共享的属性或者状态时,每个线程都创建一个Cooker对象就不是一个非常明智的选择了。
这种情况下,我们就需要考虑Cooker对象中menu属性的并发问题了。如果不对多个线程访问menu属性的行为进行限制,就可能会引起很多的问题。因为不同的线程可以自由地设置menu属性,那么某个线程在执行cook方法的时候使用的menu对象可能就不是自己在开始的时候设置的那个menu对象,导致程序的运行结果非常奇怪。
我们可以通过加锁或者同步来解决这个问题,
class Kitchen {
    private Cooker cooker = new Cooker();

    public void doCook() {
        Menu menu = new Menu();
        synchronized (cooker) {
            cooker.setMenu(menu);
            cooker.cook();
        }
    }
}


我们可以通过上面的形式,将对cooker对象的访问放在同步块中,来限制每次只有一个线程能够设置Cooker对象中的menu属性,并执行cook方法。除此之外,将doCook方法声明为同步方法可以起到同样的效果。另外还可以通过显示加锁的方式来替换同步快。
通过上面的处理,程序又可以正常运行了,但是程序的并发性却大大降低了。那么有没有更好的方式,即能够保证程序的正确性,又能够提高程序的并发性呢?答案就是使用线程隔离技术。先来看下事例代码,
public class Cooker {
    Map<Thread, Menu> menuPerThreadMap = new ConcurrentHashMap<Thread, Menu>();

    public void setMenu(Menu menu) {
        Thread currentThread = getCurrentThread();
        menuPerThreadMap.put(currentThread, menu);
    }

    public void cook() {
        Thread currentThread = getCurrentThread();
        Menu menu = menuPerThreadMap.get(currentThread);

        if (menu != null) {
            //cook according to the menu
        }
    }

    private Thread getCurrentThread() {
        return Thread.currentThread();
    }
}


上面的代码中,在Cooker中定义了一个menuPerThreadMap属性,这个属性的作用是维护一个当前线程到Menu对象的映射关系。在setMenu方法中,我们当前线程和与之对应的Menu对象放到这个map中,在cook方法中,则通过当前线程从这个map中取出对应的Menu对象,然后执行后面的动作。通过这种方式,我们将不同线程所设置的Menu对象隔离开来,使得他们相互之间不受影响,从而保证了程序的正确性。而又由于消除了使用加锁或者同步的必要性,提高了程序的并发能力。
上面的这个方法基本上阐述了线程隔离的本质,一般来说,如果一个类中某个属性会因不同线程的执行上下文不同而被赋予不同的值,从而影响类的行为,但是这个属性的影响范围仅限于当前线程的话(每个线程都会根据自己的情况为来设置这个属性),线程隔离技术是一个不错的选择。
当然,上面给出的实现非常的粗糙,一个显而意见的问题就是没有对menuPerThreadMap中的元素进行垃圾回收,因此menuPerThreadMap会随着程序的执行不断增长,最终会引起java虚拟机的内存溢出。当然,这里是有办法来进行处理的,但是却没有这个必要。因为Java已经为我们提供了相关的类和方法来有效利用线程隔离技术,这个类就是ThreadLocal。
ThreadLocal类的基本思想与本文所说的线程隔离技术完全一只,但是其实现要更加复杂和可靠,不仅考虑到了通用性,同时也很好地解决了垃圾回收问题。相关文档和实现可以参考Java的API以及源代码,这里就不再详细说明了(整个实现也比较简单明了)。
分享到:
评论
发表评论

文章已被作者锁定,不允许评论。

相关推荐

    C#多线程编程详解

    1、可以使用线程将代码同其他代码隔离,提高应用程序的可靠性。 2、可以使用线程来简化编码。 3、可以使用线程来实现并发执行。 二、基本知识 1、进程与线程:进程作为操作系统执行程序的基本单位,拥有应用程序的...

    深入了解c#多线程编程

    1、可以使用线程将代码同其他代码隔离,提高应用程序的可靠性。 2、可以使用线程来简化编码。 3、可以使用线程来实现并发执行。 二、基本知识 1、进程与线程:进程作为操作系统执行程序的基本单位,拥有应用程序的...

    亿级流量电商详情页系统实战-缓存架构+高可用服务架构+微服务架构

    6、后台服务的多线程并发架构设计:对于后台运行的服务,需要采用多线程并发设计大幅度提升系统的资源利用率以及吞吐量。 7、Redis集群的批量数据查询性能优化:对于分布式的Redis集群,数据在多个实例中分布式存储...

    Hibernate实战(第2版 中文高清版)

    第一部分 从Hibernate和EJB 3.0开始  第1章 理解对象/关系持久化  ... 17.5 利用Seam简化持久化   17.5.1 实现对话   17.5.2 让Seam管理持久化上下文   17.6 小结  附录A SQL基础知识  附录B 映射快速参考

    akka-money-transfer:使用Akka + Scala + Kafka + Microservices的React性银行帐户(P2P转移)

    分类帐是使用参与者模式编写的,以简化并发模型。 这意味着每个帐户都是一个演员,他知道如何以线程安全的方式更改可变状态。 贡献准则 为每个参与者消息编写单元测试 为每个公开的API端点编写集成测试 Scala 2.13...

    IIS6.0 IIS,互联网信息服务

     一、IIS的添加 请进入“控制面板”,依次选“添加/删除程序→添加/删除Windows组件”,将“Internet信息服务(IIS)”前的小钩去掉(如有),重新勾选中后按提示操作即可完成IIS组件的添加。用这种方法添加的IIS...

    GraphOne:“图一

    讨论并发分析机制,以及许多新事物和结果。 参考书目见文末。 我们现在正在尝试开发一个具有快照隔离事务保证的成熟图数据库。 欢迎具有查询引擎开发专业知识的人士合作。 最新版本 V0.1 包含比 V0.0 版本更简单...

    asp.net知识库

    第2章 并发操作的一致性问题 (2) Using sqlite with .NET Visual Studio 2005 中的新 DataSet 特性 MySQL 和 .Net2.0配合使用 与DotNet数据对象结合的自定义数据对象设计 (二) 数据集合与DataTable 与DotNet数据对象...

    Spring.3.x企业应用开发实战(完整版).part2

    10.4.1 Spring通过单实例化Bean简化多线程问题 10.4.2 启动独立线程调用事务方法 10.5 联合军种作战的混乱 10.5.1 Spring事务管理器的应对 10.5.2 Hibernate+Spring JDBC混合框架的事务管理 10.6 特殊方法成漏网之鱼...

    Spring3.x企业应用开发实战(完整版) part1

    10.4.1 Spring通过单实例化Bean简化多线程问题 10.4.2 启动独立线程调用事务方法 10.5 联合军种作战的混乱 10.5.1 Spring事务管理器的应对 10.5.2 Hibernate+Spring JDBC混合框架的事务管理 10.6 特殊方法成漏网之鱼...

    Visual.Basic.2010.&.NET4.高级编程(第6版)-文字版.pdf

    Visual.Basic.2010.&.NET4.高级编程(第6版)-文字版.pdf 第I部分 语言结构和环境 第1章 visual studio 2010 3 1.1 visual studio 2010:从express到ultimate的各种版本 4 1.2 visual basic的关键字和语法 7 ...

    Java课程设计项目实例《基于微服务的在线签到》子系统.pdf

    "在线签到"子系统选用目前比较流行和热门的"微服务"( Microservice)作为系统 的技术实现方案,作者在本文中将重点介绍子系统所应用的核心技术——微服务、反射、 对象序列化、多线程以及基于 TIP/IP 的 Socket ...

Global site tag (gtag.js) - Google Analytics