Java多线程学习(二)synchronized关键字(2)
转载请备注地址:https://blog.csdn.net/qq_34337272/article/details/79670775
系列文章传送门:
Java多线程学习(二)synchronized关键字(1)
Java多线程学习(二)synchronized关键字(2)
系列文章将被优先更新于微信公众号“Java面试通关手册”,欢迎广大Java程序员和爱好技术的人员关注。
(2) synchronized同步语句块
本节思维导图:
思维导图源文件+思维导图软件关注微信公众号:“Java面试通关手册”回复关键字:“Java多线程”免费领取。
一 synchronized方法的缺点
使用synchronized关键字声明方法有些时候是有很大的弊端的,比如我们有两个线程一个线程A调用同步方法后获得锁,那么另一个线程B就需要等待A执行完,但是如果说A执行的是一个很费时间的任务的话这样就会很耗时。
先来看一个暴露synchronized方法的缺点实例,然后在看看如何通过synchronized同步语句块解决这个问题。
Task.java
public class Task {
private String getData1;
private String getData2;
public synchronized void doLongTimeTask() {
try {
System.out.println("begin task");
Thread.sleep(3000);
getData1 = "长时间处理任务后从远程返回的值1 threadName="
+ Thread.currentThread().getName();
getData2 = "长时间处理任务后从远程返回的值2 threadName="
+ Thread.currentThread().getName();
System.out.println(getData1);
System.out.println(getData2);
System.out.println("end task");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
CommonUtils.java
public class CommonUtils {
public static long beginTime1;
public static long endTime1;
public static long beginTime2;
public static long endTime2;
}
MyThread1.java
public class MyThread1 extends Thread {
private Task task;
public MyThread1(Task task) {
super();
this.task = task;
}
@Override
public void run() {
super.run();
CommonUtils.beginTime1 = System.currentTimeMillis();
task.doLongTimeTask();
CommonUtils.endTime1 = System.currentTimeMillis();
}
}
MyThread2.java
public class MyThread2 extends Thread {
private Task task;
public MyThread2(Task task) {
super();
this.task = task;
}
@Override
public void run() {
super.run();
CommonUtils.beginTime2 = System.currentTimeMillis();
task.doLongTimeTask();
CommonUtils.endTime2 = System.currentTimeMillis();
}
}
Run.java
public class Run {
public static void main(String[] args) {
Task task = new Task();
MyThread1 thread1 = new MyThread1(task);
thread1.start();
MyThread2 thread2 = new MyThread2(task);
thread2.start();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
long beginTime = CommonUtils.beginTime1;
if (CommonUtils.beginTime2 < CommonUtils.beginTime1) {
beginTime = CommonUtils.beginTime2;
}
long endTime = CommonUtils.endTime1;
if (CommonUtils.endTime2 > CommonUtils.endTime1) {
endTime = CommonUtils.endTime2;
}
System.out.println("耗时:" + ((endTime - beginTime) / 1000));
}
}
运行结果:
从运行时间上来看,synchronized方法的问题很明显。可以使用synchronized同步块来解决这个问题。但是要注意synchronized同步块的使用方式,如果synchronized同步块使用不好的话并不会带来效率的提升。
二 synchronized(this)同步代码块的使用
修改上例中的Task.java如下:
public class Task {
private String getData1;
private String getData2;
public void doLongTimeTask() {
try {
System.out.println("begin task");
Thread.sleep(3000);
String privateGetData1 = "长时间处理任务后从远程返回的值1 threadName="
+ Thread.currentThread().getName();
String privateGetData2 = "长时间处理任务后从远程返回的值2 threadName="
+ Thread.currentThread().getName();
synchronized (this) {
getData1 = privateGetData1;
getData2 = privateGetData2;
}
System.out.println(getData1);
System.out.println(getData2);
System.out.println("end task");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
运行结果:
从上面代码可以看出当一个线程访问一个对象的synchronized同步代码块时,另一个线程任然可以访问该对象非synchronized同步代码块。
时间虽然缩短了,但是大家考虑一下synchronized代码块真的是同步的吗?它真的持有当前调用对象的锁吗?
是的。不在synchronized代码块中就异步执行,在synchronized代码块中就是同步执行。
验证代码:synchronizedDemo1包下
三 synchronized(object)代码块间使用
MyObject.java
public class MyObject {
}
Service.java
public class Service {
public void testMethod1(MyObject object) {
synchronized (object) {
try {
System.out.println("testMethod1 ____getLock time="
+ System.currentTimeMillis() + " run ThreadName="
+ Thread.currentThread().getName());
Thread.sleep(2000);
System.out.println("testMethod1 releaseLock time="
+ System.currentTimeMillis() + " run ThreadName="
+ Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
ThreadA.java
public class ThreadA extends Thread {
private Service service;
private MyObject object;
public ThreadA(Service service, MyObject object) {
super();
this.service = service;
this.object = object;
}
@Override
public void run() {
super.run();
service.testMethod1(object);
}
}
ThreadB.java
public class ThreadB extends Thread {
private Service service;
private MyObject object;
public ThreadB(Service service, MyObject object) {
super();
this.service = service;
this.object = object;
}
@Override
public void run() {
super.run();
service.testMethod1(object);
}
}
Run1_1.java
public class Run1_1 {
public static void main(String[] args) {
Service service = new Service();
MyObject object = new MyObject();
ThreadA a = new ThreadA(service, object);
a.setName("a");
a.start();
ThreadB b = new ThreadB(service, object);
b.setName("b");
b.start();
}
}
运行结果:
可以看出如下图所示,两个线程使用了同一个“对象监视器”,所以运行结果是同步的。
那么,如果使用不同的对象监视器会出现什么效果呢?
修改Run1_1.java如下:
public class Run1_2 {
public static void main(String[] args) {
Service service = new Service();
MyObject object1 = new MyObject();
MyObject object2 = new MyObject();
ThreadA a = new ThreadA(service, object1);
a.setName("a");
a.start();
ThreadB b = new ThreadB(service, object2);
b.setName("b");
b.start();
}
}
运行结果:
可以看出如下图所示,两个线程使用了不同的“对象监视器”,所以运行结果不是同步的了。
四 synchronized代码块间的同步性
当一个对象访问synchronized(this)代码块时,其他线程对同一个对象中所有其他synchronized(this)代码块代码块的访问将被阻塞,这说明synchronized(this)代码块使用的“对象监视器”是一个。
也就是说和synchronized方法一样,synchronized(this)代码块也是锁定当前对象的。
另外通过上面的学习我们可以得出两个结论。
- 其他线程执行对象中synchronized同步方法(上一节我们介绍过,需要回顾的可以看上一节的文章)和synchronized(this)代码块时呈现同步效果;
- 如果两个线程使用了同一个“对象监视器”,运行结果同步,否则不同步.
五 静态同步synchronized方法与synchronized(class)代码块
synchronized关键字加到static静态方法和synchronized(class)代码块上都是是给Class类上锁,而synchronized关键字加到非static静态方法上是给对象上锁。
Service.java
package ceshi;
public class Service {
public static void printA() {
synchronized (Service.class) {
try {
System.out.println(
"线程名称为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "进入printA");
Thread.sleep(3000);
System.out.println(
"线程名称为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "离开printA");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
synchronized public static void printB() {
System.out.println("线程名称为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "进入printB");
System.out.println("线程名称为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "离开printB");
}
synchronized public void printC() {
System.out.println("线程名称为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "进入printC");
System.out.println("线程名称为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "离开printC");
}
}
ThreadA.java
public class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.printA();
}
}
ThreadB.java
public class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.printB();
}
}
ThreadC.java
public class ThreadC extends Thread {
private Service service;
public ThreadC(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.printC();
}
}
Run.java
public class Run {
public static void main(String[] args) {
Service service = new Service();
ThreadA a = new ThreadA(service);
a.setName("A");
a.start();
ThreadB b = new ThreadB(service);
b.setName("B");
b.start();
ThreadC c = new ThreadC(service);
c.setName("C");
c.start();
}
}
运行结果:
从运行结果可以看出:静态同步synchronized方法与synchronized(class)代码块持有的锁一样,都是Class锁,Class锁对对象的所有实例起作用。synchronized关键字加到非static静态方法上持有的是对象锁。
线程A,B和线程C持有的锁不一样,所以A和B运行同步,但是和C运行不同步。
六 数据类型String的常量池属性
在Jvm中具有String常量池缓存的功能
String s1 = "a";
String s2="a";
System.out.println(s1==s2);//true
上面代码输出为true.这是为什么呢?
字符串常量池中的字符串只存在一份! 即执行完第一行代码后,常量池中已存在 “a”,那么s2不会在常量池中申请新的空间,而是直接把已存在的字符串内存地址返回给s2。
因为数据类型String的常量池属性,所以synchronized(string)在使用时某些情况下会出现一些问题,比如两个线程运行
synchronized(“abc”){
}和
synchronized(“abc”){
}修饰的方法时,这两个线程就会持有相同的锁,导致某一时刻只有一个线程能运行。所以尽量不要使用synchronized(string)而使用synchronized(object)
参考:
《Java多线程编程核心技术》
《Java并发编程的艺术》
如果你觉得博主的文章不错,欢迎转发点赞。你能从中学到知识就是我最大的幸运。
欢迎关注我的微信公众号:“Java面试通关手册”(分享各种Java学习资源,面试题,以及企业级Java实战项目回复关键字免费领取)。另外我创建了一个Java学习交流群(群号:174594747),欢迎大家加入一起学习,这里更有面试,学习视频等资源的分享。
Java多线程学习(二)synchronized关键字(2)的更多相关文章
- Java多线程之二(Synchronized)
常用API method 注释 run() run()方法是我们创建线程时必须要实现的方法,但是实际上该方法只是一个普通方法,直接调用并没有开启线程的作用. start() start()方法作用为使 ...
- Java多线程4:synchronized关键字
原文:http://www.cnblogs.com/skywang12345/p/3479202.html 1. synchronized原理在java中,每一个对象有且仅有一个同步锁.这也意味着,同 ...
- java线程学习之synchronized关键字
关键字synchronized的作用是实现线程间的同步.它的任务是对同步的代码加锁.一个代码块同时只能有同一个线程进行读和写操作,从而保证线程间是安全的. 线程安全的概念是:当多个线程访问某一个类(对 ...
- Java多线程学习(二)---线程创建方式
线程创建方式 摘要: 1. 通过继承Thread类来创建并启动多线程的方式 2. 通过实现Runnable接口来创建并启动线程的方式 3. 通过实现Callable接口来创建并启动线程的方式 4. 总 ...
- java多线程学习二
声明:本篇博客是本人为了自己学习保存的心得,其内容主要是从大神——五月的仓颉的博客中学习而来,在此多谢大神五月的仓颉的分享,敬礼!如有疑问请联系博主,谢谢! 本章主要记录并讲述线程在项目中常用的方法: ...
- Java多线程学习之synchronized总结
0.概述 synchronized是Java提供的内置的锁机制,来实现代对码块的同步访问,称为内置锁(Intrinsic Lock) .内置锁包括两部分:一个是作为锁的对象的引用,另一个是由这个锁保护 ...
- JAVA多线程之Synchronized关键字--对象锁的特点
一,介绍 本文介绍JAVA多线程中的synchronized关键字作为对象锁的一些知识点. 所谓对象锁,就是就是synchronized 给某个对象 加锁.关于 对象锁 可参考:这篇文章 二,分析 s ...
- Java多线程学习(二)synchronized关键字(1)
转载请备注地址: https://blog.csdn.net/qq_34337272/article/details/79655194 Java多线程学习(二)将分为两篇文章介绍synchronize ...
- Java多线程学习(三)volatile关键字
转载请备注地址:https://blog.csdn.net/qq_34337272/article/details/79680693 系列文章传送门: Java多线程学习(一)Java多线程入门 Ja ...
随机推荐
- 判断集合不为空Java
list.size>0判断集合size是否大于0 list.isEmpty()判断集合是否为空,返回布尔值
- echarts 画 canvas 如果在IE8下不显示图标
网上说法很多,可能版本问题,也有说script标签位置问题(放在body中) 不过先试已下,清除option对象中多余的逗号(,) 对象的最后一个属性后不要有逗号结尾
- 静态方法不能使用this的原因 当没有实例对象时候 在静态方法里面传入this时会出现空指针异常现象 所以为了防止该现象 静态方法里面不能使用this
静态方法不能使用this的原因 当没有实例对象时候 在静态方法里面传入this时会出现空指针异常现象 所以为了防止该现象 静态方法里面不能使用this
- BZOJ 1005 明明的烦恼(prufer序列+高精度)
有一种东西叫树的prufer序列,一个树的与一个prufer序列是一一对应的关系. 设有m个度数确定的点,这些点的度为dee[i],那么每个点在prufer序列中出现了dee[i]-1次. 由排列组合 ...
- 【codevs1380】没有上司的舞会 树形dp
题目描述 Ural大学有N个职员,编号为1~N.他们有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司.每个职员有一个快乐指数.现在有个周年庆宴会,要求与会职员的快乐指数 ...
- Java入门之:基本数据类型
Java基本数据类型 变量就是申请内存来存储值,也就是说,当创建变量的时候,需要在内存中申请空间.内存管理系统根据变量的类型为变量分配存储空间,分配的空间只能用来存储该类型的数据,如下图所示: 因此, ...
- BZOJ4889 & 洛谷3759:[TJOI2017]不勤劳的图书管理员——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=4889 https://www.luogu.org/problemnew/show/P3759 加里 ...
- BZOJ1042 [HAOI2008]硬币购物 【完全背包 + 容斥】
1042: [HAOI2008]硬币购物 Time Limit: 10 Sec Memory Limit: 162 MB Submit: 2924 Solved: 1802 [Submit][St ...
- Vue_WebPack小白入门
Vue语法笔记 Vue项目搭建过程 Vue问题总结 去掉vue 中的代码规范检测(Eslint验证) 解决跨域请求问题 Vue推荐资料
- Eclipse集成Android NDK及导出Jar和so动态库
一.安装Cygwin 在Windows环境而又不想使用linux环境,可以安装cygwin(http://www.cygwin.com/ ),为了使用gcc注意cygwin的必选安装包在devel目录 ...