一、简介

synchronized,是java的关键字,它代表这个方法加锁,相当于不管哪一个线程(例如线程A),运行到这个方法时,都要检查有没有其它线程B(或者C、 D等)正在用这个方法(或者该类的其他同步方法),有的话要等正在使用synchronized方法的线程B(或者C 、D)运行完这个方法后再运行此线程A,没有的话,锁定调用者,然后直接运行。它包括两种用法:synchronized 同步方法和synchronized同步块。

二、用法

下面我们通过一个示例说明一下synchronized在多线程同步中的使用方法,我们首先来看如下代码存在的问题:

package com.wsh.thread.synchronize.test01;public class Test01 {public static void main(String[] args) {// 静态代理-真实角色AppleRunnable appleRunnable = new AppleRunnable();// 静态代理-代理角色Thread proxy1 = new Thread(appleRunnable, "zhangsan");Thread proxy2 = new Thread(appleRunnable, "lisi");Thread proxy3 = new Thread(appleRunnable, "wangwu");// 启动线程proxy1.start();proxy2.start();proxy3.start();}}/*** 线程类*/
class AppleRunnable implements Runnable {// 共享资源(苹果总数量)private int num = 10;@Overridepublic void run() {while (true) {// 如果苹果数量小于等于0,则跳出循环if (num <= 0) {break;}// 模拟网络延时try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "吃掉第" + (num--) + "个苹果");}}}

以上代码主要是三个线程同时运行,三个人吃10个苹果,这就存在多线程中资源共享的问题。我们运行代码,如下图:

可以看到,总共十个苹果,很明显结果是错的。这就造成了多个线程访问同一个资源冲突的问题。使用synchronized同步就可以解决上述问题。我们改下代码:

【a】使用同步方法解决资源冲突问题:

/*** 线程类*/
class AppleRunnable implements Runnable {// 共享资源(苹果总数量)private int num = 60;//线程运行标识private boolean flag = true;/*** 加入同步方法处理,当num资源被访问时其他线程只能在这里等待*/private synchronized void eat() {// 如果苹果数量小于等于0,则跳出循环if (num <= 0) {flag = false;return;}// 模拟网络延时try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "吃掉第" + (num--) + "个苹果");}@Overridepublic void run() {while (flag) {eat();}}}

可以看到,我们再eat()方法上面加上了synchronized关键字, 假设当a,b,c三个人同时到达eat(),假如a首先进入了eat()方法,相当于a拥有了访问这个方法的锁,其他线程如b,c需要在外面等待,直到a访问完这个对象释放完锁之后,b,c才能获取这个锁。这样就实现了资源在某个时刻只能一个人访问,其他人都必须等待它释放锁。

运行代码:

如图可见,总共十个苹果,三个线程同时访问这个资源,资源总数正确,说明synchronized确实能够实现线程同步,以上就是synchronized同步方法,它锁定的是AppleRunnable实例对象。

【b】使用同步块解决资源冲突问题:

除了使用synchronized同步方法处理线程同步,同样可以使用同步块解决。如下:

/*** 线程类*/
class AppleRunnable implements Runnable {// 共享资源(苹果总数量)private int num = 30;//线程运行标识private boolean flag = true;private void eat() {//加入同步代码块,线程安全//this指appleRunnablesynchronized (this) {// 如果苹果数量小于等于0,则跳出循环if (num <= 0) {flag = false;return;}// 模拟网络延时try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "吃掉第" + (num--) + "个苹果");}}@Overridepublic void run() {while (flag) {eat();}}}

可以看到,我们将eat()整个方法用synchronized(this){ ..}包围,其中this指的是appleRunnable对象,使用synchronized(this){ ..}锁定了整个方法,使得在多线程环境中,只能有一个线程获得访问锁,其他线程都必须等待直到释放之后才能获取。我们运行代码:

可以看到,synchronized(this){ ..}同步块也可以解决多线程中访问共享资源冲突的问题。

三、错误锁定演示

这里主要演示三种常见的因为锁定范围不正确,锁定范围过大或者过小、锁定资源不正确导致线程不安全,资源访问冲突的问题。

【a】锁定范围过小,导致线程不安全。

private void eat() {//锁定范围不正确,导致线程不安全synchronized (this) {if (num <= 0) {flag = false;return;}}try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "吃掉第" + (num--) + "个苹果");}

分析:假设现在只剩下一个苹果,即num=1, 同时a,b,c三个线程都运行到eat()方法,注意我们只锁定了一部分判断num的代码,当a获取锁之后,b,c在外面等待,a判断完之后,num = 1 <= 0不成立,由于这个时候还没进行num--, b进来判断num = 1 <= 0还是不成立,同理c也进来不成立,这个时候a,b,c同时执行到这里:

System.out.println(Thread.currentThread().getName() + "吃掉第" + (num--) + "个苹果");

a取走最后一个苹果时,执行num--之后num = 0,然后b.c依次再去取,就造成资源错误了。

【b】锁定资源不正确,导致线程不安全

private void eat() {//锁定资源不正确,导致线程不安全synchronized (new Integer(num)) {if (num <= 0) {flag = false;return;}try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "吃掉第" + (num--) + "个苹果");}}

分析:这里是因为只锁定了num这个资源对象,线程其实还有其他属性比如flag,并不能只锁定num就能够实现线程安全。

【c】锁定范围过小,导致线程不安全。

private void eat() {if (num <= 0) {flag = false;return;}//锁定范围过小,导致线程不安全synchronized (this) {try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "吃掉第" + (num--) + "个苹果");}}

分析:分析方法如【a】

四、总结

以上通过synchronized同步方法和synchronized同步块解决了多线程中共享资源的访问冲突问题,当然这只是一个说明的示例,具体工作中比这个要复杂的多,其实除了同步方法和同步块,synchronized还有一种使用方法就是sychronized(类.class)锁定类的字节码信息这个用法,因为这涉及到一种设计模式,所以在下篇文章进行讲解。下面以一张图概况synchronized关键字的用法:

本文只是笔者在学习synchronized用法的使用的一些总结以及想法,仅供大家学习参考。

Java sychronized关键字总结(一)相关推荐

  1. java的关键字和保留字_「Java」详解常见的53个关键字

    1.在Java中目前一共有53个关键字: 其中由51+2个保留字=53个关键字 [友情提示 : Java的关键字都是小写哟] 2.Java的保留字有多少个?分别是什么? 答:Java的保留字有2个,J ...

  2. java的关键字与保留字

    参考:java的关键字和保留字 关键字上文介绍的很详细. 保留字:

  3. java的同步关键字_简单了解Java synchronized关键字同步

    简单了解Java synchronized关键字同步 发布于 2021-1-13| 复制链接 摘记:  synchronized synchronized可以用来同步块,同步方法.同步块可以用来更精确 ...

  4. Java volatile关键字原理解剖

    Java volatile关键字原理解剖 文章目录 Java volatile关键字原理解剖 参考文章 前置知识 CPU缓存模型 CPU缓存行 并发编程基本概念 Java锁概念 volatile关键字 ...

  5. java class 关键字_java关键字及其作用

    一. 关键字总览: 访问控制 private protected public 类,方法和变量修饰符 abstract class extends final implements interface ...

  6. java super关键字简述

    java super关键字简述 super关键字访问父类属性,访问权限必须是允许的 super关键字访问父类属性,访问权限必须是允许的 当创建子类对象时,默认调用父类的无参构造方法,除非显式调用父类的 ...

  7. java this关键字的使用

    java this关键字的使用 /*** this关键字的使用*/ public class Student {//成员变量private int sno; //学号private String na ...

  8. java控制关键字continue,break,return

    java控制关键字continue,break,return /*** break 可以停止循环继续执行 只能停止最内层的循环* 如果想停止外层for循环 需要给外层循环做标记* continue 停 ...

  9. Java instanceof关键字详解

    严格来说 instanceof 是 Java 中的一个双目运算符,由于它是由字母组成的,所以也是 Java 的保留关键字.在 Java 中可以使用 instanceof 关键字判断一个对象是否为一个类 ...

  10. 面试官系统精讲Java源码及大厂真题 - 03 Java 常用关键字理解

    03 Java 常用关键字理解 引导语 Java 中的关键字很多,大约有 50+,在命名上我们不能和这些关键字冲突的,编译会报错,每个关键字都代表着不同场景下的不同含义,接下来我们挑选 6 个比较重要 ...

最新文章

  1. 轻轻一扫,立刻扣款,付款码背后的原理你不想知道吗?
  2. XGBoost输出特征重要性以及筛选特征
  3. MySQL---Subquery returns more than 1 row
  4. jedivim加载失败,提示没有支持python3,(markdown也是如此)
  5. 不会三种编程语言的不算程序员 走近阿里云 MVP烁淼吐槽大佬
  6. centos7是php多少,centos7怎么安装php
  7. BIOS设置开机密码
  8. codeforces 58A-C语言解题报告
  9. ios重签名shell脚本_iOS逆向——shell重签名及代码注入
  10. matlab进行动力吸振器设计,动力吸振器的参数设计和动力学分析
  11. 数据科学(data science)概览
  12. 数据结构—线性表及其实现方式
  13. 分解原理_原理篇 | 推荐系统之矩阵分解模型
  14. Git提交代码的处理流程(转)
  15. linux上的pcb设计软件,PCB设计软件(CadSoft Eagle Professional)
  16. Camtasia Studio2022卡塔莎(专业的电脑屏幕录像软件)
  17. 印地语freeCodeCamp YouTube频道+不和谐聊天现已上线
  18. Multisim彻底卸载干净方法
  19. 怎样裁切图片?如何将图片剪裁成需要的大小?
  20. win10和win8双系统安装

热门文章

  1. html5怎么插入一段文字,HTML5教程—文字插入进度动画_HTML5教程_文字插入_动画进度_课课家...
  2. 算法:Validate Binary Search Tree(验证二叉查找树)
  3. 630. 课程表 III
  4. 公式推导 11-27
  5. IDEA插件系列-玩转JSON与实体类互相转换
  6. android studio for android learning (二十五 )activity的启动模式详解与标志位简析
  7. Elpscrk:功能强大的智能字典生成器
  8. 政企用户网络安全常见风险盘点
  9. 神经网络与深度学习第1章:绪论 阅读提问
  10. 409.最长回文串(力扣leetcode) 博主可答疑该问题