`
zds625
  • 浏览: 10861 次
  • 性别: Icon_minigender_1
  • 来自: 广州
最近访客 更多访客>>
社区版块
存档分类

Java并发编程实践-线程安全(随笔)

    博客分类:
  • Java
阅读更多
并发的编程并不会过多的涉及线程和锁,编写线程安全的嗲吗,本质上是管理对状态的访问,而且通常是共享、可变的状态。
状态:一个对象的状态时指它的数据,存储在状态变量中,比如实力域或静态域。
共享:是指一个变量可以被多个线程访问。
可变:是指变量在其生命周期内是可以改变的。
保证线程的安全性,真正要做的是在不可控制的并发访问中保护数据。

无论何时,只要多于一个线程来访问状态变量,其中一个线程会对变量进行写操作,此时就必须使用同步来协调线程对该变量的访问。

在没有正确同步的情况下,多个线程访问一个变量,会造成隐患,可有如下方法修复:
1、不要跨线程访问变量。
2、使状态变量为不可变的。
3、访问状态变量时使用同步

线程安全性定义:无论是多线程中的时序或交替操作,都要保证不破坏那些不变约束(状态变量的一致性)

原子操作:一个单独的,不可分割的操作。
class AtomTest implements Servlet {
   private long count = 0;
   public long getCount(){return count;}
   public void service(ServletRequest request,ServletResponse response) {
   ++count;
   }
}

上述例子中,++count看上去像是一个单独的操作,但它不是原子操作,这个一个“读-改-写”的操作,在缺乏同步的条件下,两个线程试图更新这个计时器的数值时,则可能会造成结果不符合,而这种结果则是由竞争条件所引起

竞争条件:当计算的正确性依赖于运行时相关的时序或者多线程的交替时,会产生竞争条件。最常见的竞争条件是“检查再运行(check-then-act)”,使用一个潜在的过期值作为决定下一步操作的依据。

复合操作:比如我们将“检查再运行”和读-改-写的操作过程看做是复合操作。为了线程安全必须执行原子操作,计时器例子的修改,可通过已有线程安全类java.util.concurrent.atomic实现
class AtomTest implements Servlet {
   private Atomiclong count = new Atomiclong(0);
   public long getCount(){return count;}
   public void service(ServletRequest request,ServletResponse response) {
    count.incrementAndGet();
   }
}


AtomicLong可确保计时器的状态操作时原子的。

锁--内部锁
java提供了强制原子性的内置锁机制:synchronized块。synchronized块分成两部分:锁对象的引用和锁保护的代码块,锁即是所在对象本身,锁的获得是在进入synchronized时自动获得的,在正常退出或者异常的时候自动释放锁。这就意味着只有一个线程能获得锁,其他线程想获得锁就只能等待或阻塞,知道占有锁的线程释放了锁。
重进入
内部锁是可以重进入,即线程在试图获取它自己占有的锁时,是能获得成功的。重进入方便了锁行为的封装,当子类override父类的synchronized类型的方法,并调用父类的方法时,如果没有可重入锁,那么这样的使用,将产生死锁。子类的synchronized方法首先获得了锁,在调用父类synchronized方法是,如果没有锁重入,那么父类的方法将永远获得锁,重入锁避免了这种情况

0
0
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics