Java锁的知识总结及实例代码_第1页
Java锁的知识总结及实例代码_第2页
Java锁的知识总结及实例代码_第3页
Java锁的知识总结及实例代码_第4页
Java锁的知识总结及实例代码_第5页
已阅读5页,还剩2页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、java锁的知识总结及实例代码这篇文章主要介绍了 java锁的知识总结及实例代码,需要的朋友可以参考下java中有哪些锁这个问题在我看了一遍vjava并发编程后尽然无法回答,说明自己对于锁的概念了解的不 够。于是再次翻看了一下书里的内容,突然有点打开脑门的感觉。看来确实是要学习的最好 方式是要带着问题去学,并且解决问题。在java屮锁主要两类:内部锁synchronized和显示锁java.util.concurrent.locks.locko但细细 想这貌似总结的也不太对。应该是由java内置的锁和concurrent实现的一系列锁。为什么这说,因为在java中一切都是对象,而java对每个

2、对象都内置了一个锁,也可以称 为对象锁/内部锁。通过synchronized来完成相关的锁操作。而因为synchronized的实现有些缺陷以及并发场景的复杂性,有人开发了一种显式的锁,而 这些锁都是由java.util.concurrent.locks.lock派牛出來的。当然目前己经内置到了 jdk1.5及之 后的版本中。syn chr on ized首先来看看用的比较多的synchronized,我的口常工作中大多用的也是它。synchronized是 用于为某个代码块的提供锁机制,在java的对象中会隐式的拥有一个锁,这个锁被称为内 置锁(intrinsic)或监视器锁(monitor

3、 locks)o线程在进入被synchronized保护的块z前自动获得 这个锁,直到完成代码后(也可能是异常)自动释放锁。内置锁是互斥的,一个锁同时只能被 一个线稈持有,这也就会导致多线程下,锁被持有后后面的线程会阻塞。正因此实现了对代 码的线程安全保证了原子性。可重入既然java内置锁是互斥的而且后面的线程会导致阻塞,那么如杲持有锁的线程再次进入试 图获得这个锁时会如何呢?比如下面的一种情况:public class baseclass public synchronized void do() system.out.println("is base"); public

