上一节讲到Synchronized关键字,synchronized上锁的区域:对象锁=方法锁/类锁

本节补充介绍一下synchronized锁重入:

关键字synchronized拥有锁重入的功能,也就是在使用synchronized时,当一个线程得到一个对象锁后,再次请求此对象锁时,是可以再次得到该对象锁的。这也证明在一个synchronized方法/块内部调用本类的其他synchronized方法/块时,是永远可以得到锁的。

(写在例子前面一个维基百科关于可重入的概念)若一个程序或子程序可以“在任意时刻被中断然后操作系统调度执行另外一段代码,这段代码又调用了该子程序不会出错”,则称其为可重入(reentrant或re-entrant)的。即当该子程序正在运行时,执行线程可以再次进入并执行它,仍然获得符合设计时预期的结果。与多线程并发执行的线程安全不同,可重入强调对单个线程执行时重新进入同一个子程序仍然是安全的。通俗来说:当线程请求一个由其它线程持有的对象锁时,该线程会阻塞,而当线程请求由自己持有的对象锁时,如果该锁是重入锁,请求就会成功,否则阻塞。

public classService {synchronized public voidservice1(){

System.out.println("service1");

service2();

}synchronized public voidservice2(){

System.out.println("service2");

service3();

}synchronized public voidservice3(){

System.out.println("service3");

}

}public class MyThread extendsThread {

@Overridepublic voidrun(){

Service service= newService();

service.service1();

}

}public classRun {public static voidmain(String[] args) {

MyThread t= newMyThread();

t.start();

}

}

可重入锁的概念是自己可以再次获得自己的内部锁,比如有一个线程获得了某个对象的锁,此时这个对象的锁还没有释放,当他再次想要获得这个对象的锁的时候还是可以获取的,如果不可锁重入的话,就会造成死锁。

此时就会出现问题:对象的锁和类锁?区别是什么

1. 类锁:在代码中的方法上加了static和synchronized的锁,或者synchronized(xxx.class)的代码段;

2.对象锁:在代码中的方法上加了synchronized的锁,或者synchronized(this)的代码段;

对象锁也叫方法锁,是针对一个对象实例的,它只在该对象的某个内存位置声明一个标识该对象是否拥有锁,所有它只会锁住当前的对象,而并不会对其他对象实例的锁产生任何影响,不同对象访问同一个被synchronized修饰的方法的时候不会阻塞。

类锁是锁住整个类,当有多个线程来声明这个类的对象时候将会被阻塞,直到拥有这个类锁的对象被销毁或者主动释放了类锁,这个时候在被阻塞的线程被挑选出一个占有该类锁,声明该类的对象。其他线程继续被阻塞住,即一句话:不管多少个对象,多少个对象,共用一把锁,且只有一把,不管怎么调用,都会同步

这里介绍一下可重入锁与不可重入锁,首先列举一个不可重入锁的例子:

public classLock {private boolean isLocked = false;public synchronized void lock() throwsInterruptedException {while(isLocked) {

System.out.println("isLocked...");

wait();

}

isLocked= true;

System.out.println("Locked! ... ");

}public synchronized voidunlock() {

isLocked= false;

System.out.println("notify() !");

notify();

}

}public classCount {

Lock lock= newLock();public void print() throwsInterruptedException {

lock.lock();

doAdd();

lock.unlock();

}public void doAdd() throwsInterruptedException {

lock.lock();//do something

System.out.println("i'm doing something...");

lock.unlock();

}public static void main(String[] args) throwsInterruptedException {

Count c= newCount();

c.print();

}

}

运行结果为:

Locked!...

isLocked...

当调用print()方法时,获得了锁,这时就无法再调用doAdd()方法,这时必须先释放锁才能调用,所以称这种锁为不可重入锁,也叫自旋锁。

同一个类的多个方法被synchronized修饰,他们是同一把锁吗(针对上文代码中的service1 service2 service3之间的调用)

synchronized是Java中的关键字,是一种同步锁。修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;修饰方法时锁定的是调用该方法的对象,即如果一个对象中有两个方法同时被synchronized,则同一个对象,调用这两个方法时,只能同时执行一个。但它并不能使调用该方法的多个对象在执行顺序上互斥。

