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 ...
随机推荐
- 统计VS2013中有效行数
将鼠标放在解决方案处,按下ctrl+shift+F b*[^:b#/]+.*$(带前面的using)^b*[^:b#/]+.*$
- idea超炫的自定义模板
idea超炫的自定义模板 idea 有些快捷键 sout -> System.out.println() ,输入sout,idea能自动补全代码, 这种约定的快捷方式大大提高了效率, 而id ...
- KMP算法字符串查找子串
题目: 经典的KMP算法 分析: 和KMP算法对应的是BF算法,其中BF算法时间复杂度,最坏情况下可以达到O(n*m),而KMP算法的时间复杂度是O(n + m),所以,KMP算法效率高很多. 但是K ...
- Delphi实现在数据库中存取图像
向窗体上添加一个TListBox组件.一个TImage组件和一个TTable组件,设计完成的主界面如图1所示. 图1 主界面 本系统中需要设计一个新的基于Paradox 7的数据库Image.db,图 ...
- 当线程是继承Thread时候 实现方法是静态方法时候 可以用锁修饰静态方法 此时锁对象是类 为啥继承的线程要用 类对象呢 因为他能生成很多实例 接口实现为啥用this 呢因为他就一个
- BZOJ 1025 游戏(分组背包)
题目所谓的序列长度实际上就是各循环节的lcm+1. 所以题目等价于求出 一串数之和等于n,这串数的lcm种数. 由唯一分解定理可以联想到只要把每个素数的幂次放在一个分组里,然后对整体做一遍分组背包就行 ...
- 前端基础:JavaScript对象
JavaScript对象 在JavaScript中除了null和undefined以外,其他的数据类型都被定义成了对象,也可以用创建对象的方法定义变量,数字型.布尔型.字符串.日期.数字和正则表达式. ...
- 在git 服务器挂载、创建新的项目、克隆新的项目
流程,服务器创建项目名-->客户端克隆-->客户端提交并且推送-->完成 详细步骤 1.在git服务器路径文件夹下创建空文件夹,名字为新的项目名,如在 F:\git ...
- BZOJ1901:Zju2112 Dynamic Rankings——题解
http://www.lydsy.com/JudgeOnline/problem.php?id=1901 Description 给定一个含有n个数的序列a[1],a[2],a[3]……a[n],程序 ...
- X day4
题目 官方题解 T1: 单调栈,单调队列因为认为考场上会写崩所以写了一个十分暴力的方法(线段树) 然后做一做区间覆盖即可 #include<iostream> #include<cs ...