目录

同步锁的引入:

无同步:

输出结果:

加同步锁:

输出结果:

解决办法:

方法一:

方法二:

输出结果:

同步锁机制:

同步锁机制:

synchronized的锁是什么?

注意:

同步的范围:

1、代码是否存在线程安全

2、如何解决

切记:

锁的释放:

释放锁的操作:

不会释放锁的操作:

单例模式-懒汉式-双重加锁校验:

第一次判断singleton是否为null

第二次判断singleton是否为null

线程的死锁问题:

死锁:

产生死锁的四个必要条件

解决方法:

Lock锁:

synchronized 与 Lock 的对比:


同步锁的引入:

java中cpu分给每个线程的时间片是随机的并且在java中好多都是多个线程共用一个资源,比如火车卖票,火车票是一定的,但卖火车票的窗口到处都有,每个窗口就相当于一个线程,这么多的线程共用所有的火车票这个资源。如果在一个时间点上,两个线程同时使用这个资源,那他们取出的火车票是一样的(座位号一样),这样就会给乘客造成麻烦。比如下面程序:

无同步:

public class Main {public static void main(String[] args) {MyThread thread1 = new MyThread();MyThread thread2 = new MyThread();MyThread thread3 = new MyThread();thread1.setName("线程一");thread2.setName("线程二");thread3.setName("线程三");thread1.start();thread2.start();thread3.start();}
}class MyThread extends Thread{static int  tick = 10;public MyThread() {}@Overridepublic void run() {while (true) {if (tick > 0) {System.out.println(Thread.currentThread().getName()+" 剩余tick:"+--tick);}}}
}

输出结果:

线程一 剩余tick:9

线程一 剩余tick:9

线程一 剩余tick:5

线程一 剩余tick:4

线程一 剩余tick:3

线程二 剩余tick:7

线程二 剩余tick:6

线程三 剩余tick:8

线程二 剩余tick:2

线程一 剩余tick:1

可以看到票数不是按照顺序减少,票刚开始卖时,有两个线程同时进入临界区,导致票买了两张,总数却只减少了一张。

加同步锁:

public class Main {public static void main(String[] args) {MyThread thread1 = new MyThread();MyThread thread2 = new MyThread();MyThread thread3 = new MyThread();thread1.setName("线程一");thread2.setName("线程二");thread3.setName("线程三");thread1.start();thread2.start();thread3.start();}
}class MyThread extends Thread{static int  tick = 10;public MyThread() {}@Overridepublic synchronized void run() {while (true) {if (tick > 0) {System.out.println(Thread.currentThread().getName()+" 剩余tick:"+--tick);}}}
}

输出结果:

线程一 剩余tick:9

线程一 剩余tick:6

线程一 剩余tick:5

线程一 剩余tick:4

线程一 剩余tick:3

线程二 剩余tick:8

线程二 剩余tick:1

线程三 剩余tick:7

线程二 剩余tick:0

线程一 剩余tick:2

这时一种非常典型的同步锁错误,以为自己加了锁,其实锁加在了各自对象的非静态方法上,即对同一个对象,这把锁才有用,我们创建了三个不同的对象,三个有各自的方法,此时锁方法无法锁住任何临界区。

解决办法:

  1. 让MyThread实现Runnable接口,只创建一个对象,将同一个对象作为参数传给Thread的构造器。
  2. 在run方法中用MyThread.class锁住代码块。

方法一:

public class Main {public static void main(String[] args) {MyThread mt = new MyThread();//只创建一个MyThread对象Thread thread1 = new Thread(mt);Thread thread2 = new Thread(mt);Thread thread3 = new Thread(mt);thread1.setName("线程一");thread2.setName("线程二");thread3.setName("线程三");thread1.start();thread2.start();thread3.start();}
}class MyThread implements Runnable{//实现Runnable接口static int  tick = 10;public MyThread() {}@Overridepublic synchronized void run() {while (true) {if (tick > 0) {System.out.println(Thread.currentThread().getName()+" 剩余tick:"+--tick);}}}
}

方法二:

public class Main {public static void main(String[] args) {MyThread thread1 = new MyThread();MyThread thread2 = new MyThread();MyThread thread3 = new MyThread();thread1.setName("线程一");thread2.setName("线程二");thread3.setName("线程三");thread1.start();thread2.start();thread3.start();}
}class MyThread extends Thread{static int  tick = 10;public MyThread() {}@Overridepublic  void run() {while (true) {synchronized (this.getClass()) {if (tick > 0) {System.out.println(Thread.currentThread().getName()+" 剩余tick:"+--tick);}}}}
}

输出结果:

线程一 剩余tick:9

线程一 剩余tick:8

线程一 剩余tick:7

线程一 剩余tick:6

线程一 剩余tick:5

线程一 剩余tick:4

线程一 剩余tick:3

线程一 剩余tick:2

线程一 剩余tick:1

线程一 剩余tick:0

如此这般,票数才能按照顺序减少,而不会一个消费者看到窗口还有八张票,另一个消费者看到还有2张票。也不会出现两个消费者同时买到同一张票

同步锁机制:

同步锁机制:

同步机制中的锁在《Thinking in Java》中,是这么说的:对于并发工作,你需要某种方式来防止两个任务访问相同的资源(其实就是共享资源竞争)。 防止这种冲突的方法就是当资源被一个任务使用时,在其上加锁。第一个访问某项资源的任务必须锁定这项资源,使其他任务在其被解锁之前,就无法访问它了,而在其被解锁之时,另一个任务就可以锁定并使用它了。

synchronized的锁是什么?

任意对象都可以作为同步锁。所有对象都自动含有单一的锁(监视器)。

同步方法的锁:静态方法(类名.class)、非静态方法(this)

同步代码块:自己指定,很多时候也是指定为this或类名.class

注意:

必须确保使用同一个资源的多个线程共用一把锁,这个非常重要,否则就无法保证共享资源的安全

一个线程类中的所有静态方法共用同一把锁(类名.class),所有非静态方法共用同一把锁(this),同步代码块(指定需谨慎)

同步的范围:

1、代码是否存在线程安全

(1)明确哪些代码是多线程运行的代码,如窗口

(2)明确多个线程是否有共享数据,如车票

(3)明确多线程运行代码中是否有多条语句操作共享数据,run方法

2、如何解决

对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。即所有操作共享数据的这些语句都要放在同步范围中

切记:

范围太小:没锁住所有有安全问题的代码

范围太大:没发挥多线程的功能。

锁的释放:

释放锁的操作:

  1. 当前线程的同步方法、同步代码块执行结束。
  2. 当前线程在同步代码块、同步方法中遇到break、return终止了该代码块、该方法的继续执行。
  3. 当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束。
  4. 当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁。

不会释放锁的操作:

  1. 线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行,陈果老师说过,带锁睡眠是多线程编程的大忌
  2. 线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁(同步监视器)。
  3. 应尽量避免使用suspend()和resume()来控制线程

单例模式-懒汉式-双重加锁校验:

class Singleton {private static Singleton instance = null;private Singleton(){//私有化构造方法}public static Singleton getInstance(){if(instance==null){synchronized(Singleton.class){if(instance == null){instance=new Singleton();} } }return instance;}
}

第一次判断singleton是否为null

  第一次判断是在Synchronized同步代码块外进行判断,由于单例模式只会创建一个实例,并通过getInstance方法返回singleton对象,所以,第一次判断,是为了在singleton对象已经创建的情况下,避免进入同步代码块,提升效率。

第二次判断singleton是否为null

第二次判断是为了避免以下情况的发生。

  1. 假设:线程A已经经过第一次判断,判断singleton=null,准备进入同步代码块.
  2. 此时线程B获得时间片,犹豫线程A并没有创建实例,所以,判断singleton仍然=null,所以线程B创建了实例singleton。
  3. 此时,线程A再次获得时间片,犹豫刚刚经过第一次判断singleton=null(不会重复判断),进入同步代码块,这个时候,我们如果不加入第二次判断的话,那么线程A又会创造一个实例singleton,就不满足我们的单例模式的要求,所以第二次判断是很有必要的。

线程的死锁问题:

final StringBuffer s1 = new StringBuffer();
final StringBuffer s2 = new StringBuffer();
new Thread() {//匿名内部类@Overridepublic void run() {synchronized (s1) {s2.append("A");synchronized (s2) {s2.append("B");System.out.print(s1);System.out.print(s2);}}}
}.start();
new Thread() {//匿名内部类@Overridepublic void run() {synchronized (s2) {s2.append("C");synchronized (s1) {s1.append("D");System.out.print(s2);System.out.print(s1);}}}
}.start();

死锁:

不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续

产生死锁的四个必要条件

1.互斥性:线程对资源的占有是排他性的,一个资源只能被一个线程占有,直到释放。

2.请求和保持条件:一个线程对请求被占有资源发生阻塞时,对已经获得的资源不释放。

3.不剥夺:一个线程在释放资源之前,其他的线程无法剥夺占用。

4.循环等待:发生死锁时,线程进入死循环,永久阻塞。

解决方法:

专门的算法、原则

尽量减少同步资源的定义

尽量避免嵌套同步

Lock锁:

从JDK 5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当。

java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。

ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁。

class A{private final ReentrantLock lock = new ReentrantLock();public void m(){lock.lock();try{//保证线程安全的代码;}finally{//注意:如果同步代码有异常,要将unlock()写入finally语句块lock.unlock();}}
}

synchronized 与 Lock 的对比:

  1. Lock是显式锁(手动开启和关闭锁,别忘记关闭锁),synchronized是隐式锁,出了作用域自动释放
  2. Lock只有代码块锁,synchronized有代码块锁和方法锁
  3. 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)所以优先使用顺序:Lock -> 同步代码块->同步方法

java同步锁-详细易懂相关推荐

  1. java同步锁售票_Java基础学习笔记: 多线程,线程池,同步锁(Lock,synchronized )(Thread类,ExecutorService ,Future类)(卖火车票案例)...

    学习多线程之前,我们先要了解几个关于多线程有关的概念. 进程:进程指正在运行的程序.确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能. 线程:线程是 ...

  2. java同步锁实例_Java lock同步锁使用实例解析

    这篇文章主要介绍了Java lock同步锁使用实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1)Lock是一个接口,而synchroniz ...

  3. java同步锁优化方案学习笔记(偏向锁,轻量级锁,自旋锁,重量级锁)

    目录 一,概述 二,CAS算法 三,Java对象的对象头,以及Mark Word 四,偏向锁 Baised Lock 五,轻量级锁 六,自旋锁 SpinLock 七,重量级锁 八,在应用层提高锁效率的 ...

  4. java 同步锁_java线程中的同步锁和互斥锁有什么区别?

    在java中,同步锁和互斥锁英文关键字都是Synchronized,没有本质上的区别,两者都包括对资源的独占,使用起来没有区别.概念上的区别是 1:互斥是通过竞争对资源的独占使用,彼此没有什么关系,执 ...

  5. 你真的知道Java同步锁何时释放?

    在测试java多线程中有关 "生产者和消费者" 这个经典问题的时候,写代码测试的时候,思考到一些问题(所以还是要动手,实践才能储真知啊), synchronize 同步锁何时释放, ...

  6. Java同步锁——lock与synchronized 的区别【转】

    在网上看来很多关于同步锁的博文,记录下来方便以后阅读 一.Lock和synchronized有以下几点不同: 1)Lock是一个接口,而synchronized是Java中的关键字,synchroni ...

  7. java 同步锁_死磕 java同步系列之自己动手写一个锁Lock

    问题 (1)自己动手写一个锁需要哪些知识? (2)自己动手写一个锁到底有多简单? (3)自己能不能写出来一个完美的锁? 简介 本篇文章的目标一是自己动手写一个锁,这个锁的功能很简单,能进行正常的加锁. ...

  8. Java同步锁Synchronized底层源码和原理剖析

    目录 1 synchronized场景回顾 2 反汇编寻找锁实现原理 3 synchronized虚拟机源码 3.1 HotSpot源码Monitor生成 3.2 HotSpot源码之Monitor竞 ...

  9. js模拟java同步锁

    有这方面知识的朋友,看到题目会认为,你这样做没有意义的,因为script脚本永远不会产生并发.确实脚本执行是单线程的,即使有setTimeout.setInterval等方法,他也永远不会产生并发.所 ...

  10. java同步锁如何使用_java 同步锁(synchronized)的正确使用姿势

    关于线程安全,线程锁我们经常会用到,但你的使用姿势正确不,反正我用错了好长一段时间而不自知.所以有了这篇博客总结下线程锁的正确打开姿势 废话不说看例子 一,对整个方法进行加锁 1,对整个方法进行加锁, ...

最新文章

  1. 剑指Offer系列 重建二叉树
  2. 米家扫拖一体机器人重置键_全面升级——米家扫拖机器人1T体验
  3. oracle估算大小,Oracle 估算數據庫大小的方法
  4. java登录界面命令_Java命令行界面(第29部分):自己动手
  5. php jquery 局部页面刷新,jQuery页面刷新(局部、全部)问题分析_jquery
  6. BCS冬奥主题活动日:奥运网络安全成全球关注焦点
  7. Linux系统给进程绑核
  8. 2019数据安装勾选_【在发票平台确认签名后,为什么在电子税务局增值税申报表上没有自动显示进项数据?】一张发票的“有效税额”是否可以分两个月抵扣?...
  9. Power Switching ----- Controlling power for power shutoff
  10. 什么是广域网WAN?学WAN,看这篇文章就够了
  11. embed预览pdf_09.html使用iframe、embed查看pdf不显示(未解决),使用pdf.js预览pdf
  12. CancelledError: [_Derived_]RecvAsync is cancelled.
  13. phpstorm+xdebug远程调试
  14. 短视频平台开发VS直播平台开发,未来发展趋势
  15. 让程序员工资随年龄增长递减更合理?
  16. 【公钥密码】ECC椭圆密码体制 (实现Elgamal加密方法)
  17. 实战丨Halo-轻松部署属于自己的博客系统
  18. 下载vue模板框架并使用
  19. v93000测试系统软件,V93000自动测试系统
  20. 一张图理解非对称加密解密过程

热门文章

  1. 有限差分法下-一维中的显示有限差分(python)
  2. 大学BBS年度十大原创淡黄笑话
  3. 【转】win7旗舰版英文版下载(64位|32位)|Windows7英文版ISO镜像
  4. 买电梯房几楼才是最好 几个最差楼层千万不能选
  5. 基于Struts开发网上商城购物系统
  6. zendstudio html插件,ZendStudio安装Aptana插件(html,css,js代码提示功能)_html/css_WEB-ITnose...
  7. mysql 8.0 手册
  8. 联想ThinkPad E420安装7450M的显卡驱动后进入不了系统,试了4个系统都不..
  9. 文本聚类 java_【Java】文本聚类
  10. IDEA中配置OpenJDK8并查看native方法源码