如以下代码,只能同时执行set方法或out方法:

classResoure{privateString name;private int count = 1;//生产者调用

public synchronized voidset(String name){this.name = name+count;//编号自增

count++;//打印生产了哪个产品

System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);

}//消费者调用

public synchronized voidout(){

System.out.println(Thread.currentThread().getName()+"...消费者..."+this.name);

}

}

再来一个可重入代码的对比:

public class Xttblog extendsSuperXttblog {public static voidmain(String[] args) {

Xttblog child= newXttblog();

child.doSomething();

}public synchronized voiddoSomething() {

System.out.println("child.doSomething()" +Thread.currentThread().getName());

doAnotherThing();//调用自己类中其他的synchronized方法

}private synchronized voiddoAnotherThing() {super.doSomething(); //调用父类的synchronized方法

System.out.println("child.doAnotherThing()" +Thread.currentThread().getName());

}

}classSuperXttblog {public synchronized voiddoSomething() {

System.out.println("father.doSomething()" +Thread.currentThread().getName());

}

}

输出结果为:

child.doSomething()Thread-5492father.doSomething()Thread-5492child.doAnotherThing()Thread-5492

现在可以验证出 synchronized 是可重入锁了吧!因为这些方法输出了相同的线程名称,表明即使递归使用synchronized也没有发生死锁,证明其是可重入的。

还看不懂?那我就再解释下!

这里的对象锁只有一个,就是 child 对象的锁,当执行 child.doSomething 时,该线程获得 child 对象的锁,在 doSomething 方法内执行 doAnotherThing 时再次请求child对象的锁,因为synchronized 是重入锁,所以可以得到该锁,继续在 doAnotherThing 里执行父类的 doSomething 方法时第三次请求 child 对象的锁,同样可得到。如果不是重入锁的话,那这后面这两次请求锁将会被一直阻塞,从而导致死锁。

所以在 java 内部,同一线程在调用自己类中其他 synchronized 方法/块或调用父类的 synchronized 方法/块都不会阻碍该线程的执行。就是说同一线程对同一个对象锁是可重入的,而且同一个线程可以获取同一把锁多次,也就是可以多次重入。因为java线程是基于“每线程(per-thread)”,而不是基于“每调用(per-invocation)”的(java中线程获得对象锁的操作是以线程为粒度的,per-invocation 互斥体获得对象锁的操作是以每调用作为粒度的)。

直接看结论:

从互斥锁的设计上来说,当一个线程试图操作一个由其他线程持有的对象锁的临界资源时,将会处于阻塞状态,但当一个线程再次请求自己持有对象锁的临界资源时,这种情况属于重入锁,请求将会成功,在java中synchronized是基于原子性的内部锁机制,是可重入的,因此在一个线程调用synchronized方法的同时在其方法体内部调用该对象另一个synchronized方法,也就是说一个线程得到一个对象锁后再次请求该对象锁,是允许的,这就是synchronized的可重入性。

可重入锁的实现原理?

看到这里,你终于明白了 synchronized 是一个可重入锁。但是面试官要再问你,可重入锁的原理是什么?

对不起,你又卡壳了。

可重入锁的原理。具体我们后面再写 ReentrantLock 的时候来验证或看它源码。

重入锁实现可重入性原理或机制是:每一个锁关联一个线程持有者和计数器,当计数器为 0 时表示该锁没有被任何线程持有,那么任何线程都可能获得该锁而调用相应的方法;当某一线程请求成功后,JVM会记下锁的持有线程,并且将计数器置为 1;此时其它线程请求该锁,则必须等待;而该持有锁的线程如果再次请求这个锁,就可以再次拿到这个锁,同时计数器会递增;当线程退出同步代码块时,计数器会递减,如果计数器为 0,则释放该锁。

