转载请备注地址: https://blog.csdn.net/qq_34337272/article/details/79655194

Java多线程学习(二)将分为两篇文章介绍synchronized同步方法另一篇介绍synchronized同步语句块

系列文章传送门:
Java多线程学习(一)Java多线程入门

Java多线程学习(二)synchronized关键字(1)

Java多线程学习(二)synchronized关键字(2)

Java多线程学习(三)volatile关键字

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 blocke.printStackTrace();}}}

ThreadA.java

public class ThreadA extends Thread {private HasSelfPrivateNum numRef;public ThreadA(HasSelfPrivateNum numRef) {super();this.numRef = numRef;}@Overridepublic 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;}@Overridepublic 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;}@Overridepublic 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 blocke.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 {@Overridepublic 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 blocke.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 {@Overridepublic 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实战项目回复关键字免费领取):

转载于:https://www.cnblogs.com/snailclimb/p/9086419.html

Java多线程学习(二)synchronized关键字(1)相关推荐

  1. Java多线程学习二十九:AtomicInteger(原子类) 和 synchronized 的异同点?

    原子类和 synchronized 关键字都可以用来保证线程安全,在本课时中,我们首先分别用原子类和 synchronized 关键字来解决一个经典的线程安全问题,给出具体的代码对比,然后再分析它们背 ...

  2. java多线程学习二、安全与不安全示例:12306买票和银行取钱、java内存模型、内存可见性、线程同步块和方法

    文章目录 前言 1. 什么是块,分为几种 2. 静态块与构造块的区别 一. 举例说明:并发情况下,线程不安全 1. 示例1:unsafe12306取票 2. 示例2:unsafe银行取钱 二.线程不安 ...

  3. Java多线程学习二十八:原子类和 volatile 有什么异同?

    原子类和 volatile 有什么异同 案例说明 volatile 和原子类的异同 我们首先看一个案例.如图所示,我们有两个线程. 在图中左上角可以看出,有一个公共的 boolean flag 标记位 ...

  4. Java多线程学习(二)---线程创建方式

    线程创建方式 摘要: 1. 通过继承Thread类来创建并启动多线程的方式 2. 通过实现Runnable接口来创建并启动线程的方式 3. 通过实现Callable接口来创建并启动线程的方式 4. 总 ...

  5. Java多线程学习二十:HashMap 为什么是线程不安全的

    为什么 HashMap 是线程不安全的?而对于 HashMap,相信你一定并不陌生,HashMap 是我们平时工作和学习中用得非常非常多的一个容器,也是 Map 最主要的实现类之一,但是它自身并不具备 ...

  6. Java多线程学习二十一:ConcurrentHashMap 在 Java7 和 8 有何不同

    在 Java 8 中,对于 ConcurrentHashMap 这个常用的工具类进行了很大的升级,对比之前 Java 7 版本在诸多方面都进行了调整和变化.不过,在 Java 7 中的 Segment ...

  7. Java多线程学习二十七:AtomicInteger 在高并发下性能不好,如何解决?为什么?

    AtomicInteger 在高并发下性能不好,如何解决?以及为什么会出现这种情况? 我们知道在 JDK1.5 中新增了并发情况下使用的 Integer/Long 所对应的原子类 AtomicInte ...

  8. Java多线程学习二十六:原子类是如何利用 CAS 保证线程安全的?

    什么是原子类,以及它有什么作用. 在编程领域里,原子性意味着"一组操作要么全都操作成功,要么全都失败,不能只操作成功其中的一部分".而 java.util.concurrent.a ...

  9. Java多线程学习二十三:什么是阻塞队列

    阻塞队列的作用 阻塞队列,也就是 BlockingQueue,它是一个接口,如代码所示: public interface BlockingQueue<E> extends Queue&l ...

  10. Java多线程学习二十五:阻塞和非阻塞队列的并发安全原理||如何选择适合自己的阻塞队列?

    阻塞和非阻塞队列的并发安全原理. 之前我们探究了常见的阻塞队列的特点,以 ArrayBlockingQueue 为例, 首先分析 BlockingQueue 即阻塞队列的线程安全原理,然后再看看它的兄 ...

最新文章

  1. TextLink的空格使用
  2. 【错误记录】Error creating bean with name: Unsatisfied dependency expressed through field
  3. 【队列】【225. 用队列实现栈】【简单】
  4. cad与连接mySQL数据库_跨服务器操作数据库?其实很简单!(下)
  5. 带有关闭按钮的alertView
  6. 有经验的面试官都是如何快速判断程序员能力的?
  7. 2.分布式服务架构:原理、设计与实战 --- 彻底解决分布式系统一致性的问题
  8. TI TMS570LC43xx 裸机开发快速上手
  9. c语言程序设计第4版pdf荣政_C语言程序设计 PDF 第4版
  10. Ladon8.9扫描器简明教程/用法例子
  11. maven环境变量配置?
  12. 软件测试面试——常见的面试问题总结(大全)
  13. Java 金额转换帮助类(元转分)
  14. Panel的基本用法
  15. PAT : PAT (Basic Level) Practice(中文)答案(1001 ~ 1095)(纯C编写)
  16. IPv6 address示例
  17. 短链接服务架构设计概览
  18. Eclipse -- 打包 -- MANIFEST.MF 规则
  19. 测试用例(功能用例)——资产盘点
  20. 硬盘结构,主引导记录MBR,硬盘分区表DPT,主分区、扩展分区和逻辑分区

热门文章

  1. ffmpeg-filter 入门
  2. java baen转json 已经 json 转java bean 优秀博客分享
  3. HDU——2768 Cat vs. Dog
  4. OpenStack Juno版本网络节点gre模式配置
  5. [zz]ctags和vim- -
  6. 蚂蚁式管理(Style of Ant Management)
  7. 利用Lucene.net搭建站内搜索(3)---创建索引
  8. Visual Studio 剪切板新特性
  9. makefile 无法工作_什么是Makefile,它如何工作?
  10. gnome没有命令行_命令行提示,喜欢GNOME的8个理由,自动Raspberry Pi备份等