写点什么

快速鸟瞰并发编程,- 呕心沥血整理的架构技术【2】,分层展示的架构图

用户头像
极客good
关注
发布于: 刚刚

确保此引用在构造期间不逃逸。


this 引用逃逸("this"escape)是指对象还没有构造完成,它的 this 引用就被发布出去了。这是危及到线程安全的,因为其他线程有可能通过这个逸出的引用访问到“初始化了一半”的对象(partially-constructed object)。这样就会出现某些线程中看到该对象的状态是没初始化完的状态,而在另外一些线程看到的却是已经初始化完的状态,这种不一致性是不确定的,程序也会因此而产生一些无法预知的并发错误。在说明并发编程中如何避免 this 引用逸出之前


class JamesThisEscapes {private final String name;ThisEscapes(String name) {JamesCache.putIntoCache(this);this.name = name;}String getName() {return name;}}class JamesCache {private static final Map<String, ThisEscapes> CACHE = new ConcurrentHashMap<>();static void putIntoCache(JamesThisEscapes thisEscapes) {//“this”引用在对象完全构造之前逃逸 CACHE.putIfAbsent(thisEscapes.getName(), thisEscapes);}}


  • 正确同步成员变量。


class JamesSynchronization {private String state;synchronized String getState() {if (state == null)state = "Initial";return state;}}

第 6 节 不可变的对象

不可变对象具备执行安全的特性。此外,相较于可变对象,不可变对象通常也较合理,易于了解,而且提供较高的安全性。不可变对象的一个重要特性是它们都是线程安全的,因此不需要同步。当然对象不可变的是有如下要求滴:


  • 所有变量都是?final.

  • 所有变量必须是可变对象或不可变对象。

  • this?在构造方法执行期间引用不会逃脱。

  • 该类是 final,因此无法在子类中覆盖此行为。


不可变对象的示例:


// 声明为 final 类 public final class JamesArtist {// 不可变对象, 字段为 finalprivate final String name;//用于保存不可变对象, final 类型 private final List<JamesTrack> tracks;public JamesArtist(String name, List<JamesTrack> tracks) {this.name = name;//防止拷贝 List<JamesTrack> copy = new ArrayList<>(tracks);//标记为不可更改 this.tracks = Collections.unmodifiableList(copy);// “this”在构造期间不会传递到任何地方。}}// 同上声明为 final 类 public final class JamesTrack {// 不可变对象, 字段为 finalprivate final String title;public JamesTrack(String title) {this.title = title;}}

第 7 节 线程 Thread 类

java.lang.Thread类用于表示应用程序或 JVM 线程。代码总是在某些 Thread 类的上下文中执行(用于 Thread#currentThread()获取自己的 Thread)。


线程状态如下



线程协调方法如下


怎么处理 InterruptedException 异常?

  • 在重新抛出 InterruptedException 之前执行特定于任务的清理工作。

  • 声明当前方法抛出?InterruptedException.

  • 如果未声明某个方法抛出?InterruptedException,则应通过调用将中断的标志恢复为 true,?Thread.currentThread().interrupt()并且应该抛出一个更合适的异常。将标志设置为 true 非常重要,以便有机会处理更高级别的中断。

不可预知的异常处理

线程可以指定 UncaughtExceptionHandler将接收任何导致线程突然终止的未捕获异常的通知。


Thread thread = new Thread(runnable);thread.setUncaughtExceptionHandler((failedThread,exception)->{logger.error("Caught unexpected exception in thread‘{}’.", failedThread.getName(), exception);});thread.start();

第 8 节 线程的活跃度

死锁

当存在多个线程时会发生死锁,每个线程等待另一个线程持有的资源,从而形成资源循环和获取线程。


潜在的死锁示例:


class JamesAccount {private long amount;void plus(long amount) {this.amount += amount;}void minus(long amount) {if (this.amount < amount)throw new IllegalArgumentException(); elsethis.amount -= amount;}static void transferWithDeadlock(long amount, JamesAccount first, JamesAccount second) {synchronized (first) {synchronized (second) {first.minus(amount);second.plus(amount);}}}}


如果同时发生死锁:


  • 一个线程正在尝试从第一个帐户转移到第二个帐户,并且已经获得了第一个帐户的锁。

  • 另一个线程正在尝试从第二个帐户转移到第一个帐户,并且已经获得了第二个帐户的锁。


避免死锁的技巧:


  • 锁定顺序 - 始终以相同的顺序获取锁。


class JamesAccount {private long id;private long amount;// 此处省略了一些方法 static void transferWithLockOrdering(long amount, JamesAccount first, JamesAccount second) {Boolean lockOnFirstAccountFirst = first.id < second.id;Account firstLock = lockOnFirstAccountFirst ? first : second;Account secondLock = lockOnFirstAccountFirst ? second : first;synchronized (firstLock) {synchronized (secondLock) {first.minus(amount);second.plus(amount);


【一线大厂Java面试题解析+核心总结学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码


}}}}

用户头像

极客good

关注

还未添加个人签名 2021.03.18 加入

还未添加个人简介

评论

发布
暂无评论
快速鸟瞰并发编程,-呕心沥血整理的架构技术【2】,分层展示的架构图