关于同步机制的一些见解

首先先提一下线程安全问题,我们判断一个程序是否有线程安全的问题的标准是:

   a.是否是处于多线程环境

   b.是否有共享数据

   c.是否有多条语句操作共享数据

通过以上的参考标准我们可以清晰的知道,关于一二两点,我们是无法改变的,因此我们只能尝试去改变第三条:

思想:就是将多条语句操作共享的语句包成一个整体,即让这段代码具有原子性,让某个线程在执行的时候,别人的线程无法执行,因此Java提供可同步机制

正文开始:

    因为如果没有同步机制的话,便会由于线程的不确定先后执行顺序,可能导致数据的讹误,因此Java中有两种机制可以防止代码块受并发访问的干扰,第一种就是synchronized关键字,第二种就是JavaSE 5.0引入的ReentranLock类。

 一、Lock接口

首先说说Lock的实现类ReentranLock:

扫描二维码关注公众号,回复: 10292624 查看本文章
 1 public class Bank{
 2     
 3         private Lock bankLock = new ReentrantLock();
 4         
 5         public void transfer(int from,int to,int amount){
 6             bankLock.lock();
 7             try
 8             {
 9                     System.out.print(Thread.currentThread());
10                     ...
//getTotalBalance()方法中也有一个bankLock锁;
11 System.out.print(getTotalBalance()); 12 } 13 finally 14 { 15 bankLock.unlock(); 16 } 17 } 18 19 }

1、 假定一个线程调用 tranfer ,在执行结束前被剥夺了运行权。假定第二个线程也调用了 tranfer ,但是由于第二个线程不能获得锁,将在调用lock方法时 被阻塞。它必须等待第一个线程完成 tranfer 方法的执行之后才能再度被激活。当第一个线程释放时,那么第二个线程才能开始开始运行 ,因此这个类上锁的代码不会出现数据的讹误。

注意每一个bank对象都有自己的ReentranLock对象,如果两个线程访问同一个Bank对象,那么锁将以串行的方式提供服务,但是,如果两个线程访问不同的Bank对象,每一个线程得到不同的锁对象,两个线程都不会发生阻塞,这也是应该的,因为线程在操作不同的Bank对象的时候,线程之间时互不影响的。

2、锁是可重入的,线程可以重复地获取自己所持有的锁,锁保持了一个持有计数(holdcount)来跟踪对lock方法的嵌套使用。线程每一次调用lock都要调用unlock来释放锁,由于这个特性,被一个锁保护的代码可以调用另一个使用相同的锁的方法:蓝色字什么意思呢?我举个例子:

例如,上图的代码,仔细看,tranfer 方法调用 getTotalBalance 方法,这也会封装bankLock 对象,此时bankLock 对象的持有计数为2,当getTotalBalance 方法退出的时候,持有计数变回1。当tranfer 方法也退出的时候,持有计数变为 0 ,线程锁释放。

注意:要留意临界区种的代码,不要因为异常的抛出而跳出了临界区。如果在临界区代码结束之前抛出了异常,finally子句将释放锁,但会使对象可能处于一种受损状态。

待续。。。。

猜你喜欢

转载自www.cnblogs.com/afei1013/p/12442326.html