`
Mysun
  • 浏览: 273136 次
  • 性别: 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以及源代码,这里就不再详细说明了(整个实现也比较简单明了)。
分享到:
评论
发表评论

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

相关推荐

    java 并发编程 多线程

    - **每个资源应有一个唯一的锁**:为了简化并发控制,建议每个共享资源都关联一个唯一的锁,这样可以减少死锁的风险。 - **锁粒度的选择**:锁的范围应该尽可能小,这样可以降低锁竞争的可能性,从而提高并发性能。 ...

    java 并发编程实践

    - **线程池**:通过`ExecutorService`管理线程池,有效控制系统的并发程度,提高系统资源利用率。 5. **死锁与活锁** - **死锁**:多个线程相互等待对方释放资源,导致无法继续执行的状态。避免死锁需遵循资源...

    Labwindows的多线程技术

    - 环境隔离:多线程可以用于在多个独立环境中执行特定任务,如并行测试,简化管理复杂度。 2. **选择合适的操作系统**: - Windows 9x系列不支持多处理器,而Windows Vista/XP/2000/NT 4.0及更高版本对多线程有更...

    Java线程 高级使用

    - **简化建模**:多线程可以更好地模拟现实世界的并发情况,简化问题建模。 #### 二、Java线程模型 - **线程模型**:Java中的线程模型主要包括线程对象、线程状态、线程同步机制等。 - **线程对象**:通过`java....

    多线程ADO安全访问SQL

    总结,多线程ADO安全访问SQL涉及到数据库并发控制、事务管理、线程同步和资源管理等多个方面。开发者必须理解这些概念并正确实施,以确保应用程序的稳定性和数据的准确性。通过合理的设计和编程实践,可以在提高性能...

    Java线程 学习

    - **线程的优势**:使用线程可以提高用户界面的响应速度、充分利用多处理器系统、简化模型构建、实现异步处理等。 #### 三、线程在Java中的应用 - **事件驱动的用户界面**:在Java图形用户界面库(如AWT和Swing)中...

    Java多线程

    现代操作系统能利用多处理器系统并发调度线程在多个处理器上运行,从而提高程序运行效率。 - 多线程还可以简化模型的构建,使得程序设计更加模块化,易于管理和维护。 - 实现异步或后台处理任务,可以提高应用程序...

    Java虚拟机并发编程.pdf

    这在需要线程间隔离数据的场景中非常有用,但需要注意过度使用可能导致内存泄漏。 Java并发工具类如`ExecutorService`, `Future`, `Callable`, `BlockingQueue`等,提供了比基本的`Thread`和`synchronized`更高级的...

    利用MoveToThread实现多线程编写

    - **更好的线程隔离**:每个线程都有自己的事件循环,减少了线程之间的相互影响,提高了系统的稳定性和可预测性。 然而,`MoveToThread`也有其局限性,例如不适用于所有类型的对象,特别是那些依赖于特定线程环境...

    操作系统中的进程与线程

    操作系统中的进程与线程是计算机科学中两个基本的并发执行单元。进程是操作系统资源分配的基本单位,它拥有独立的内存空间,包括代码、数据、堆栈等...因此,合理地利用进程和线程,可以优化程序性能并简化同步控制。

    Windows并发编程指南pdf

    - 如何利用.NET并行扩展简化并发编程。 - **当前状态 (Where Are We?)** - 当前.NET并行扩展技术的发展状况。 ### 结论 本书《Windows并发编程指南》全面而深入地探讨了Windows平台上的并发编程知识。从基本...

    JavaWeb并发编程与高并发解决方案.docx

    并发编程旨在利用多线程或多进程来提高程序的执行效率,通过并行处理任务来缩短响应时间和提升系统吞吐量。 ##### 并发概念解析 并发是指多个线程或进程共享资源,并在逻辑上同时执行的能力。在单核处理器环境下,...

    java自带并发框架

    Java并发框架是Java JDK中内置的一系列用于处理多线程并行执行的工具和类库,自JDK 5.0引入以来,极大地简化了并发编程的复杂性。这个框架由Doug Lea设计,并在JSR-166任务中提出,最终在Tiger(JDK 5)版本中被引入...

    servlet多线程

    1. **变量的线程安全**:对于Servlet中的变量,特别是那些可能被多个线程访问的共享数据,如表单参数值,应尽可能将其转化为局部变量,因为多线程环境下的局部变量是线程隔离的。此外,可以使用`synchronized`关键字...

    java线程使用教程

    ### Java线程使用教程知识点详解 ...无论是处理用户界面响应性、利用多核处理器优势还是进行并发编程,都需要深入理解和灵活运用Java线程的相关概念和技术。希望本教程能帮助读者更好地理解和掌握Java线程的使用方法。

    c#线程参考手册 c#线程参考手册 c#线程参考手册

    理解并熟练运用以上知识点,可以帮助开发者高效地编写并发和多线程的C#程序,充分利用多核处理器的性能,同时避免可能出现的并发问题。在实际应用中,还需要结合具体场景灵活选择和使用各种工具和策略,以实现最佳...

    JAVA并发编程实践.zip

    7. **线程局部变量**:ThreadLocal类为每个线程提供独立的变量副本,确保线程间的数据隔离,常用于解决并发环境中的数据一致性问题。 8. **原子操作与Atomic类**:Java提供了一系列Atomic类,如AtomicInteger、...

    Java多线程Junit测试GroboUtils-5.zip

    GroboUtils-5可能就是为了解决这些问题而设计的工具集,它可能包含了用于创建和控制线程的辅助类,以及帮助验证并发条件下的正确性的测试工具。 例如,GroboUtils可能包含: 1. **线程控制**:提供类或方法来启动...

Global site tag (gtag.js) - Google Analytics