Java String

String是Java中使用最频繁的对象之一,其本质上是维护了一个char或byte数组,并提供了基于这个数组的一些操作

Java内存模型

假设一个线程为变量赋值var = 3,那么内存模型需要解决一个问题:在什么条件下,读取var的线程能看到这个值为3

这看上去理所当然,但如果缺少内存同步,那么将有许多因素使得线程无法立即看到,甚至永远看不到另一个线程的操作结果:

  • 在编译器中生成的指令顺序,可以与源代码中的顺序不同,此外编译器还会将变量保存在寄存器而不是内存中;
  • 处理器可以采用乱序或并行等方式来执行指令;
  • 缓存可能会改变将写入变量提交到内存的次序;
  • 而且保存在处理器本地缓存中的值,对于其他处理器是不可见的;

这些因素都会使得一个线程无法看到变量的最新值,并且会导致其他线程中的内存操作看起来似乎在乱序执行。

Jvm规定了一组最小保证,这组保证规定了对变量的写入操作将在何时对于其他线程可见。Jvm在设计时就在可预测性和程序的易于开发性之间进行了权衡,从而在各种主流的处理器体系架构上都能实现高性能的Jvm。Java语言规范要求Jvm在线程中维护一种类似串行的语义:只要程序的最终结果与严格串行环境中执行的结果相同,那么上面所有的操作都是允许的

这确实是一件好事,因为计算机在性能上的提升很大程度要归功于这些重排序措施。在单线程环境中,我们无法看到所有这些底层技术,它们除了提高程序的执行速度外,不会产生其他影响。而在多线程环境中,要维护程序的串行性将导致很大的性能开销。对于并发应用程序中的线程来说,它们在大部分时间里都执行各自的任务,因此,在线程之间的协调操作只会降低应用程序的运行速度,而不会带来任何好处。只有当多个线程要共享数据时,才必须协调它们之间的操作,并且Jvm依赖程序也是通过同步操作来找出这些协调操作将在何时发生。

非阻塞的同步

非阻塞算法可以使多个线程在竞争共享的数据时不发生阻塞,因此它能在粒度更细的层次上进行协调,并且极大地减少调度开销。在基于锁的算法中,如果一个线程在休眠或者自旋时持有锁,那么其他线程都无法执行下去,而非阻塞算法则不会受到单个线程失败的影响。

同步工具类的设计

创建状态依赖类最简单的方法通常是在类库中现有状态依赖类的基础上进行构造,如果类库不支持,则可以使用Java语言和类库提供的底层机制来构造自己的同步机制,包括内置的条件队列、显示的Condition对象以及AbstractQueuedSynchronizer框架。

在单线程的程序中调用方法时,如果基于某个状态的前提条件未得到满足,那么这个条件永远无法成真。而在并发程序中,基于状态的条件可能会由于其他线程的操作而改变。

显式锁的使用

在java 5.0之前,协调共享对象的访问可以使用的机制有synchronizedvolatile,java 5.0中新增了一种新的机制:ReentrantLock。它的作用并不是用来替代内置锁,而是当内置锁不再适用时,作为一种可选择的高级功能。在java 6.0中,也使用了与ReentrantLock类似的算法对内置锁本身进行了改进,有效地提高了内置锁的可伸缩性。

同步的性能问题

安全性活跃性之间通常存在着某种制衡,我们可以使用加锁机制来确保线程安全,但如果过度的使用加锁,则可能导致死锁。