Java最新【Java并发编程实战】04对象的组合,2024华为Java面试真题

最后

2020年在匆匆忙忙慌慌乱乱中就这么度过了,我们迎来了新一年,互联网的发展如此之快,技术日新月异,更新迭代成为了这个时代的代名词,坚持下来的技术体系会越来越健壮,JVM作为如今是跳槽大厂必备的技能,如果你还没掌握,更别提之后更新的新技术了。

更多JVM面试整理:

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

  • 找出构成对象状态的所有变量。

  • 找出约束变量的不变性条件。

  • 建立对象状态的并发访问管理策略。

1.1 收集同步需求

在很多类中都定义了一些不可变条件,用于判断状态是否有效。比如:计数器的取值范围上存在一个限制,就是不能是负值。在操作中还会包含一些后验条件来判断状态迁移是否有有效。比如:计数器的当前状态为8,那么下一个有效状态只能是9。由于不变性条件和后验条件在状态及状态转换上施加了各种约束,因此就需要额外的同步和封装。

1.2 依赖状态的操作

在某些对象的方法中还包含一些基于状态的先验条件。比如:不能从空队列中移除一个元素,在删除元素前,队列必须处于非空状态。如果在某个操作中包含有基于状态的先验条件,那么这个操作就称为依赖状态的操作。

1.3 状态的所有权

对象封装了它拥有的状态,那么它就拥有封装状态的所有权。状态的所有者将决定采用何种加锁协议来维持状态的完整性。所有权意味着控制权。如果发布了某个可变对象的引用,那么就不再拥有独占的控制权,最多是共享控制权。为了防止多个线程在并发访问同一个对象时产生相互干扰,这些对象应该要么是线程安全的对象,要么是事实不可变的对象,或者用同一个锁保护的对象。

2. 实例封闭

将数据封装在对象内部,可以将数据的访问限制在对象的方法上,从而更容易确保线程在访问数据是总能持有正确的锁。被封闭对象一定不能超过它们既定的作用域。对象可以封闭在类的一个实例中,比如:作为类的一个私有成员;或者封闭在某个作用域内,比如:作为一个局部变量;或者封闭在线程中,比如:在某个线程中将对象从一个方法传递到另一个方法,而不是在多个线程之间共享该对象。

封闭机制更易于构造线程安全的类,因为当封闭类的状态时,在分析类的线程安全性时就无须检测整个程序。

2.1 Java监视器模式

遵循Java监视器模式的对象会把对象的所有可变状态都封装起来,并由对象自己的内置锁来保护。示例:一个用于调度车辆的“车辆跟踪器”,每台车都由一个String对象来标识,并且拥有一个对应的位置坐标(x,y)。创建一个追踪器类用来封装所有车辆的标识和位置,该类由多个线程(读取操作和更新操作)共享。

@NotThreadSafe

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;

}

}

@ThreadSafe

public class MonitorVehicleTracker {

@GuardedBy(“this”) private final Map<String, MutablePoint> locations;

public MonitorVehicleTracker(Map<String, MutablePoint> locations) {

this.locations = deepCopy(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 synchronized 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<String, MutablePoint>();

for (String id : m.keySet())

result.put(id, new MutablePoint(m.get(id)));

return Collections.unmodifiableMap(result);

}

}

虽然类MutablePoint不是线程安全的,但是追踪器类是线程安全的。它所包含的Map对象和可变的Point对象都未曾发布。当需要回去车辆的位置时,通过拷贝构造函数或deepCopy方法复制正确的值,从而生成一个新的Map对象。车辆少是,这并不存在性能问,单在车辆数量非常大的情况下将极大地降低性能。

3. 线程安全性的委托

大多数对象都是组合对象。当从头开始构建一个类,或者将多个非线程安全的类组合为一个类时,Java监视器模式是非常有用的。但是,如果类中的各个组件都已经是线程安全的,那么实现线程安全就应视情况而定。

3.1 基于委托的车辆追踪器

保存车辆位置信息的Map类,使用线程安全的ConcurrentMap类代替。使用不可变的Point类来代替MutablePoint类:

public class Point {

public final int x, y;

public Point(int x, int y) {

this.x = x;

this.y = y;

}

}

在DelegatingVehicleTracker中没有使用显式的同步,所有对状态的访问都由ConcurrentHashMap来管理:

@ThreadSafe

public class DelegatingVehicleTracker {

private final ConcurrentMap<String, Point> locations;

private final Map<String, Point> unmodifiableMap;

public DelegatingVehicleTracker(Map<String, Point> points) {

locations = new ConcurrentHashMap<String, Point>(points);

unmodifiableMap = Collections.unmodifiableMap(locations);

}

public Map<String, Point> getLocations() {

return unmodifiableMap;

}

public Point getLocation(String id) {

return locations.get(id);

}

public void setLocation(String id, int x, int y) {

if (locations.replace(id, new Point(x, y)) == null)

throw new IllegalArgumentException("invalid vehicle name: " + id);

}

}

在使用监视器模式的车辆追踪器中返回的是车辆位置的快照,而在使用委托的车辆追踪器中返回的是一个不可修改却实时的车辆位置信息。

3.2 独立的状态变量

我们还可以将线程安全性委托给多个状态变量,只要这些变量是彼此独立的,即组合而成的类并不会在其包含的多个状态变量上增加任何不变形条件。

最后

给大家送一个小福利

附高清脑图,高清知识点讲解教程,以及一些面试真题及答案解析。送给需要的提升技术、准备面试跳槽、自身职业规划迷茫的朋友们。

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

解析。送给需要的提升技术、准备面试跳槽、自身职业规划迷茫的朋友们。

[外链图片转存中…(img-ieIw5urO-1715417489837)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值