Java Concurrency in Practice 4.1-4.2相关问题及理解

今天终于又重新拿起了Java Concurrency in Practice,之前被虐的体无完肤,在看这本书之前,有一部分自己写的代码我根本没意识到是线程不安全的,还真的是要恶补这方面的知识。

1.Java监视器模式

监视器模式其实很简单,就是用私有对象的锁或者内置锁来保证所属对象的线程安全性。这里引入一个例子:车辆追踪

public class MonitorVehicleTracker {
    private final Map<String, MutablePoint> locations;

    public MonitorVehicleTracker(Map<String, MutablePoint> locations) {
        this.locations = locations;
    }

    public synchronized Map<String, MutablePoint> getLocations() {
        return deepCopy(locations);
    }

    public synchronized MutablePoint getLocation(String id) {
        MutablePoint loc = locations.get(id);
        return loc == null ? null : new MutablePoint(loc);
    }

    public void setLocation(String id, int x, int y) {
        MutablePoint loc = locations.get(id);
        if(loc == null)
            throw new IllegalArgumentException("No such ID: " + id);
        loc.x = x;
        loc.y = y;
    }

    private static Map<String, MutablePoint> deepCopy(Map<String, MutablePoint> m) {
        Map<String, MutablePoint> result = new HashMap<>();
        for(String id : m.keySet())
            result.put(id, new MutablePoint(m.get(id)));
        return Collections.unmodifiableMap(result);
}

这里MutablePoint是个可变类,是线程不安全的:

public class MutablePoint {
    public int x, y;

