您的位置:首页 > 编程语言 > Java开发

线程安全性

2016-12-14 11:40 197 查看
以下内容简要摘自《java并发编程实战》

  线程或者锁在并发编程中的作用就像铆钉和工字梁在土木工程中的作用。所以在构建稳健的并发程序时,必须正确地使用线程和锁。

  要编写线程安全的代码,其核心在于要对状态访问操作进行管理,特别是对共享的和可变的状态的访问。

  “共享”意味着可以由多个线程同时访问,“可变”意味着变量的值在其生命周期内可以放生变化。

  同步机制协同线程对变量的访问:

   1、synchronized – 独占的加锁方式

   2、volatile 类型的变量

   3、显示锁(Explicit Lock)

   4、原子变量

什么是线程安全性

  当多个线程访问某个类时,这个类始终都能表现出正确的行为,那么就称这个类是线程安全的。

  无状态对象一定是线程安全的。 –例如servlet

原子性

竞态条件

  由于不恰当的执行时序而出现不正确的结果。

  最常见的竞态条件类型就是“先检查后执行”操作,即通过一个可能失效的观测结果来决定下一步的动作。–例如 ++count问题

  要避免竞态条件问题,就必须在某个线程修改该变量时,通过某种方式防止其他线使用这个变量,从而确保其他线程只能在修改线程完成之前或之后读取和修改状态,而不是在修改状态的过程中。

  原子操作是指,对于访问同一状态的所有操作(包括操作本身)来说,这个操作是一个以原子方式执行的操作。

  针对++count问题,可以使用线程安全类AtomicLong管理count的状态,从而确保代码的线程安全性。

  
AtomicLong count = new AtomicLong(0);
count.incrementAndGet();


加锁机制

内置锁

同步代码块(synchronized block)
synchronized (lock){
//访问或修改由锁保护的共享状态
}


  线程在进入同步块代码之前会自动获得锁,在退出同步代码块时自动释放锁。

重入

  当一个线程请求得到一个对象锁后再次请求此对象锁,可以再次得到该对象锁,就是说在一个synchronized方法/块的内部调用本类的其他synchronized方法/块时,是永远可以拿到锁。

class Father {
synchronized void doSomething(){
System.out.println("Father doSomething");
}
}
class Son extends Father{
synchronized void doSomething(){
System.out.println("Son doSomething");
super.doSomething();
}
}


  如果内置锁不是可重入的,那么在调用super.doSomething()时将无法获得Father 上的锁,因为这个锁已经被Father持有了,从而线程将永远停顿下去。重入则避免了这种死锁情况的发生。

活跃性与性能

  在使用锁时,无论是执行计算密集的操作,还是在执行某个可能阻塞的操作,如果持有锁的时间过长,那么都会带来活跃性或性能问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 并发 线程安全