- 浏览: 7928885 次
- 性别:
- 来自: 广州
文章分类
- 全部博客 (2425)
- 软件工程 (75)
- JAVA相关 (662)
- ajax/web相关 (351)
- 数据库相关/oracle (218)
- PHP (147)
- UNIX/LINUX/FREEBSD/solaris (118)
- 音乐探讨 (1)
- 闲话 (11)
- 网络安全等 (21)
- .NET (153)
- ROR和GOG (10)
- [网站分类]4.其他技术区 (181)
- 算法等 (7)
- [随笔分类]SOA (8)
- 收藏区 (71)
- 金融证券 (4)
- [网站分类]5.企业信息化 (3)
- c&c++学习 (1)
- 读书区 (11)
- 其它 (10)
- 收藏夹 (1)
- 设计模式 (1)
- FLEX (14)
- Android (98)
- 软件工程心理学系列 (4)
- HTML5 (6)
- C/C++ (0)
- 数据结构 (0)
- 书评 (3)
- python (17)
- NOSQL (10)
- MYSQL (85)
- java之各类测试 (18)
- nodejs (1)
- JAVA (1)
- neo4j (3)
- VUE (4)
- docker相关 (1)
最新评论
-
xiaobadi:
jacky~~~~~~~~~
推荐两个不错的mybatis GUI生成工具 -
masuweng:
(转)JAVA获得机器码的实现 -
albert0707:
有些扩展名为null
java 7中可以判断文件的contenttype了 -
albert0707:
非常感谢!!!!!!!!!
java 7中可以判断文件的contenttype了 -
zhangle:
https://zhuban.me竹板共享 - 高效便捷的文档 ...
一个不错的网络白板工具
https://mp.weixin.qq.com/s/b3Sx2IDs6pJ3dANRIdPwRw
synchronized在非静态上下文中的应用解析
首先,我们先看一个代码例子,然后再详细分析一下为什么会这样。
1
//模拟一个线程,探究在编程实践中
//synchronized是如何持有对象锁
//如何实现同步控制的
public class DemoA extends Thread{
private String msg;
public DemoA(String str){
this.msg = str;
}
public synchronized void print(String str){
while(true){
try {
//模拟一个耗时操作,调用sleep函数
//会释放占用的CPU,但是不会释放对象锁
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(str);
}
}
//覆写Thread的run方法
public void run(){
print(msg);
}
}
2
public class DemoB extends Thread{
private DemoA demoA;
public DemoB(DemoA a){
demoA = a;
}
//覆写Thread方法
//委托DemoA对象,调用操作
public void run(){
demoA.print("B");
}
}
3
//非静态上下文
//synchronized修饰符在同步控制中的作用
public class NoStaticSyncTest {
public static void main(String[] args){
DemoA demoA1 = new DemoA("A1");
demoA1.start(); //线程A1
DemoB demoB = new DemoB(demoA1);
demoB.start(); //线程B
DemoA demoA2 = new DemoA("A2");
demoA2.start(); //线程A2
}
}
老铁们可以先想一想该程序的运行结果,然后再往下看。
线程A1通过对象demoA1启动,并调用了被synchronized修饰的print成员函数,按照我们上文(NO.25 synchronized关键字画像:开胃菜)所提到的,线程要进入并执行该成员函数,需要先获取demoA1所关联锁,由于该锁当前并没有被任何线程使用,所以线程A1顺利的获取了demoA1的所关联的lock,并开始执行print函数。
紧接着,线程B通过对象demoB启动,并通过传入的demoA1对象引用,调用了被synchronized修饰的print成员函数;同理,要执行该函数,需要获取调用该函数的对象demoA1所持有的锁,由于该锁已经被线程A1执行过程所持有,为此线程B只能等待线程A1执行完成释放demoA1所持有的锁以后,才能得到执行的机会;但是,print函数是一个“死”循环,为此线程B会一直被阻塞着,直到JVM退出而终止。
最后,线程A2通过对象demoA2启动,并调用了被synchronized修饰的print成员函数;同理,线程要进入并执行该成员函数,需要先获取demoA2所关联锁,由于该锁(由于每个对象都会有一个与之关联的lock,即使属于同一类型的对象所持有的锁也是不同的,因此该锁不同于demoA1所关联的锁)当前并没有被任何线程使用,所以线程A2顺利的获取了demoA2的所关联的lock,并开始执行print函数。
通过以上分析,线程A1与线程A2会交替执行,线程B会因为一直不能获取到demoA1所关联的锁而被永久阻塞。因此,该函数的输出结果,“可能”会是这样:
结果
A1
A2
A1
A2
A1
A2
02、synchronized在静态上下文中的应用解析
同样地,我们先看一个代码例子,然后再详细分析一下为什么会这样。
4
public class DemoC implements Runnable{
private String msg;
public DemoC(String str){
msg = str;
}
public synchronized void print1(String str){
while(true){
try {
//模拟一个耗时操作,调用sleep函数
//会释放占用的CPU,但是不会释放对象锁
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(str);
}
}
public synchronized static void print2(String str){
while(true){
try {
//模拟一个耗时操作,调用sleep函数
//会释放占用的CPU,但是不会释放对象锁
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(str);
}
}
public void run(){
print1(msg);
}
}
5
public class StaticSyncTest {
public static void main(String[] args){
DemoC demoC1 = new DemoC("C1");
Thread c1 = new Thread(demoC1);
c1.start();
demoC1.print2("C2");
}
}
老铁们可以先想一想该程序的运行结果,接着,我们再一起往下看。
该程序首先定义了一个对象引用demoC1,然后启动线程c1,该线程会执行被synchronized修饰的print1函数,进入print1函数前,会首先判断是否持有对象引用demoC1的锁,由于该锁尚未被其他线程所占用,为此该线程会被执行;接着demoC1会调用print2函数,该函数同时被synchronized与static关键字修饰,按照我们上文所提及的方法,要进入该函数,需要持有DemoC.class对象所关联lock,由于该对象尚未被其他线程所持有,为此该函数也将被执行。
通过以上分析,线程c1与demoC1.print2都会被执行。因此,该函数的输出结果,“可能”会是这样:
结果
C1
C2
C1
C2
C2
C1
C2
03、合理利用synchronized实现线程同步
通过以上分析,我们就基本掌握了synchronized在非静态上下文与静态上下文中是如何在线程间同步发挥作用的。
推而广之,我们一般有三种利用synchronized方法实现线程同步的方法:
第一种:修饰方法
6
//synchronized修饰成员函数
//该函数能被执行,则意味则持有了调用
//该函数的对象引用所持有的lock
//多个函数如果存在同步关系,则可以在这
//些函数前面都增加synchronize修饰
public synchronized void subroutine1(){
}
第二种:修饰一般对象或this对象
7
//多个函数如果存在同步关系,则可以
//用synchronize修饰this对象
public synchronized void subroutine1(){
synchronized(this){
}
}
private DemoA demoA = new DemoA("A");
//多个函数如果存在同步关系,则可以
//用synchronize修饰demoA对象
public synchronized void subroutine2(){
synchronized(demoA){
}
}
第三种,修饰轻量级对象
8
private byte[] lock = new byte[1];
//多个函数如果存在同步关系,则可以
//用synchronize修饰lock对象
//lock是一个轻量级对象
public void subroutine2(){
synchronized(lock){
}
}
为了适应高并发性能以及快速响应的要求,synchronized不同的写法对程序响应的快慢和对CPU等资源利用率是不同的;对比以上方式,从程序运行性能与执行效率来看,从高到低依次排序为:第三种方式 > 第二种方式 > 第一种方式。
划重点
关键字synchronized锁定的是对象,而不是函数或代码。函数或代码区块被声明或修饰为synchronized,并非意味着它同一时刻只能由一个线程执行。
因为锁定的对象不一样,加锁与解锁都需要此对象资源,为此锁定的对象资源越小,性能开销就越小,采用byte作为锁对象最为经济。
当synchronized用于修饰对象引用时,则取得的lock将被交给该引用所指向的对象。
当调用一个synchronized static 函数时,获得的lock将与定义该函数的class相关联,而不是与调用函数的那个对象相关联。当synchronized修饰的是A.class时,获得的lock也是与上述相同,即与class相关联。
synchronized在非静态上下文中的应用解析
首先,我们先看一个代码例子,然后再详细分析一下为什么会这样。
1
//模拟一个线程,探究在编程实践中
//synchronized是如何持有对象锁
//如何实现同步控制的
public class DemoA extends Thread{
private String msg;
public DemoA(String str){
this.msg = str;
}
public synchronized void print(String str){
while(true){
try {
//模拟一个耗时操作,调用sleep函数
//会释放占用的CPU,但是不会释放对象锁
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(str);
}
}
//覆写Thread的run方法
public void run(){
print(msg);
}
}
2
public class DemoB extends Thread{
private DemoA demoA;
public DemoB(DemoA a){
demoA = a;
}
//覆写Thread方法
//委托DemoA对象,调用操作
public void run(){
demoA.print("B");
}
}
3
//非静态上下文
//synchronized修饰符在同步控制中的作用
public class NoStaticSyncTest {
public static void main(String[] args){
DemoA demoA1 = new DemoA("A1");
demoA1.start(); //线程A1
DemoB demoB = new DemoB(demoA1);
demoB.start(); //线程B
DemoA demoA2 = new DemoA("A2");
demoA2.start(); //线程A2
}
}
老铁们可以先想一想该程序的运行结果,然后再往下看。
线程A1通过对象demoA1启动,并调用了被synchronized修饰的print成员函数,按照我们上文(NO.25 synchronized关键字画像:开胃菜)所提到的,线程要进入并执行该成员函数,需要先获取demoA1所关联锁,由于该锁当前并没有被任何线程使用,所以线程A1顺利的获取了demoA1的所关联的lock,并开始执行print函数。
紧接着,线程B通过对象demoB启动,并通过传入的demoA1对象引用,调用了被synchronized修饰的print成员函数;同理,要执行该函数,需要获取调用该函数的对象demoA1所持有的锁,由于该锁已经被线程A1执行过程所持有,为此线程B只能等待线程A1执行完成释放demoA1所持有的锁以后,才能得到执行的机会;但是,print函数是一个“死”循环,为此线程B会一直被阻塞着,直到JVM退出而终止。
最后,线程A2通过对象demoA2启动,并调用了被synchronized修饰的print成员函数;同理,线程要进入并执行该成员函数,需要先获取demoA2所关联锁,由于该锁(由于每个对象都会有一个与之关联的lock,即使属于同一类型的对象所持有的锁也是不同的,因此该锁不同于demoA1所关联的锁)当前并没有被任何线程使用,所以线程A2顺利的获取了demoA2的所关联的lock,并开始执行print函数。
通过以上分析,线程A1与线程A2会交替执行,线程B会因为一直不能获取到demoA1所关联的锁而被永久阻塞。因此,该函数的输出结果,“可能”会是这样:
结果
A1
A2
A1
A2
A1
A2
02、synchronized在静态上下文中的应用解析
同样地,我们先看一个代码例子,然后再详细分析一下为什么会这样。
4
public class DemoC implements Runnable{
private String msg;
public DemoC(String str){
msg = str;
}
public synchronized void print1(String str){
while(true){
try {
//模拟一个耗时操作,调用sleep函数
//会释放占用的CPU,但是不会释放对象锁
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(str);
}
}
public synchronized static void print2(String str){
while(true){
try {
//模拟一个耗时操作,调用sleep函数
//会释放占用的CPU,但是不会释放对象锁
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(str);
}
}
public void run(){
print1(msg);
}
}
5
public class StaticSyncTest {
public static void main(String[] args){
DemoC demoC1 = new DemoC("C1");
Thread c1 = new Thread(demoC1);
c1.start();
demoC1.print2("C2");
}
}
老铁们可以先想一想该程序的运行结果,接着,我们再一起往下看。
该程序首先定义了一个对象引用demoC1,然后启动线程c1,该线程会执行被synchronized修饰的print1函数,进入print1函数前,会首先判断是否持有对象引用demoC1的锁,由于该锁尚未被其他线程所占用,为此该线程会被执行;接着demoC1会调用print2函数,该函数同时被synchronized与static关键字修饰,按照我们上文所提及的方法,要进入该函数,需要持有DemoC.class对象所关联lock,由于该对象尚未被其他线程所持有,为此该函数也将被执行。
通过以上分析,线程c1与demoC1.print2都会被执行。因此,该函数的输出结果,“可能”会是这样:
结果
C1
C2
C1
C2
C2
C1
C2
03、合理利用synchronized实现线程同步
通过以上分析,我们就基本掌握了synchronized在非静态上下文与静态上下文中是如何在线程间同步发挥作用的。
推而广之,我们一般有三种利用synchronized方法实现线程同步的方法:
第一种:修饰方法
6
//synchronized修饰成员函数
//该函数能被执行,则意味则持有了调用
//该函数的对象引用所持有的lock
//多个函数如果存在同步关系,则可以在这
//些函数前面都增加synchronize修饰
public synchronized void subroutine1(){
}
第二种:修饰一般对象或this对象
7
//多个函数如果存在同步关系,则可以
//用synchronize修饰this对象
public synchronized void subroutine1(){
synchronized(this){
}
}
private DemoA demoA = new DemoA("A");
//多个函数如果存在同步关系,则可以
//用synchronize修饰demoA对象
public synchronized void subroutine2(){
synchronized(demoA){
}
}
第三种,修饰轻量级对象
8
private byte[] lock = new byte[1];
//多个函数如果存在同步关系,则可以
//用synchronize修饰lock对象
//lock是一个轻量级对象
public void subroutine2(){
synchronized(lock){
}
}
为了适应高并发性能以及快速响应的要求,synchronized不同的写法对程序响应的快慢和对CPU等资源利用率是不同的;对比以上方式,从程序运行性能与执行效率来看,从高到低依次排序为:第三种方式 > 第二种方式 > 第一种方式。
划重点
关键字synchronized锁定的是对象,而不是函数或代码。函数或代码区块被声明或修饰为synchronized,并非意味着它同一时刻只能由一个线程执行。
因为锁定的对象不一样,加锁与解锁都需要此对象资源,为此锁定的对象资源越小,性能开销就越小,采用byte作为锁对象最为经济。
当synchronized用于修饰对象引用时,则取得的lock将被交给该引用所指向的对象。
当调用一个synchronized static 函数时,获得的lock将与定义该函数的class相关联,而不是与调用函数的那个对象相关联。当synchronized修饰的是A.class时,获得的lock也是与上述相同,即与class相关联。
发表评论
-
复习:强迫线程顺序执行方式
2019-01-03 23:42 1556方法1: 三个线程,t1,t2,t3,如果一定要按顺序执行, ... -
(转)不错的前后端处理异常的方法
2019-01-02 23:16 2015前言 在 Web 开发中, 我们经常会需要处理各种异常, 这是 ... -
info q的极客时间大咖说等资料下载
2018-08-15 08:40 3460info q的极客时间大咖说等资料下载,还有不少思维导图 链 ... -
CXF 客户端超时时间设置(非Spring配置方式)
2018-07-03 22:38 2225import org.apache.cxf.endpoint. ... -
CountDownLatch的例子
2018-06-13 14:10 676public class StatsDemo { ... -
两道面试题,带你解析Java类加载机制
2018-06-12 16:29 602https://mp.weixin.qq.com/s/YTa0 ... -
Spring中获取request的几种方法,及其线程安全性分析
2018-06-11 09:03 664https://mp.weixin.qq.com/s/KeFJ ... -
内部类小结
2018-06-06 10:25 432https://mp.weixin.qq.com/s/hErv ... -
JVM虚拟机小结1
2018-06-04 20:43 5271 jps -l //列出详细的类名和进程ID 2)jps ... -
windows下自带命令行工具查看CPU资源情况等
2018-06-04 12:53 3093微软提供了不少命令行 ... -
(收藏)深入分析Java的序列化与反序列化
2018-05-30 15:21 610https://mp.weixin.qq.com/s/T2Bn ... -
apache common包中的序列化工具
2018-05-30 09:10 1840什么是序列化 我们的 ... -
JAVA8 JVM的变化: 元空间(Metaspace)
2018-05-24 22:30 957本文将会分享至今为至我收集的关于永久代(Permanent G ... -
(转)服务器性能指标(一)——负载(Load)分析及问题排查
2018-05-21 21:03 1354原创: Hollis Hollis 负载 ... -
(转)对象复用
2018-05-20 15:27 850public class Student { priv ... -
mapreduce中入门中要注意的几点
2018-05-06 08:59 665在 mapreduce中,比如有如下的词: I love b ... -
HDFS的基本操作
2018-05-02 21:47 931-mkdir 在HDFS创建目录 ... -
一个不错的开源工具类,专门用来解析日志头部的,好用
2018-05-02 20:00 764一个不错的开源工具类,专门用来解析日志头部的,好用。 http ... -
介绍个不错的RESTFUL MOCK的工具wiremock
2018-04-27 21:02 1901介绍个不错的RESTFUL MOCK的工具wiremock,地 ... -
LINUX下EPOLL等不错的文章收藏
2018-04-25 09:35 5531 通俗讲解 异步,非阻塞和 IO 复用 https:/ ...
相关推荐
本文将深入探讨synchronized关键字的工作原理、使用方式以及在实际编程中的应用。 synchronized关键字是Java中实现线程同步的重要工具。通过本文的探讨,我们了解到了synchronized的工作原理、基本用法、以及在实际...
Java中的`synchronized`关键字是多线程编程中的一个重要概念,用于控制并发访问共享资源时的同步机制。在Java中,当多个线程试图同时访问和修改同一块代码或数据时,可能会导致数据不一致的问题。为了解决这个问题,...
Java 线程同步机制中 synchronized 关键字的理解 Java 的线程同步机制是为了解决多个线程共享同一片存储空间所带来的访问冲突问题。其中,synchronized 关键字是 Java 语言中解决这种冲突的重要机制。 ...
java多线程中synchronized关键字的用法 解压密码 www.jiangyea.com
`synchronized`关键字有两种主要的使用方式:一种是在方法声明中使用,另一种则是在代码块中使用。 ##### 1. synchronized方法 在方法声明中添加`synchronized`关键字,可以将整个方法体变成同步代码块。例如: `...
synchronized 关键字是 Java 中解决线程同步问题的重要工具,但是需要正确的用法,否则将无法实现线程平安。在编写代码时,需要仔细考虑 synchronized 关键字的使用,以确保代码的线程平安和高效运行。 此外,...
重点解析了synchronized的工作原理,包括其底层原理、Monitor监视器锁的工作方式,以及Java对象的内存布局。文中详细介绍了synchronized在JVM中的实现,侧重于其内部对象Monitor(监视器锁)的实现原理。讨论了监视...
通过`synchronized`关键字,开发者可以控制代码块或方法的并发访问,从而确保数据的一致性和程序的正确性。 #### 使用场景 1. **同步代码块**:可以通过`synchronized`关键字来声明同步代码块,即通过指定对象锁来...
在Java编程语言中,`synchronized`关键字是一个至关重要的概念,尤其在多线程环境下,它用于控制对共享资源的并发访问,确保线程安全。本教程将深入讲解`synchronized`关键字及其在Java多线程中的应用。 一、...
### Lock接口与synchronized关键字详解 #### 一、概述 在Java并发编程中,Lock接口与synchronized关键字都是实现同步的重要工具。它们虽然都用于控制多线程对共享资源的访问,但在使用方式、功能特性及灵活性方面...
《深入理解Java中的synchronized关键字》 在Java编程语言中,`synchronized`关键字是用于实现线程同步的重要工具,它的本质在于确保多线程环境下的数据一致性与安全性。通过`synchronized`,我们可以控制对共享资源...
Java 并发编程 Synchronized 关键字实现原理 Synchronized 关键字是 Java 并发编程中最基本的同步机制,它可以保证线程安全,包括原子性、可见性和有序性。Synchronized 关键字可以修饰方法或代码块,使得在同一...
"Java 多线程与并发(4-26)-关键字- synchronized详解" Java 多线程与并发中的 synchronized 关键字是实现同步块的互斥访问和线程的阻塞及唤醒等工作的重要工具。下面将对 synchronized 关键字进行详细分析。 ...
Java中的`synchronized`关键字是用于实现线程同步的关键机制,它的主要目的是确保在多线程环境中,对共享资源的访问能够保持数据的一致性和完整性。本文将深入探讨`synchronized`的两种主要用法:synchronized方法和...
Java 中的 synchronized 用法详解 Java 中的 synchronized 关键字是用于解决多线程并发问题的重要工具之一。...正确地使用 synchronized 关键字可以帮助开发者们编写更加高效、可靠的多线程程序。
synchronized关键字是Java中实现线程同步的基本工具,它通过锁定对象的monitor来控制对共享资源的并发访问。理解synchronized的工作原理和使用方式对于编写线程安全的Java程序至关重要。然而,由于其局限性,开发者...
本文将探讨Synchronized关键字在解决并发控制中的作用及其使用方式。 首先,让我们回顾一下问题的背景:在给出的示例代码中,创建了10个线程,每个线程都对共享变量`count`进行10000000次的累加操作。理论上,最终...
Javaynchronized关键字使用方式及特性解析 Java中的synchronized关键字是一种实现锁的方式,它是在JVM层面实现的非公平锁。...但是,需要正确地使用synchronized关键字,以避免出现不必要的错误。
并发编程是多线程环境下确保程序正确性的关键技术,而Java中的`synchronized`关键字则是实现并发控制的重要工具。`synchronized`关键字可以用于修饰方法或作为同步代码块,其核心目标是保证线程对共享资源的访问具有...