java同步锁-详细易懂
目录
同步锁的引入:
无同步:
输出结果:
加同步锁:
输出结果:
解决办法:
方法一:
方法二:
输出结果:
同步锁机制:
同步锁机制:
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
这时一种非常典型的同步锁错误,以为自己加了锁,其实锁加在了各自对象的非静态方法上,即对同一个对象,这把锁才有用,我们创建了三个不同的对象,三个有各自的方法,此时锁方法无法锁住任何临界区。
解决办法:
- 让MyThread实现Runnable接口,只创建一个对象,将同一个对象作为参数传给Thread的构造器。
- 在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、如何解决
对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。即所有操作共享数据的这些语句都要放在同步范围中
切记:
范围太小:没锁住所有有安全问题的代码
范围太大:没发挥多线程的功能。
锁的释放:
释放锁的操作:
- 当前线程的同步方法、同步代码块执行结束。
- 当前线程在同步代码块、同步方法中遇到break、return终止了该代码块、该方法的继续执行。
- 当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束。
- 当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁。
不会释放锁的操作:
- 线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行,陈果老师说过,带锁睡眠是多线程编程的大忌
- 线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁(同步监视器)。
- 应尽量避免使用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
第二次判断是为了避免以下情况的发生。
- 假设:线程A已经经过第一次判断,判断singleton=null,准备进入同步代码块.
- 此时线程B获得时间片,犹豫线程A并没有创建实例,所以,判断singleton仍然=null,所以线程B创建了实例singleton。
- 此时,线程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 的对比:
- Lock是显式锁(手动开启和关闭锁,别忘记关闭锁),synchronized是隐式锁,出了作用域自动释放
- Lock只有代码块锁,synchronized有代码块锁和方法锁
- 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)所以优先使用顺序:Lock -> 同步代码块->同步方法
java同步锁-详细易懂相关推荐
- java同步锁售票_Java基础学习笔记: 多线程,线程池,同步锁(Lock,synchronized )(Thread类,ExecutorService ,Future类)(卖火车票案例)...
学习多线程之前,我们先要了解几个关于多线程有关的概念. 进程:进程指正在运行的程序.确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能. 线程:线程是 ...
- java同步锁实例_Java lock同步锁使用实例解析
这篇文章主要介绍了Java lock同步锁使用实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1)Lock是一个接口,而synchroniz ...
- java同步锁优化方案学习笔记(偏向锁,轻量级锁,自旋锁,重量级锁)
目录 一,概述 二,CAS算法 三,Java对象的对象头,以及Mark Word 四,偏向锁 Baised Lock 五,轻量级锁 六,自旋锁 SpinLock 七,重量级锁 八,在应用层提高锁效率的 ...
- java 同步锁_java线程中的同步锁和互斥锁有什么区别?
在java中,同步锁和互斥锁英文关键字都是Synchronized,没有本质上的区别,两者都包括对资源的独占,使用起来没有区别.概念上的区别是 1:互斥是通过竞争对资源的独占使用,彼此没有什么关系,执 ...
- 你真的知道Java同步锁何时释放?
在测试java多线程中有关 "生产者和消费者" 这个经典问题的时候,写代码测试的时候,思考到一些问题(所以还是要动手,实践才能储真知啊), synchronize 同步锁何时释放, ...
- Java同步锁——lock与synchronized 的区别【转】
在网上看来很多关于同步锁的博文,记录下来方便以后阅读 一.Lock和synchronized有以下几点不同: 1)Lock是一个接口,而synchronized是Java中的关键字,synchroni ...
- java 同步锁_死磕 java同步系列之自己动手写一个锁Lock
问题 (1)自己动手写一个锁需要哪些知识? (2)自己动手写一个锁到底有多简单? (3)自己能不能写出来一个完美的锁? 简介 本篇文章的目标一是自己动手写一个锁,这个锁的功能很简单,能进行正常的加锁. ...
- Java同步锁Synchronized底层源码和原理剖析
目录 1 synchronized场景回顾 2 反汇编寻找锁实现原理 3 synchronized虚拟机源码 3.1 HotSpot源码Monitor生成 3.2 HotSpot源码之Monitor竞 ...
- js模拟java同步锁
有这方面知识的朋友,看到题目会认为,你这样做没有意义的,因为script脚本永远不会产生并发.确实脚本执行是单线程的,即使有setTimeout.setInterval等方法,他也永远不会产生并发.所 ...
- java同步锁如何使用_java 同步锁(synchronized)的正确使用姿势
关于线程安全,线程锁我们经常会用到,但你的使用姿势正确不,反正我用错了好长一段时间而不自知.所以有了这篇博客总结下线程锁的正确打开姿势 废话不说看例子 一,对整个方法进行加锁 1,对整个方法进行加锁, ...
最新文章
- 剑指Offer系列 重建二叉树
- 米家扫拖一体机器人重置键_全面升级——米家扫拖机器人1T体验
- oracle估算大小,Oracle 估算數據庫大小的方法
- java登录界面命令_Java命令行界面(第29部分):自己动手
- php jquery 局部页面刷新,jQuery页面刷新(局部、全部)问题分析_jquery
- BCS冬奥主题活动日:奥运网络安全成全球关注焦点
- Linux系统给进程绑核
- 2019数据安装勾选_【在发票平台确认签名后,为什么在电子税务局增值税申报表上没有自动显示进项数据?】一张发票的“有效税额”是否可以分两个月抵扣?...
- Power Switching ----- Controlling power for power shutoff
- 什么是广域网WAN?学WAN,看这篇文章就够了
- embed预览pdf_09.html使用iframe、embed查看pdf不显示(未解决),使用pdf.js预览pdf
- CancelledError: [_Derived_]RecvAsync is cancelled.
- phpstorm+xdebug远程调试
- 短视频平台开发VS直播平台开发,未来发展趋势
- 让程序员工资随年龄增长递减更合理?
- 【公钥密码】ECC椭圆密码体制 (实现Elgamal加密方法)
- 实战丨Halo-轻松部署属于自己的博客系统
- 下载vue模板框架并使用
- v93000测试系统软件,V93000自动测试系统
- 一张图理解非对称加密解密过程
热门文章
- 有限差分法下-一维中的显示有限差分(python)
- 大学BBS年度十大原创淡黄笑话
- 【转】win7旗舰版英文版下载(64位|32位)|Windows7英文版ISO镜像
- 买电梯房几楼才是最好 几个最差楼层千万不能选
- 基于Struts开发网上商城购物系统
- zendstudio html插件,ZendStudio安装Aptana插件(html,css,js代码提示功能)_html/css_WEB-ITnose...
- mysql 8.0 手册
- 联想ThinkPad E420安装7450M的显卡驱动后进入不了系统,试了4个系统都不..
- 文本聚类 java_【Java】文本聚类
- IDEA中配置OpenJDK8并查看native方法源码