    public MutablePoint() { x = 0; y = 0; }
    public MutablePoint(MutablePoint p) {
        this.x = p.x;
        this.y = p.y;
    }
}

细心的读者肯定发现了,MonitorVehicleTracker类中几乎每一个方法都要复制MutablePoint或者locations的值,原因在于,这俩不是线程安全的,不能直接发布出去,所以只能发布一个副本,但是这又出

了新的问题:虽然MonitorVehicleTracker类是线程安全的,但是由于数据都是复制的,那么假设线程A调用了getLocations()方法得到了位置,此时车的位置变化,线程B调用setLocation()修改了内部变量

locations,这时车辆的位置已经修改了,但是线程A返回的还是旧的位置。当然,如果为了保持数据的一致性,那么这样做就是优点;但如果想要得到车辆的实时位置,就不得不得到车辆位置的最新快照,

上述的方法会造成严重的性能问题。那么如何改进呢?在这里我给个提示:复制数据的原因是因为属性是线程不安全的,不能直接发布,那么,如果发布一个线程安全的属性,是否就解决了实时性的问题?

原文地址:https://www.cnblogs.com/cedriccheng/p/9016810.html

时间: 2024-12-16 16:48:24

Java Concurrency in Practice 4.1-4.2相关问题及理解的相关文章

Java Concurrency In Practice -Chapter 2 Thread Safety

Writing thread-safe code is managing access to state and in particular to shared, mutable state. Object's state is its data, stored in state variables such as instance or static fields. Whether an object needs to be thread-safe depends on whether it

[Java Concurrency in Practice]第三章 对象的共享

对象的共享 第二章介绍了如何通过同步来避免多个线程在同一时刻访问相同的数据,而第三章则介绍如何共享和发布对象,从而使它们能够安全地由多个线程同时访问.这两章合在一起就形成了构建线程安全类以及通过java.util.concurrent类库来构建并发应用程序的重要基础. synchronized不仅仅只有原子性,还具有内存可见性.我们不仅希望防止某个线程正在使用对象状态而另一个线程在同时修改该状态,而且希望确保当一个线程修改了对象状态后,其他线程能够看到发生的状态变化.如果没有同步,那么这种情况就

java并发编程实战(java concurrency in practice)

第一章 线程共享进程范围内的资源,但每个线程都有各自的程序计数器.栈以及局部变量等. 多个线程可以同时调度到多个CPU上运行. 线程的优势? 在服务应用程序中,可以提升资源利用率以及系统吞吐率,发挥多处理器的强大功能. 线程的优先级  执行时间  线程切换需要额外的开销 第二章 如果多个线程访问同一个可变的状态变量是没有使用合适的同步,那么程序就会出现错误,有以下三种方法修复这种问题. 1.不在线程之间共享该状态变量 2.将状态变量改为不可变的变量 3.在访问状态变量时使用同步 什么是线程安全?

[Java Concurrency in Practice]第七章 取消与关闭

取消与关闭 要使任务和线程能安全.快速.可靠地停止下来,并不是一件容易的事.Java没有提供任何机制来安全地终止线程(虽然Thread.stop和suspend方法提供了这样的机制,但由于存在缺陷,因此应该避免使用).但它提供了中断,这是一种协作机制,能够使一个线程终止另一个线程的当前工作. 这种协作式的方法是必要的,我们很少希望某个任务.线程或服务立即停止,因为这种立即停止会使共享的数据结构处于不一致的状态.相反,在编写任务和服务时可以使用一种协作的方式:当需要停止时,它们首先会清除当前正在执

[Java Concurrency in Practice]第八章 线程池的使用

线程池的使用 8.1 在任务与执行策略之间的隐性耦合 虽然Executor框架为制定和修改执行策略提供了相当大的灵活性,但并非所有的任务都能适用所有的执行策略.有些类型的任务需要明确地执行执行策略,包括: 1. 依赖性任务:当线程池中运行任务都是独立的时,我们可以随意地修改池的长度与配置,这不会影响到性能以外的任何事情.但如果你提交到线程池中的任务依赖于其他的任务,这就会隐式地给执行策略带来了约束. 2. 非安全性任务:如果即使一个任务有线程安全性问题,只要它在单线程的环境下运行是不会有问题,如

[Java Concurrency in Practice]第二章 线程安全性

线程安全性 要编写线程安全的代码,其核心在于要对状态访问操作进行管理,特别是对共享和可变的状态的访问. 对象的状态是指存储在状态变量(例如实例或静态域)中的数据.对象的状态可能包括在其他依赖对象的域.例如,某个HashMap的状态不仅存储在HashMap对象本身,还存储在许过Map.Entry对象中.在对象的状态中包含了任何可能影响其外部可见行为的数据. "共享"意味着变量可以由多个线程同时访问,而"可变"则意味着变量的值在其生命周期内可以发生变化. 一个对象是否需

[Java Concurrency in Practice]第五章 基础构建模块

基础构建模块 委托时创建线程安全类的一个最有效的策略,只需让现有的线程安全类管理所有的状态即可. 平台类库中包含了一个并发构建块的丰富集合,如线程安全的容器与同步工具. 5.1 同步容器类 分两部分,一是JDK1.0的Vector与Hashtable,另一个是JDK1.2才被加入的同步包装类Collections.synchronizedXxx工厂方法创建的.Collections.synchronizedXxx工厂方法构造出的容器返回的List与Set的iterator()与listItera

[Java Concurrency in Practice]二至五章小结

下面这个"并发技巧清单"列举了在第一部分(二至五章)中介绍的主要概念和规则. 可变状态是至关重要的(It's the mutable state,stupid). 所有的并发问题都可以归结为如何协调对并发状态的访问.可变状态越少,就越容易确保线程安全性. 尽量将域声明为final类型,除非需要它们是可变的. 不可变对象一定是线程安全的. 不可变对象能极大地降低并发编程的复杂性.它们更为简单而且安全,可以任意共享而无须使用加锁或保护性复制等机制. 封装有助于管理复杂性. 在编写线程安全的

深入浅出 Java Concurrency (28): 线程池 part 1 简介[转]

从这一节开始正式进入线程池的部分.其实整个体系已经拖了很长的时间,因此后面的章节会加快速度,甚至只是一个半成品或者简单化,以后有时间的慢慢补充.完善. 其实线程池是并发包里面很重要的一部分,在实际情况中也是使用很多的一个重要组件. 下图描述的是线程池API的一部分.广义上的完整线程池可能还包括Thread/Runnable.Timer/TimerTask等部分.这里只介绍主要的和高级的API以及架构和原理. 大多数并发应用程序是围绕执行任务(Task)进行管理的.所谓任务就是抽象.离散的工作单元