热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Java的Synchronized关键字学习指南(全面&详细)

这篇文章主要给大家介绍了关于Java的Synchronized关键字的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

前言

在Java中,有一个常被忽略 但 非常重要的关键字Synchronized今天,我将详细讲解 Java关键字Synchronized的所有知识,希望你们会喜欢

目录

示意图

1. 定义

Java中的1个关键字

2. 作用

保证同一时刻最多只有1个线程执行 被Synchronized修饰的方法 / 代码

其他线程 必须等待当前线程执行完该方法 / 代码块后才能执行该方法 / 代码块

3. 应用场景

保证线程安全,解决多线程中的并发同步问题(实现的是阻塞型并发),具体场景如下:

修饰 实例方法 / 代码块时,(同步)保护的是同一个对象方法的调用 & 当前实例对象修饰 静态方法 / 代码块时,(同步)保护的是 静态方法的调用 & class 类对象

4. 原理

依赖 JVM 实现同步底层通过一个监视器对象(monitor)完成, wait()、notify() 等方法也依赖于 monitor 对象

监视器锁(monitor)的本质 依赖于 底层操作系统的互斥锁(Mutex Lock)实现

5. 具体使用

Synchronized 用于 修饰 代码块、类的实例方法 & 静态方法

5.1 使用规则

示意图

5.2 锁的类型 & 等级 由于Synchronized 会修饰 代码块、类的实例方法 & 静态方法,故分为不同锁的类型具体如下

示意图

之间的区别

示意图

5.3 使用方式

/**
 * 对象锁
 */
 public class Test{ 
 // 对象锁:形式1(方法锁) 
 public synchronized void Method1(){ 
  System.out.println("我是对象锁也是方法锁"); 
  try{ 
   Thread.sleep(500); 
  } catch (InterruptedException e){ 
   e.printStackTrace(); 
  } 
 
 } 
 
 // 对象锁:形式2(代码块形式) 
 public void Method2(){ 
  synchronized (this){ 
   System.out.println("我是对象锁"); 
   try{ 
    Thread.sleep(500); 
   } catch (InterruptedException e){ 
    e.printStackTrace(); 
   } 
  } 
 
 } 
 }

/**
 * 方法锁(即对象锁中的形式1)
 */
 public synchronized void Method1(){ 
  System.out.println("我是对象锁也是方法锁"); 
  try{ 
   Thread.sleep(500); 
  } catch (InterruptedException e){ 
   e.printStackTrace(); 
  } 
 
 } 

/**
 * 类锁
 */
public class Test{ 
   // 类锁:形式1 :锁静态方法
 public static synchronized void Method1(){ 
  System.out.println("我是类锁一号"); 
  try{ 
   Thread.sleep(500); 
  } catch (InterruptedException e){ 
   e.printStackTrace(); 
  } 
 
 } 
 
 // 类锁:形式2 :锁静态代码块
 public void Method2(){ 
  synchronized (Test.class){ 
   System.out.println("我是类锁二号"); 
   try{ 
    Thread.sleep(500); 
   } catch (InterruptedException e){ 
    e.printStackTrace(); 
   } 
 
  } 
 
 } 
}

5.4 特别注意

Synchronized修饰方法时存在缺陷:若修饰1个大的方法,将会大大影响效率

示例

若使用Synchronized关键字修饰 线程类的run(),由于run()在线程的整个生命期内一直在运行,因此将导致它对本类任何Synchronized方法的调用都永远不会成功

解决方案

使用 Synchronized关键字声明代码块

该解决方案灵活性高:可针对任意代码块 & 任意指定上锁的对象

代码如下
 synchronized(syncObject) { 
 // 访问或修改被锁保护的共享状态 
 // 上述方法 必须 获得对象 syncObject(类实例或类)的锁
}

6. 特点

示意图

注:原子性、可见性、有序性的定义

示意图

7. 其他控制并发 / 线程同步方式

7.1 Lock、ReentrantLock 简介

示意图

区别

示意图

7.2 CAS

7.2.1 定义

Compare And Swap,即 比较 并 交换,是一种解决并发操作的乐观锁

synchronized锁住的代码块:同一时刻只能由一个线程访问,属于悲观锁

7.2.2 原理

// CAS的操作参数
内存位置(A)
预期原值(B)
预期新值(C)

// 使用CAS解决并发的原理:
// 1. 首先比较A、B,若相等,则更新A中的值为C、返回True;若不相等,则返回false;
// 2. 通过死循环,以不断尝试尝试更新的方式实现并发

// 伪代码如下
public boolean compareAndSwap(long memoryA, int oldB, int newC){
 if(memoryA.get() == oldB){
  memoryA.set(newC);
  return true;
 }
 return false;
}


7.2.3 优点

资源耗费少:相对于synchronized,省去了挂起线程、恢复线程的开销

但,若迟迟得不到更新,死循环对CPU资源也是一种浪费

7.2.4 具体实现方式 使用CAS有个“先检查后执行”的操作而这种操作在Java中是典型的不安全的操作,所以 CAS在实际中是由C++通过调用CPU指令实现的具体过程

// 1. CAS在Java中的体现为Unsafe类
// 2. Unsafe类会通过C++直接获取到属性的内存地址
// 3. 接下来CAS由C++的Atomic::cmpxchg系列方法实现

7.2.5 典型应用:AtomicInteger

对 i++ 与 i–,通过compareAndSet & 一个死循环实现

compareAndSet函数内部 = 通过jni操作CAS指令。直到CAS操作成功跳出循环

 private volatile int value; 
 /** 
  * Gets the current value. 
  * 
  * @return the current value 
  */ 
 public final int get() { 
  return value; 
 } 
 /** 
  * Atomically increments by one the current value. 
  * 
  * @return the previous value 
  */ 
 public final int getAndIncrement() { 
  for (;;) { 
   int current = get(); 
   int next = current + 1; 
   if (compareAndSet(current, next)) 
    return current; 
  } 
 } 
 
 /** 
  * Atomically decrements by one the current value. 
  * 
  * @return the previous value 
  */ 
 public final int getAndDecrement() { 
  for (;;) { 
   int current = get(); 
   int next = current - 1; 
   if (compareAndSet(current, next)) 
    return current; 
  } 
 }

8. 总结

本文主要对Java中常被忽略 但 非常重要的关键字Synchronized进行讲解

到此这篇关于Java的Synchronized关键字学习指南的文章就介绍到这了,更多相关Java的Synchronized关键字内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!


推荐阅读
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • Java String与StringBuffer的区别及其应用场景
    本文主要介绍了Java中String和StringBuffer的区别,String是不可变的,而StringBuffer是可变的。StringBuffer在进行字符串处理时不生成新的对象,内存使用上要优于String类。因此,在需要频繁对字符串进行修改的情况下,使用StringBuffer更加适合。同时,文章还介绍了String和StringBuffer的应用场景。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • Tomcat/Jetty为何选择扩展线程池而不是使用JDK原生线程池?
    本文探讨了Tomcat和Jetty选择扩展线程池而不是使用JDK原生线程池的原因。通过比较IO密集型任务和CPU密集型任务的特点,解释了为何Tomcat和Jetty需要扩展线程池来提高并发度和任务处理速度。同时,介绍了JDK原生线程池的工作流程。 ... [详细]
  • 本文介绍了Java高并发程序设计中线程安全的概念与synchronized关键字的使用。通过一个计数器的例子,演示了多线程同时对变量进行累加操作时可能出现的问题。最终值会小于预期的原因是因为两个线程同时对变量进行写入时,其中一个线程的结果会覆盖另一个线程的结果。为了解决这个问题,可以使用synchronized关键字来保证线程安全。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 如何在服务器主机上实现文件共享的方法和工具
    本文介绍了在服务器主机上实现文件共享的方法和工具,包括Linux主机和Windows主机的文件传输方式,Web运维和FTP/SFTP客户端运维两种方式,以及使用WinSCP工具将文件上传至Linux云服务器的操作方法。此外,还介绍了在迁移过程中需要安装迁移Agent并输入目的端服务器所在华为云的AK/SK,以及主机迁移服务会收集的源端服务器信息。 ... [详细]
  • 本文介绍了如何使用python从列表中删除所有的零,并将结果以列表形式输出,同时提供了示例格式。 ... [详细]
  • 本文介绍了南邮ctf-web的writeup,包括签到题和md5 collision。在CTF比赛和渗透测试中,可以通过查看源代码、代码注释、页面隐藏元素、超链接和HTTP响应头部来寻找flag或提示信息。利用PHP弱类型,可以发现md5('QNKCDZO')='0e830400451993494058024219903391'和md5('240610708')='0e462097431906509019562988736854'。 ... [详细]
  • win10电脑蓝屏代码0x000000a5无法进入系统解决方法详解
    许多用户在使用电脑的时候遇到蓝屏问题,重启无法进入系统。本文提供了解决方法:调整BIOS设置、禁用安全启动、重装系统等。如果以上方法都无法解决问题,需要重新安装一个系统。详细步骤请参考正文内容。 ... [详细]
  • A题这题贼水,直接暴力就可以了。用个bool数组记录一下,如果某一天,当前剩下的最大的出现了的话,就输出一段。1#include<stdio.h>2intn;3boolvi ... [详细]
  • #-*-coding:utf-8-*-print(upython与开源QGis课题研究组)#print(汉字)##创建矢量数据文件#try:fromosgeoimporto ... [详细]
  • 主函数:CStringGetCPUIDString()Copyright(C)shangweixiao2011 ... [详细]
author-avatar
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有