java并发笔记一

Posted by Peterliao Blog on May 2, 2018

java并发-如何构建一个线程安全的程序

对于多线程程序来说,引发多线程安全问题的原因为同时访问共享资源导致的,因此设计一个多线程安全的应用程序关键在于两个方面,就是让两个原因不成立即可。所以对于多线程的并发问题只有两个解决方案,那就是让两个条件不成立,使变量不共享或者控制线程不要同时访问。对应到java中,就是线程封闭和访问控制两种方案。线程封闭是指将变量封闭到各个线程私有,使得变量不共享,从而达到多线程安全。访问控制则是通过锁,信号量等机制控制线程对于非私有变量的访问,达到多线程安全。

在java中实现线程安全的具体做法有三种

  • 不可变(无法修改不存在线程安全问题)
  • 线程封闭(ThreadLocal的set和get实现,使得变量不共享)
  • 锁机制(访问控制,不同时访问)

对于实现线程安全来说,最简单和最实用的方法为线程封闭,通过调整程序的结构,使得变量尽量封闭在单个线程里,比如将状态变为方法的成员变量,使用ThreadLocal变量(每个线程一份副本)。

线程封闭

所谓线程封闭,就是利用封装将状态封装到线程中,使得该线程的状态对其他线程不可访问。具体比如线程方法的临时变量就是被封装到线程中了。线程封闭的状态必定是线程安全的,因为该状态不被其他线程共享,破坏了线程不安全的共享条件。

不可变

另外一个常见的控制线程安全的方法为使得变量不可变,也就是使用final关键字来修饰变量。一旦状态不可变也同样破环了线程不安全的条件。同时final变量还提高了程序的健壮性,使得调用方无法随意更改状态,防止了外部不大安全或者不合适的更改,当然final变量同时也消耗了一部分内存(较少),因此对于能够确定无需外部更改的变量,尽量使用final进行修饰。

锁机制

锁机制是当上述两种方案都无法实现线程安全后的另一种方法,java中提供了众多的锁机制。比如常见的syncronized关键字,ReentrantLock,CountDownLantch(闭锁),semaphore(信号量)和CyclicBarrier(栅栏)等,还有不常见的自旋锁,CAS操作等。所有的这些都是为了提供一种机制来控制线程对于共享状态的访问,使得每次对于共享状态的访问只有一个线程。