如何保证线程安全?

线程安全的定义

当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象是线程安全的。

线程安全类型

为了更加深入地理解线程安全,我们将Java语言中各种操作共享的数据分为以下5类: 不可变、绝对线程安全、相对线程安全、线程兼容和线程对立。

(1)不可变 : 一定是线程安全的,包括final修饰的基本数据类型,以及不可变对象例如java.lang.String类对象,对象的行为不会对其状态产生任何影响。

(2)绝对线程安全:绝对线程安全通常需要付出很大的,甚至是不切实际的代价,所以一般难以实现。

(3)相对线程安全:通常意义上的线程安全,在调用的时候不需要做额外的保障措施,但是对于一些特定顺序的连续调用,就可能需要在调用端使用额外的同步手段来保证调用的正确性

(4)线程兼容:指的是对象本身并不是线程安全的,但是可以通过在调用端正确地使用同步手段来保证对象在并发环境中可以安全地使用

(5)线程对立:无论调用端是否采用了同步措施,都无法再多线程环境中并发使用的代码,由于Java语言天生具备多线程特性,这种排斥多线程的代码很少出现


线程安全的实现方法

1.互斥同步

互斥同步是常见的一种并发正确性保障手段。

同步是指在多个线程并发访问共享数据时,保证共享数据在同一个时刻只被一个(或者是一些,使用信号量的时候)线程使用。

互斥是实现同步的一种手段,临界区、互斥量、信号量都是主要的互斥实现方式。

互斥是方法,同步是目的。

 

在Java中,最基本的互斥同步手段就是synchronized关键字,synchronized关键字经过编译后,会在同步块的前后分别形成monitorenter和monitorexit这两个字节码指令,这两个字节码都需要一个reference类型的参数来指明要锁定和解锁的对象。

(1)如果Java程序中synchronized明确制定了对象参数,那就是这个对象的reference;

(2)如果没有明确指定,那就根据synchronized修饰的是实例方法还是类方法,去取对应的对象实例或Class对象来作为锁对象。


2.非阻塞同步

互斥同步最主要的问题就是进行线程阻塞和唤醒带来的性能问题,因此这种同步也成为阻塞同步。非阻塞同步是先进行操作,如果没有其他线程争用共享数据,那操作就成功;如果数据有争用,产生了冲突,那就采取其他的补偿措施。

 

3.无同步方案

对于一个方法本来就不涉及共享数据,那就自然无须同步措施来保证正确性。

(1)可重入代码:在代码执行的任何时候中断它,转而去执行另一段代码,控制权返回后,原来的程序不会出现任何错误。

(2)线程本地存储:共享数据的代码是否能在同一个线程中执行,如“生产者-消费者”模式。

你可能感兴趣的:(Java线程及进程)