Java多线程学习(二)synchronized关键字(1)
转载请备注地址: https://blog.csdn.net/qq_34337272/article/details/79655194
Java多线程学习(二)将分为两篇文章介绍synchronized同步方法另一篇介绍synchronized同步语句块。
系列文章传送门:
Java多线程学习(一)Java多线程入门
Java多线程学习(二)synchronized关键字(1)
Java多线程学习(二)synchronized关键字(2)
Java多线程学习(四)等待/通知(wait/notify)机制
系列文章将被优先更新与微信公众号“Java面试通关手册”,欢迎广大Java程序员和爱好技术的人员关注。
(1) synchronized同步方法
本节思维导图:
思维导图源文件+思维导图软件关注微信公众号:“Java面试通关手册”回复关键字:“Java多线程”免费领取。
一 简介
Java并发编程这个领域中synchronized关键字一直都是元老级的角色,很久之前很多人都会称它为“重量级锁”。但是,在JavaSE 1.6之后进行了主要包括为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻量级锁以及其它各种优化之后变得在某些情况下并不是那么重了。
这一篇文章不会介绍synchronized关键字的实现原理,更多的是synchronized关键字的使用。如果想要了解的可以看看方腾飞的《Java并发编程的艺术》。
二 变量安全性
“非线程安全”问题存在于“实例变量”中,如果是方法内部的私有变量,则不存在“非线程安全”问题,所得结果也就是“线程安全”的了。
如果两个线程同时操作对象中的实例变量,则会出现“非线程安全”,解决办法就是在方法前加上synchronized关键字即可。前面一篇文章我们已经讲过,而且贴过相应代码,所以这里就不再贴代码了。
三 多个对象多个锁
先看例子:
HasSelfPrivateNum.java
public class HasSelfPrivateNum {
private int num = 0;
synchronized public void addI(String username) {
try {
if (username.equals("a")) {
num = 100;
System.out.println("a set over!");
//如果去掉hread.sleep(2000),那么运行结果就会显示为同步的效果
Thread.sleep(2000);
} else {
num = 200;
System.out.println("b set over!");
}
System.out.println(username + " num=" + num);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
ThreadA.java
public class ThreadA extends Thread {
private HasSelfPrivateNum numRef;
public ThreadA(HasSelfPrivateNum numRef) {
super();
this.numRef = numRef;
}
@Override
public void run() {
super.run();
numRef.addI("a");
}
}
ThreadB.java
public class ThreadB extends Thread {
private HasSelfPrivateNum numRef;
public ThreadB(HasSelfPrivateNum numRef) {
super();
this.numRef = numRef;
}
@Override
public void run() {
super.run();
numRef.addI("b");
}
}
Run.java
public class Run {
public static void main(String[] args) {
HasSelfPrivateNum numRef1 = new HasSelfPrivateNum();
HasSelfPrivateNum numRef2 = new HasSelfPrivateNum();
ThreadA athread = new ThreadA(numRef1);
athread.start();
ThreadB bthread = new ThreadB(numRef2);
bthread.start();
}
}
运行结果:
a num=100停顿一会才执行
上面实例中两个线程ThreadA和ThreadB分别访问同一个类的不同实例的相同名称的同步方法,但是效果确实异步执行。
为什么会这样呢?
这是因为synchronized取得的锁都是对象锁,而不是把一段代码或方法当做锁。所以在上面的实例中,哪个线程先执行带synchronized关键字的方法,则哪个线程就持有该方法所属对象的锁Lock,那么其他线程只能呈等待状态,前提是多个线程访问的是同一个对象。本例中很显然是两个对象。
在本例中创建了两个HasSelfPrivateNum类对象,所以就产生了两个锁。当ThreadA的引用执行到addI方法中的runThread.sleep(2000)语句时,ThreadB就会“乘机执行”。所以才会导致执行结果如上图所示(备注:由于runThread.sleep(2000),“a num=100”停顿了两秒才输出)
四 synchronized方法与锁对象
通过上面我们知道synchronized取得的锁都是对象锁,而不是把一段代码或方法当做锁。如果多个线程访问的是同一个对象,哪个线程先执行带synchronized关键字的方法,则哪个线程就持有该方法,那么其他线程只能呈等待状态。如果多个线程访问的是多个对象则不一定,因为多个对象会产生多个锁。
那么我们思考一下当多个线程访问的是同一个对象中的非synchronized类型方法会是什么效果?
答案是:会异步调用非synchronized类型方法,解决办法也很简单在非synchronized类型方法前加上synchronized关键字即可。
五 脏读
发生脏读的情况实在读取实例变量时,此值已经被其他线程更改过。
PublicVar.java
public class PublicVar {
public String username = "A";
public String password = "AA";
synchronized public void setValue(String username, String password) {
try {
this.username = username;
Thread.sleep(5000);
this.password = password;
System.out.println("setValue method thread name="
+ Thread.currentThread().getName() + " username="
+ username + " password=" + password);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//该方法前加上synchronized关键字就同步了
public void getValue() {
System.out.println("getValue method thread name="
+ Thread.currentThread().getName() + " username=" + username
+ " password=" + password);
}
}
ThreadA.java
public class ThreadA extends Thread {
private PublicVar publicVar;
public ThreadA(PublicVar publicVar) {
super();
this.publicVar = publicVar;
}
@Override
public void run() {
super.run();
publicVar.setValue("B", "BB");
}
}
Test.java
public class Test {
public static void main(String[] args) {
try {
PublicVar publicVarRef = new PublicVar();
ThreadA thread = new ThreadA(publicVarRef);
thread.start();
Thread.sleep(200);//打印结果受此值大小影响
publicVarRef.getValue();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
运行结果:
解决办法:getValue()方法前加上synchronized关键字即可。
加上synchronized关键字后的运行结果:
六 synchronized锁重入
“可重入锁”概念是:自己可以再次获取自己的内部锁。比如一个线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想要获取这个对象的锁的时候还是可以获取的,如果不可锁重入的话,就会造成死锁。
Service.java
public class Service {
synchronized public void service1() {
System.out.println("service1");
service2();
}
synchronized public void service2() {
System.out.println("service2");
service3();
}
synchronized public void service3() {
System.out.println("service3");
}
}
MyThread.java
public class MyThread extends Thread {
@Override
public void run() {
Service service = new Service();
service.service1();
}
}
Run.java
public class Run {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
}
}
运行结果:
另外可重入锁也支持在父子类继承的环境中
Main.java:
public class Main {
public int i = 10;
synchronized public void operateIMainMethod() {
try {
i--;
System.out.println("main print i=" + i);
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Sub.java:
public class Sub extends Main {
synchronized public void operateISubMethod() {
try {
while (i > 0) {
i--;
System.out.println("sub print i=" + i);
Thread.sleep(100);
this.operateIMainMethod();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
MyThread.java:
public class MyThread extends Thread {
@Override
public void run() {
Sub sub = new Sub();
sub.operateISubMethod();
}
}
Run.java:
public class Run {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
}
}
运行结果:
说明当存在父子类继承关系时,子类是完全可以通过“可重入锁”调用父类的同步方法。
另外出现异常时,其锁持有的锁会自动释放。
七 同步不具有继承性
如果父类有一个带synchronized关键字的方法,子类继承并重写了这个方法。
但是同步不能继承,所以还是需要在子类方法中添加synchronized关键字。
参考:
《Java多线程编程核心技术》
欢迎关注我的微信公众号(分享各种Java学习资源,面试题,以及企业级Java实战项目回复关键字免费领取):
Java多线程学习(二)synchronized关键字(1)的更多相关文章
- 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关键字(2)
转载请备注地址:https://blog.csdn.net/qq_34337272/article/details/79670775 系列文章传送门: Java多线程学习(一)Java多线程入门 Ja ...
- Java多线程学习(三)volatile关键字
转载请备注地址:https://blog.csdn.net/qq_34337272/article/details/79680693 系列文章传送门: Java多线程学习(一)Java多线程入门 Ja ...
随机推荐
- cacti监控多个mysql端口
1. 在Console -> Data Templates 找到mysql-cacti-templates的mysql模板,编辑: 在这里面把Port的Use Per-Data Source V ...
- 【python】windows7下怎样安装whl
windows7 python2.7 1.用管理员方式打开cmd 2.首先通过pip命令安装wheel 如果提示’pip’不是内部或外部命令,也不是可运行的程序或批处理文件 ①将python安装目录下 ...
- 【codevs1404】字符串匹配 KMP
题目描述 给你两个串A,B,可以得到从A的任意位开始的子串和B匹配的长度.给定K个询问,对于每个询问给定一个x,求出匹配长度恰为x的位置有多少个.N,M,K<=200000 输入 第一行三个数 ...
- Luogu3147 USACO16OPEN 262144(动态规划)
感觉上这个题是可以直接暴力的,每次根据一段连续最小值个数的奇偶性决定是否划分区间,递归处理.然而写起来实在太麻烦了. 设f[i][j]为以i为左端点合并出j时的右端点.则有f[i][j]=f[f[i] ...
- 洛谷 P2747 [USACO5.4]周游加拿大Canada Tour 解题报告
P2747 [USACO5.4]周游加拿大Canada Tour 题目描述 你赢得了一场航空公司举办的比赛,奖品是一张加拿大环游机票.旅行在这家航空公司开放的最西边的城市开始,然后一直自西向东旅行,直 ...
- SpringAOP简介
AOP(Aspect Orient Programming) --- 面向切面编程 将分散在各个业务逻辑中的相同代码 通过 “横向”切割方式抽取到独立模块中 方式抽取到独立模块中;[它针对的是程序运行 ...
- some of the properties associated with the solution could not be read解决方法
基于TFS管理的解决方案打开时提示:“some of the properties associated with the solution could not be read”,并不影响项目加载,O ...
- shell中的颜色显示
By francis_hao Sep 30,2017 图片来自参考[1] 其中,"033"是八进制数,其对应的asciima也就是ESC.后面的颜色格式为:[背景 ...
- Linux 第30天: (08月5日) 练习和作业
变量脚本 1.编写脚本/root/bin/systeminfo.sh,显示当前主机系统信息,包括主机名,IPv4地址,操作系统版本,内核版本,CPU型号,内存大小,硬盘大小 server_ip=`if ...
- 背景建模技术(二):BgsLibrary的框架、背景建模的37种算法性能分析、背景建模技术的挑战
背景建模技术(二):BgsLibrary的框架.背景建模的37种算法性能分析.背景建模技术的挑战 1.基于MFC的BgsLibrary软件下载 下载地址:http://download.csdn.ne ...