博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java并发编程(synchronized同步和Lock显示锁)
阅读量:5030 次
发布时间:2019-06-12

本文共 3884 字,大约阅读时间需要 12 分钟。

为什么需要并发程序?       

       线程是java语言中不可或缺的重要功能,它们能使复杂的异步代码变得更简单,从而极大地简化了复杂系统的开发。另外,在开发当数据量大的时候,往往需要使用多线程来提高程序的运行速度,尤其是如今处在一个大数据的时代。在并发编程中,就是需要解决实现线程安全问题,而这个问题的核心就在于要对状态访问操作进行管理,说简单点,就是要管理好对共享数据的访问。“共享”意味着多个线程可以同时访问,如果多个线程同时操作一个共享的变量,就会出现错误的数据,所以就需要采取相应的方法来解决它。

       在java中有多种方式可以防止代码块受并发访问的干扰,这里说说synchronized同步和Lock显示锁。

synchronized同步

       为了解决线程安全问题,java从早期的版本就开始提供synchronized关键字来实现同步。从1.0版本开始,java的每一个对象都有一个内部锁。如果一个方法用synchronized关键字声明,那么对象的锁将保护这个方法。也就是说,要调用该方法,线程必须获得内部的对象锁。另外,同步synchronized提供两种锁的方式,一个是同步方法,另一个是同步代码块,同步方法用的是该方法的所属类的对象,而同步代码块的锁由你指定,可以是任意对象。可以看看这里。synchronized关键字能自动提供一个锁以及相关的“条件”,对于大多数情况,用它是很方便的。
synchronized(对象) {  // 任意对象都可以。这个对象就是锁。	需要被同步的代码;}
public synchronized void transfer() {}

Lock显示锁

       java从1.5版本之后,提供了Lock接口。这时候,直接将锁封装成了对象。线程进入同步就是具备了锁,执行完,离开同步,就是释放了锁。在后期对锁的分析过程中,发现,获取锁,或者释放锁的动作应该是锁这个事物更清楚。所以将这些动作定义在了锁当中,并把锁定义成对象。所以,同步是隐式的锁操作,而Lock对象是显示的锁操作
bankLock.lock(); //a ReentrantLock object      try      {         //临界区      }      finally      {         bankLock.unlock();  //如果在临界区抛出异常,必须保证锁被释放      }
另外,有一个需要注意的地方是,锁的唤醒机制的不同。
       在用同步synchronized来实现加锁时,由于这时的锁用的是任意对象,所以如wait,notify,notifyAll等操作锁的等待唤醒的方法都定义在Object中。
       而现在用Lock时,用的锁是Lock对象。所以查找等待唤醒机制方式需要通过Lock接口来完成。而Lock接口中并没有直接操作等待唤醒的方法,而是将这些方式又单独封装到了一个对象中。这个对象就是Condition,将Object中的三个方法进行单独的封装。并提供了功能一致的方法 await()、signal()、signalAll()体现新版本对象的好处。
下面是java核心技术卷1中关于显示锁核心代码:
/**    * Transfers money from one account to another.    * @param from the account to transfer from    * @param to the account to transfer to    * @param amount the amount to transfer    */   public void transfer(int from, int to, double amount) throws InterruptedException   {      bankLock.lock();      try      {         while (accounts[from] < amount)            sufficientFunds.await();         System.out.print(Thread.currentThread());         accounts[from] -= amount;         System.out.printf(" %10.2f from %d to %d", amount, from, to);         accounts[to] += amount;         System.out.printf(" Total Balance: %10.2f%n", getTotalBalance());         sufficientFunds.signalAll();      }      finally      {         bankLock.unlock();      }   }
这里使用一个锁来保护Bank中的transfer方法,模拟现实中转账操作。
       假定一个线程调用transfer时,在执行结束前被剥夺了运行权。假定第二个线程也调用transfer,由于第二个线程不能获得锁,将在调用lock方法时被阻塞。它必须等待第一个线程完成transfer方法的执行之后才能再度激活。当第一个线程释放锁时,那么第二个线程才可以运行。
锁是可重入的,因为线程可以重复地获得已经持有的锁。锁保持一个持有计数来跟踪对lock方法的嵌套调用。线程在每一次调用lock都要使用unlock来释放锁。由于这一特性,被一个锁保护的代码可以调用另一个使用相同的锁的方法。例如,当transfer方法调用getTotalBalance方法,这也会封锁bankLock对象,此时bankLock持有的计数为2.当getTotalBalance方法退出是,持有计数变为1.当transfer方法退出的时候,持有的计数变为0.线程释放锁。上面实例代码中调用的方法getTotalBalance与transfer方法使用的是同一个锁。
/**    * Gets the sum of all account balances.    * @return the total balance    */   public double getTotalBalance()   {      bankLock.lock();      try      {         double sum = 0;         for (double a : accounts)            sum += a;         return sum;      }      finally      {         bankLock.unlock();      }   }
另外,上面的案列使用显示锁Lock,同样可以用同步的方法来实现,将transfer方法用synchronized修饰,同样在transfer中调用的getTotalBalance也需要用synchronized来修饰。
/**    * Transfers money from one account to another.    * @param from the account to transfer from    * @param to the account to transfer to    * @param amount the amount to transfer    */   public synchronized void transfer(int from, int to, double amount) throws InterruptedException   {      while (accounts[from] < amount)         wait();      System.out.print(Thread.currentThread());      accounts[from] -= amount;      System.out.printf(" %10.2f from %d to %d", amount, from, to);      accounts[to] += amount;      System.out.printf(" Total Balance: %10.2f%n", getTotalBalance());      notifyAll();   }
/**    * Gets the sum of all account balances.    * @return the total balance    */   public synchronized double getTotalBalance()   {      double sum = 0;      for (double a : accounts)         sum += a;      return sum;   }
以上代码案列均来自于java核心技术卷。

 

转载于:https://www.cnblogs.com/jasongaoh/p/7834239.html

你可能感兴趣的文章
视频监控 封装[PlayCtrl.dll]的API
查看>>
软件工程APP进度更新
查看>>
Python 使用正则替换 re.sub
查看>>
CTF中那些脑洞大开的编码和加密
查看>>
c++ 调用外部程序exe-ShellExecuteEx
查看>>
IdentityServer流程图与相关术语
查看>>
BirdNet: a 3D Object Detection Framework from LiDAR information
查看>>
icon fonts入门
查看>>
【Django】如何按天 小时等查询统计?
查看>>
测试用例(一)
查看>>
【转】 mysql反引号的使用(防冲突)
查看>>
邮件中的样式问题
查看>>
AJAX 状态值与状态码详解
查看>>
php面向对象编程(oop)基础知识示例解释
查看>>
1.在数组中找到与给定总和的配对
查看>>
树的子结构
查看>>
关于根据Build Platform或者OS 加载x86或者x64 dll的问题
查看>>
程序员高效开发的几个技巧
查看>>
js-权威指南学习笔记19.2
查看>>
hexo 搭建博客
查看>>