java丐帮_java多线程学习笔记(四)相关推荐

  1. java丐帮_java多线程学习笔记(三)

    java多线程下的对象及变量的并发访问 上一节讲到,并发访问的时候,因为是多线程,变量如果不加锁的话,会出现"脏读"的现象,这个时候需要"临界区"的出现去解决多 ...

  2. java丐帮_Java多线程学习笔记(一)

    一.什么是多线程 首先是多线程的概念: 多线程是异步的,和单任务不同,并不一定按照代码的执行顺序(上图左)来运行,而是交错占用CPU运行(上图右): 二.如何使用多线程 JAVA多线程有两种实现方式: ...

  3. java丐帮_java多线程学习笔记(五)

    补充一个synchronized关键字的结论: 线程A先持有object对象的Lock锁,B线程可以以异步的方式调用object对象中的非synchronized类型的方法 A线程现持有object对 ...

  4. java丐帮_java多线程学习笔记(二)

    上一节讲到多线程的创建,两种创建方式一种继承Thread类,一种实现Runnable接口: 常用的多线程函数: currentThread()方法        返回代码段正在被哪个线程调用的信息 i ...

  5. java丐帮_java多线程学习笔记(八)

    本节开始线程间通信: 使用wait/notify实现线程间通信 生产者/消费者模式的实现 方法join的使用 ThreadLocal类的使用 可以通过使用 sleep() 结合 while(true) ...

  6. java丐帮_java多线程学习笔记(六)

    本节开始synchronized关键字的最后一部分内容: 静态同步方法synchronized方法与synchronized(class)代码块 关键字synchronized还可以用在static静 ...

  7. java 编程思想 多线程学习笔记

    java 编程思想 多线程学习笔记 一.如何创建多线程? 1.继承 java.lang.Thread 类 2.实现 java.lang.Runnable 接口 3.Callable接口 总之,在任何线 ...

  8. java线程集合点_Java多线程学习笔记(三) 甚欢篇

    使人有乍交之欢,不若使其无久处之厌 <小窗幽记>很多时候,我们需要的都不是再多一个线程,我们需要的线程是许多个,我们需要让他们配合.同时我们还有一个愿望就是复用线程,就是将线程当做一个工人 ...

  9. java 指针_java多线程学习二十二:::java中的指针

    在上面那个图,我们看到一个特殊的变量unsafe,它的包名是 sun.misc.Unsafe;从名字看,这个类应该是封装一些不安全的操作,为什么不安全?对c语言理解的朋友就知道了,指针是不安全的,在j ...

最新文章

  1. redis如何解决秒杀超卖java_Spring Boot + redis解决商品秒杀库存超卖,看这篇文章就够了...
  2. 优秀产品经理(CEO)必须get的财税知识
  3. 让ModalPopupExtender的控制控件能响应服务器事件
  4. 详解--单调队列 经典滑动窗口问题
  5. 解决mediawiki上传文件文件名是中文上传失败
  6. python遵循什么协议_《Python网络爬虫》2.3 Robots协议的遵守方式
  7. Reflector:.NET反编译工具及导出CS文件插件
  8. 软件测试教程从入门到精通
  9. 迅雷SVIP版(资源下载神器)官方中文版V11.1.2.1078 | 迅雷不限速版下载
  10. 北京航空航天大学计算机考研信息汇总
  11. 中证登 中债登 上清所 证券清算 证券账户 资金账户
  12. Window10 Excel复制粘贴卡死
  13. hdu 2586 How far away ? (LCA转RMQ)
  14. C#强密匙加密文件.snk
  15. Cocos2d-x 是一个支持多平台的 2D 手机游戏引擎
  16. VM是什么,干什么的
  17. 【bug】vue.runtime.esm.js?2b0e:619 [Vue warn]: Failed to mount component: template or render function
  18. 国内优秀的IC设计公司主要分布在哪些城市?
  19. 《数据结构与算法》(二十)- 散列表查找
  20. iOS 苹果所有设备的系统、屏幕尺寸和像素

热门文章

  1. Hive之bucket表使用场景
  2. 蓝牙地址解析(NAP/UAP/LAP)
  3. c4.5决策树算法python_Python3实现机器学习经典算法(四)C4.5决策树
  4. Unity ACT游戏相机逻辑
  5. HTML5期末大作业:商城网站设计——仿天猫在线商城(HTML和CSS实现天猫在线商城网站)...
  6. 简单编程---哥德巴赫猜想
  7. Nginx 搭建DASH服务器
  8. Siri语音控制树莓派开关灯 --智能家居
  9. java缺少方法主体_Java开发网 - 总是报错(缺少方法主体,或声明抽象)
  10. 微信公众号+获取文章内容【只是记录自己的学习过程】