4、 class sonclass exte nds baseclass public syn chronized void do() system.out.println("is son”); super.do(); sonclass son = new sonclass();son.do();此时派生类的do方法除了会首先会持有一次锁,然后在调用super.dof)的时候又会再一次进 入锁并去持有,如果锁是互斥的话此时就应该死锁了。但结果却不是这样的,这是因为内部锁是具有可重入的特性,也就是锁实现了一个重入机制, 引用计数管理。当线程1持有了对象的锁a,此时会对锁a的引用计算加1。然

5、后当线程1 再次获得锁a时,线程1还是持有锁a的那么计算会加1。当然每次退出同步块吋会减1, 直到为0吋释放锁。synchronized 的一些特点修饰代码的方式修饰方法public class baseclass public synchronized void do() system.out.println(”is base"); 这种就是直接对某个方法进行加锁,进入这个方法块吋需要获得锁。修饰代码块public class baseclass private static object lock = new object(); public void do() synchron

6、ized (lock) system.out.println("is base"); 这里就将锁的范围减少到了方法中的部分代码块,这对于锁的灵活性就提高了,毕竟锁的粒 度控制也是锁的一个关键问题。对彖锁的类型经常看到一些代码中对synchronized使用比较特别,看一下如下的代码:public class baseclass private static object lock = new object(); public void do() synchronized (lock) public synchronized void dovoid() publicsynch

7、ronized static void dostaticvoid() public static void dostaticvoid()synchronized (baseclass.class) 这里出现了四种情况:修饰代码块,修饰了方法,修饰了静态方法,修饰baseclass的class 对象。那这几种情况会有什么不同呢?修饰代码块这种情况下我们创建了一个对象lock,在代码中使用synchronized(lock)这种形式,它的意思 是使用lock这个对象的内置锁。这种情况下就将锁的控制交给了一个对象。当然这种情况 还有一种方式:public void do() synchronized

8、 (this) system.out.println("is base"); 使用this的意思就是当前对象的锁。这里也道出了内置锁的关键,我提供一把锁来保护这块 代码,无论哪个线程来都面对同一把锁咯。修饰对象方法这种直接修饰在方法是咱个情况?其实和修饰代码块类似,只不过此时默认使用的是this, 也就是当前对象的锁。这样写起代码來倒也比较简单明确。前面说过了与修饰代码块的区别 主要还是控制粒度的区别。修饰静态方法静态方法难道有啥不一样吗?确实是不一样的,此时获取的锁已经不是this 了,而this对象 指向的class,也就是类锁。因为java中的类信息会加载到方法常量区

9、,全局是唯一的。这 其实就提供了一种全局的锁。修饰类的class对象这种情况其实和修改静态方法时比较类似,只不过还是一个道理这种方式可以提供更灵活的 控制粒度。小结通过这儿种情况的分析与理解,其实可以看内置锁的主要核心理念就是为一块代码提供一个 可以用于互斥的锁,起到类似于开关的功能。java中对内置锁也提供了一些实现,主要的特点就是java都是对象,而每个对象都有锁,所 以可以根据情况选择用什么样的锁。java.util.c on curre nto cks.lock前而看了 synchronized.大部分的情况下差不多就够啦,但是现在系统在并发编程中复朵性 是越来越高,所以总是有许多场景

10、synchronized处理起来会比较费劲。或者像vjava并发编 程中说的那样,concurrent中的lock是对内部锁的一种补充,提供了更多的一些高级特性。java.util.concurrentocks.lock 简单分析这个接口抽象了锁的主要操作,也因此让从lock派生的锁具备了这些基本的特性:无条件 的、可轮循的、定时的、可中断的。而且加锁与解锁的操作都是显式进行。下面是它的代码:public in terface lock void lock(); void lockl nterruptibly() throws in terruptedexception; boolean tr

11、ylock(); boolean trylock(long time, timellnit unit) throws interruptedexception; void unlock(); condition newcondition();ree ntran tlockreentrantlock就是可重入锁,连名字都这么显式。reentrantlock提供了和synchronized类似 的语义,但是reentrantlock必须显式的调用,比如:public class baseclass private lock lock = new reentrantlock();public voi

12、d do()lock.lock(); try /. finally lock.unlockf); 这种方式对于代码阅读来说还是比较清楚的,只不过有个问题,就是如果忘了加try finally 或忘了写lock.unlock()的话导致锁没释放,很有可能导致一些死锁的情况,synchronized就 没有这个风险。trylockreentrantlock是实现lock接口,所以自然就拥有它的那些特性,其中就有trylock« trylock 就是尝试获取锁,如杲锁已经被其他线程占用那么立即返回false,如杲没有那么应该占用 它并返回true,表示拿到锁啦。另一个trylock方法里带

13、了参数,这个方法的作用是指定一个时间,表示在这个时间内一直 尝试去获得锁,如果到时间还没有拿到就放弃。因为trylock对锁并不是一直阻塞等待的,所以可以更多的规避死锁的发生。locklnterruptiblylocklnterruptibly是在线程获取锁时优先响应屮断,如果检测到中断抛出中断异常由上层代码 去处理。这种情况下就为一种轮循的锁提供了退出机制。为了更好理解可中断的锁操作,写 了一个demo来理解。package com.test; import java.util.date;import java.util.concurrentocks.reentrantlock; publi

14、c class testlocklnterruptibly static reentrantlock lock = new reentrantlock(); public static void main(stri ng args) thread threadl = new thread (new runn able() override publicvoid run() try doprint("thread 1 get lock."); dol23(); doprint("thread 1 end.")/ catch (interruptedexce

15、ption e) doprint("thread 1 is interrupted."); );thread thread2 = new thread (new runn able() override public void run() try doprintf'thread 2 get lock."); dol23(); doprint("thread 2 end."); catch(interruptedexception e) dopri nt(”thread 2 is in terrupted."); );threa

16、dl.setname(hthreadl"); thread2.setname("thread2n); threadl.start(); try thread.sleep(loo);/ 等待一会使得 threadl 会在 thread2 前面执行 catch (interruptedexception e) e.printstacktrace(); thread2.start(); private static void dol23() throwsin terruptedexcepti onlockockln terruptibly();doprint cthread.cu

17、rrentthread().getname()+"islocked.");try doprint(thread.currentthread().getname() + " dosomingl.thread.sleep(5000);/等待几秒方便查看线程的先后顺序 doprint(thread.currentthread().getname() + " dosoming2."); doprintcthread.currentthread().getname() + " is finished."); finally lock.

18、unlock); private static void doprint(string text) system.out.println(new date().tolocalestring() + " : " + text);上面代码屮有两个线程,threadl比thread2更早启动,为了能看到拿锁的过程将上锁的代 码sleep 7 5秒钟,这样就可以感受到前后两个线程进入获取锁的过程。最终上面的代码运 行结果如下:2016-9-28 15:12:56 : thread 1 get lock.2016-9-28 15:12:562016-9-28 15:12:562016

19、-9-28 15:12:562016-9-28 15:13:012016-9-28 15:13:012016-9-28 15:13:012016-9-28 15:13:012016-9-28 15:13:012016-9-28 15:13:012016-9-28 15:13:062016-9-28 15:13:062016-9-28 15:13:062016-9-28 15:13:06threadl is locked, threadl dosomingl. thread 2 get lock.threadl dosoming2. threadl is finished, threadl is

20、 unloaded. thread2 is locked.thread2 dosomingl. thread 1 end.thread2 dosoming2. thread2 is finished.thread2 is unloaded, thread 2 end.可以看到,threadl先获得锁,一会thread2也来拿锁,但这个时候threadl己经占用了,所 以thread2 一直到threadl释放了锁后才拿到锁。*这段代码说明locklnterruptibly后面来获取锁的线程需要等待前血的锁释放了才能获得锁。 和但这里还没有体现出可中断的待点,为此增加一些代码:thread2.s

21、tart();try thread.sleep(1000); catch (interruptedexception e) e.printstacktrace(); /i 秒后把线程 2 中断 errupt();在thread2启动后调用一下thread2的中断方法,好吧,先跑一下代码看看结果:2016-9-28 15:16:46 : thread 1 get lock.2016-9-28 15:16:46 : threadl is locked.2016-9-28 15:16:46 : threadl dosomingl.2016-9-28 15:16:46 : thr

22、ead 2 get lock.2016-9-28 15:16:47 : thread 2 is interrupted. v-直接就响应了线程中断2016-9-28 15:16:51: threadl dosoming2.2016-9-28 15:16:51: threadl is finished.2016-9-28 15:16:51: threadl is unloaded.2016-9-28 15:16:51: thread 1 end.和前面的代码相比可以发现,thread2正在等待threadl释放锁,但是这时thread2自己中 断了,thread2后面的代码则不会再继续执行。re

23、adwritelock顾名思义就是读写锁,这种读写锁的应用场景可以这样理解,比如一波数据大部分时候都 是提供读取的,而只有比较少量的写操作,那么如果用互斥锁的话就会导致线程间的锁竞争。 如果对于读取的吋候大家都可以读,一旦要写入的吋候就再将某个资源锁住。这样的变化就 很好的解决了这个问题,使的读操作可以提高读的性能,又不会影响写的操作。一个资源可以被多个读者访问,或者被一个写者访问,两者不能同时进行。这是读写锁的抽象接口,定义一个读锁和一个写锁。public interface readwritelock /* * returns the lock used for reading. * *

24、return the lock used for reading */ lock readlock(); /* * returns the lock used for writing.* return the lock used for writing */ lock writelock();在 jdk 里有个 reentrantreadwritelock 实现,就是可重入的读写锁。reentrantreadwritelock 可以构造为公平的或者非公平的两种类型。如果在构造时不显式指定则会默认的创建非公平 锁。在非公平锁的模式下,线程访问的顺序是不确定的,就是可以闯入;可以由写者降级为 读者,但是读者不能升级为写者。如果是公平锁模式,那么选择权交给等待时间最长的线程,如果一个读线程获得锁,此时一 个写线程请求写入锁,那么就不再接收读锁的获取,直到写入操作完成。简单的代码分析 在reentrantreadwritelock里其实维护的是一个sync的锁,只是看起来语 义上像是一个读锁和写锁。看一下它的构造函数:public reentrantreadwritelock(boolean fair) sync = fair ? new fairsync() : new nonfairsync(); readerlock = new readlock(this); w

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论