您的位置:首页 > 其它

深入JVM 线程安全分级

2019-03-17 13:29 141 查看

线程安全:当多个线程访问一个对象,如果不需要考虑这些线程在运行时环境的调度和执行,也不需要额外的同步,或者调用方进行协调,调用执行的结果总是正确的,那么这个对象是线程安全的

在Java中按照安全等级可以将共享数据划分成5类

——不可变

不可变的对象一定是线程安全的,无论是对象的方法实现还是方法的调用者,都不需要采用任何线程安全保障,final关键字:只要一个不可变对象被创建出来,那其外部的可见状态永远不会改变,不必考虑在多线程中的不一致性

在语言层面,如果共享数据是一个基本数据类型,那么只要在定义时使用final关键字修饰即可保证它的不可变。如果共享数据是一个对象,那就需要保证对象的行为不会对其状态产生影响,如 java.lang.String对象,它是一个典型的不可变对象,调用substring() replace() concat()方法都不会影响原先的值,只会返回新构建的字符串对象

——绝对线程安全

所谓绝对线程安全即和线程安全定义一致,但是为了达到这个定义要求,需要很大的代价,所以通常Java语言的线程安全离绝对线程安全有一定差距,如java.util.Vector是一个线程安全的容器,因为它的方法add() get() size()都是被synchronized关键字修饰的,尽管效率很低,但确实是安全的,但是即使它的所有方法被修饰成同步,也不意味者不需要同步手段

案例:

[code]public class VectorTest {

private static Vector<Integer> vector=new Vector<Integer>();

public static void main(String[] args) {
// TODO 自动生成的方法存根

while(true)
{
for(int i=0;i<10;i++)
vector.add(i);

Thread removeThread=new Thread(
new Runnable()
{
@Override
public void run()
{
for(int i=0;i<vector.size();i++)
vector.remove(i);
}
}

);

Thread printThread=new Thread(
new Runnable()
{
@Override
public void run()
{
for(int i=0;i<vector.size();i++)
System.out.println(vector.get(i));
}
}

);

removeThread.start();
printThread.start();
while(Thread.activeCount()>20);

}
}

}

这段程序实际运行过程中会出现ArrayIndexOutOfBoundsException异常,因为如果一个线程恰好在错误的时间内删除了一个元素,导致序号i不可用,再用i访问数组就会抛出异常,所有仍然需要在读写vector进行必要的同步 

——相对线程安全

相对线程安全即通常意义上的线程安全,它需要保证对对象单独的操作是线程安全的,调用的时候不需要额外的保障措施,但是对于一些特定顺序的连续调用,就需要一定的同步手段

大部分线程安全类属于此类型,如Vector HashTable Collections的sychronizedCollecton方法

——线程兼容

线程兼容指对象本身不是线程安全,但可以通过在调用端使用同步手段保证对象在对象环境下的安全,如与Vector HashTable相对的ArrayList HashMap

——线程独立

线程独立是指无论调用端是否采用同步措施,都无法在多线程中并发使用的代码,一个线程对立的例子是Thread类的suspend和resume方法,如果2个线程同时持有一个线程对象,一个试图去中断线程,一个试图去恢复线程,在并发环境下,无论调用是否进行了同步,目标线程都存在死锁的风险,因此高版本的JDK废弃了这些方法

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: