苟哥的笔记本
首页
文章归档
关于
文章归档
关于
首页
编程
正文
讨论多线程synchronized的用法
苟哥
2019-12-29 PM
1000℃
0条
## 概念 synchronized 是 Java 中的关键字,利用锁的机制来实现同步的。 锁机制有如下两种特性: - 互斥性:即在同一时间只允许一个线程持有某个对象锁,通过这种特性来实现多线程中的协调机制。只有这样,在同一时间只有一个线程对需同步的代码块(复合操作)进行访问。互斥性我们通常称为操作的原子性。 - 可见性:必须确保在锁被释放之前,对共享变量所做的修改,对于随后获得该锁的另一个线程是可见的(即在获得锁时应获得最新共享变量的值),否则另一个线程可能是在本地缓存的某个副本上继续操作从而引起不一致,产生所谓的“脏数据”。 ## synchronized修饰的对象 - 修饰一个类: - 作用范围: synchronized后面括号括起来的部分 - 作用对象: 这个类的所有对象 - 修饰一个方法:被修饰的方法称为同步方法 - 作用范围:整个方法 - 作用对象:调用这个方法的对象 - 修饰一个静态的方法: - 作用范围:整个方法 - 作用对象:这个类的所有对象; - 修饰一个代码块:被修饰的代码块称为同步语句块 - 作用范围:大括号{}括起来的代码块 - 作用对象:调用这个代码块的对象; ## 实验验证 #### - 验证修饰一个类,代码如下: ```java package reflection; public class TestReflection { public static void main(String[] args) { testSynchronized(); } /** * 多线程synchronized演示 */ public static void testSynchronized() { //创建线程1 Thread t1 = new Thread() { public void run() { new TestReflection().method1(); } }; t1.setName("t1"); t1.start(); //主线程睡眠1秒,给线程1足够时间运行,清除干扰 try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //创建线程2 Thread t2 = new Thread() { public void run() { new TestReflection().method1(); } }; t2.setName("t2"); t2.start(); } /** * 模拟线程执行某个任务 */ public void method1(){ System.out.println(Thread.currentThread().getName() + "占用资源"); synchronized(TestReflection.class){ try { System.out.println("method1休眠5秒"); Thread.sleep(5000); //假设任务执行5秒钟,增强实验效果 System.out.println("method1休眠完毕"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } ``` 运行代码,执行结果如下图1: ![](http://images.kuryun.com/blog/typecho/1577626147.png) 图1 分析代码:按照代码的执行顺序,理论上的打印顺序是: 线程t1启动后,执行method1方法,依次打印“t1占用资源”、“method1休眠5秒”,由于synchronized的修饰作用,此时**线程1占据了TestReflection类中synchronized修饰的那部分代码**;大概在1秒后t2启动,也执行了method1的部分代码(synchronized之前的部分),因此会打印“t2占用资源”,因为此时t1的任务(睡眠5秒)还未完成,因此这是t2无法执行synchronized后面的代码段;t1任务执行完后,会打印“method1休眠完毕”,同时**释放TestReflection类中synchronized修饰的那部分代码**,t1线程结束。此时,正在等待中的**线程t2就接管了TestReflection类中synchronized修饰的那部分代码**,同样是打印“method1休眠5秒”,等待5秒后,打印“method1休眠完毕”,此时线程t2也结束了。 理论分析结果和实验结果对比是一致的。 #### - 验证修饰一个方法,代码如下 ```java package reflection; public class TestReflection { public static void main(String[] args) { testSynchronized(); } /** * 多线程synchronized演示 */ public static void testSynchronized() { //创建线程1 Thread t1 = new Thread() { public void run() { new TestReflection().method1(); } }; t1.setName("t1"); t1.start(); //主线程睡眠1秒,给线程1足够时间运行,清除干扰 try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //创建线程2 Thread t2 = new Thread() { public void run() { new TestReflection().method1(); } }; t2.setName("t2"); t2.start(); } /** * 模拟线程执行某个任务 */ public synchronized void method1(){ String tName = Thread.currentThread().getName(); System.out.println(tName + "占用资源"); try { System.out.println(tName + "休眠5秒"); Thread.sleep(5000); //假设任务执行5秒钟,增强实验效果 System.out.println(tName + "休眠完毕"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } ``` 运行代码,执行结果如下图2: ![](http://images.kuryun.com/blog/typecho/1577627839.png) 图2 分析代码:按照代码的执行顺序,理论上的打印顺序是: 线程t1启动后,执行method1方法,依次打印“t1占用资源”、“t1休眠5秒”,过了1秒后,会打印“t2占用资源”、“t2休眠5秒”。这是因为synchronized修饰的是普通方法method1,因此**作用的对象只是当前对象**,t1和t2 分别是定义TestReflection的不同对象来调用method1的,因此t1锁method1时,对t2中的对象并无影响。实际上,t2的执行对比t1,只有在最初的时候延迟了1秒,本质上是互不干扰地执行了任务。 理论分析结果和实验结果对比是一致的。 #### - 验证修饰一个静态的方法,代码如下 ```java package reflection; public class TestReflection { public static void main(String[] args) { testSynchronized(); } /** * 多线程synchronized演示 */ public static void testSynchronized() { //创建线程1 Thread t1 = new Thread() { public void run() { method1(); } }; t1.setName("t1"); t1.start(); //主线程睡眠1秒,给线程1足够时间运行,清除干扰 try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //创建线程2 Thread t2 = new Thread() { public void run() { method1(); } }; t2.setName("t2"); t2.start(); } /** * 模拟线程执行某个任务 */ public synchronized static void method1(){ String tName = Thread.currentThread().getName(); System.out.println(tName + "占用资源"); try { System.out.println(tName + "休眠5秒"); Thread.sleep(5000); //假设任务执行5秒钟,增强实验效果 System.out.println(tName + "休眠完毕"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } ``` 运行代码,执行结果如下图3: ![](http://images.kuryun.com/blog/typecho/1577628515.png) 图3 分析代码:按照代码的执行顺序,理论上的打印顺序是: 线程t1启动后,执行method1方法,依次打印“t1占用资源”、“t1休眠5秒”,**由于synchronized的作用,此时TestReflection类的所有对象将被锁住,直到t1完成任务并释放锁**。因此直到打印“t1休眠完毕”后,才轮到t2的相关打印。 理论分析结果和实验结果对比是一致的。 #### - 验证修饰一个代码块 这个无需新代码,直接看验证修饰一个类处的代码和结果即可看出。我们再看图1: ![](http://images.kuryun.com/blog/typecho/1577626147.png) “t2占用资源”这句打印位置一定是在t1的“method1休眠完毕”之前的,因为打印这句话的语句,并未收到synchronized的作用,各对象互不干扰。而synchronized修饰的部分就得按顺序执行完才能被另一个对象执行,符合我们最初结论。
标签:
Java
,
多线程
,
Synchronized
,
线程锁
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:
http://www.i366211.com/archives/66/
上一篇
基于Swing的一对一聊天程序
下一篇
php7编译安装openssl扩展
取消回复
评论啦~
提交评论
栏目分类
软件安装
10
开发工具
8
算法
2
测试
1
架构
3
填坑记
2
开源
6
科普
6
私域
2
读书笔记
4
编程
48
运营
3
管理
1
标签云
算法
C程序设计语言
C语言
Java
mysql
PHP
ffmpeg
golang
VueJs
脚手架
VueJs实战项目
Intellij IDEA
Centos7
Hyperf
抖音运营
杰克韦尔奇
跌荡一百年
生成海量测试数据
企业管理
习题2-3
习题2-4
习题2-6
异常分类
File
习题2-7
习题2-8
习题2-9
习题3-3
习题3-4
习题3-5
友情链接
申请
SaaS引擎
机器人框架
京东捡漏