「 代码性能优化 」作为一名Java程序员,你真的了解 synchronized 吗?(三)

简介: 文接上篇,本文将继续介绍 Synchronized,感兴趣的小伙伴继续跟博主一起讨论下。更多、更体系化的内容请持续关注博主,您的 关注、点赞、收藏 都将是小编持续创作的动力!

前言

文接上篇,本文将继续介绍 Synchronized,感兴趣的小伙伴继续跟博主一起讨论下。

更多、更体系化的内容请持续关注博主,您的 关注、点赞、收藏 都将是小编持续创作的动力!

Synchronized关键字用法

前文中提到synchronized 的用法可以从两个维度上面分类,下面蒋总结讨论具体用法:

1. 根据修饰对象分类

1.1 修饰代码块

synchronized(this|object) {}

synchronized(类.class) {}

1.2 修饰方法

修饰非静态方法

修饰静态方法

2. 根据获取的锁分类

2.1 获取对象锁

synchronized(this|object) {}

修饰非静态方法

2.2 获取类锁

synchronized(类.class) {}

修饰静态方法

Synchronized修饰实例方法

示例:

publicclassSyncDemo {
privateintcount;
publicsynchronizedvoidadd() {
count++;
  }
publicstaticvoidmain(String[] args) throwsInterruptedException {
SyncDemosyncDemo=newSyncDemo();
Threadt1=newThread(() -> {
for (inti=0; i<10000; i++) {
syncDemo.add();
      }
    });
Threadt2=newThread(() -> {
for (inti=0; i<10000; i++) {
syncDemo.add();
      }
    });
t1.start();
t2.start();
t1.join(); // 阻塞住线程等待线程 t1 执行完成t2.join(); // 阻塞住线程等待线程 t2 执行完成System.out.println(syncDemo.count);// 输出结果为 20000  }
}

 

在上面的代码当中的add方法只有一个简单的count++操作,因为这个方法是使用synchronized修饰的因此每一个时刻只能有一个线程执行add方法,因此上面打印的结果是20000。如果add方法没有使用synchronized修饰的话,那么线程t1和线程t2就可以同时执行add方法,这可能会导致最终count的结果小于20000,因为count++操作不具备原子性。

Synchronized修饰静态方法

示例:

publicclassSyncDemo {
privatestaticintcount;
publicstaticsynchronizedvoidadd() {
count++; 
  }
publicstaticvoidmain(String[] args) throwsInterruptedException {
Threadt1=newThread(() -> {
for (inti=0; i<10000; i++) {
SyncDemo.add();
      }
    });
Threadt2=newThread(() -> {
for (inti=0; i<10000; i++) {
SyncDemo.add();
      }
    });
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(SyncDemo.count); // 输出结果为 20000  }
}

上面的代码最终输出的结果也是20000,但是与前一个程序不同的是。这里的add方法用static修饰的,在这种情况下真正的只能有一个线程进入到add代码块,因为用static修饰的话是所有对象公共的,因此和前面的那种情况不同,不存在两个不同的线程同一时刻执行add方法。

Sychronized修饰多个方法

示例:

publicclassAddMinus {
publicstaticintans;
publicstaticsynchronizedvoidadd() {
ans++;
  }
publicstaticsynchronizedvoidminus() {
ans--;
  }
publicstaticvoidmain(String[] args) throwsInterruptedException {
Threadt1=newThread(() -> {
for (inti=0; i<10000; i++) {
AddMinus.add();
      }
    });
Threadt2=newThread(() -> {
for (inti=0; i<10000; i++) {
AddMinus.minus();
      }
    });
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(AddMinus.ans); // 输出结果为 0  }
}

在上面的代码当中我们用synchronized修饰了两个方法,addminus。这意味着在同一个时刻这两个函数只能够有一个被一个线程执行,也正是因为addminus函数在同一个时刻只能有一个函数被一个线程执行,这才会导致ans最终输出的结果等于0。

对于一个实例对象来说:

publicclassAddMinus {
publicintans;
publicsynchronizedvoidadd() {
ans++;
  }
publicsynchronizedvoidminus() {
ans--;
  }
publicstaticvoidmain(String[] args) throwsInterruptedException {
AddMinusaddMinus=newAddMinus();
Threadt1=newThread(() -> {
for (inti=0; i<10000; i++) {
addMinus.add();
      }
    });
Threadt2=newThread(() -> {
for (inti=0; i<10000; i++) {
addMinus.minus();
      }
    });
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(addMinus.ans);
  }
}

上面的代码没有使用static关键字,因此我们需要new出一个实例对象才能够调用addminus方法,但是同样对于AddMinus的实例对象来说同一个时刻只能有一个线程在执行add或者minus方法,因此上面代码的输出同样是0。

Synchronized修饰实例方法代码块

示例:

publicclassCodeBlock {
privateintcount;
publicvoidadd() {
System.out.println("进入了 add 方法");
synchronized (this) {
count++;
    }
  }
publicvoidminus() {
System.out.println("进入了 minus 方法");
synchronized (this) {
count--;
    }
  }
publicstaticvoidmain(String[] args) throwsInterruptedException {
CodeBlockcodeBlock=newCodeBlock();
Threadt1=newThread(() -> {
for (inti=0; i<10000; i++) {
codeBlock.add();
      }
    });
Threadt2=newThread(() -> {
for (inti=0; i<10000; i++) {
codeBlock.minus();
      }
    });
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(codeBlock.count); // 输出结果为 0  }
}

上面的代码当中addminus方法没有使用synchronized进行修饰,因此一个时刻可以有多个线程执行这个两个方法。在上面的synchronized代码块当中我们使用了this对象作为锁对象,只有拿到这个锁对象的线程才能够进入代码块执行,而在同一个时刻只能有一个线程能够获得锁对象。也就是说add函数和minus函数用synchronized修饰的两个代码块同一个时刻只能有一个代码块的代码能够被一个线程执行,因此上面的结果同样是0。

Synchronized修饰静态代码块

示例:

publicclassCodeBlock {
privatestaticintcount;
publicstaticvoidadd() {
System.out.println("进入了 add 方法");
synchronized (CodeBlock.class) {
count++;
    }
  }
publicstaticvoidminus() {
System.out.println("进入了 minus 方法");
synchronized (CodeBlock.class) {
count--;
    }
  }
publicstaticvoidmain(String[] args) throwsInterruptedException {
Threadt1=newThread(() -> {
for (inti=0; i<10000; i++) {
CodeBlock.add();
      }
    });
Threadt2=newThread(() -> {
for (inti=0; i<10000; i++) {
CodeBlock.minus();
      }
    });
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(CodeBlock.count);
  }
}

上面代码的锁对象是CodeBlock.class,这个时候他不再是锁住一个对象了,而是一个类了,这个时候的并发度就变小了,当锁对象是CodeBlock.class的时候,实例对象之间时不能够并发的,因为这个时候的锁对象是一个类。

总结

Synchronized修饰实例方法:不同的对象之间是可以并发的;
Synchronized修饰静态实例方法:不同的对象是不能并发的,但是不同的类之间可以进行并发;
Sychronized修饰多个方法:多个方法在同一时刻只能有一个方法被执行,而且只能有一个线程能够执行;
Synchronized修饰实例方法代码块:同一个时刻只能有一个线程执行代码块;
Synchronized修饰静态代码块:同一个时刻只能有一个线程执行这个代码块,而且不同的对象之间不能够进行并发;

参考&致谢

深入Synchronized各种使用方法

莫笑少年江湖梦,谁不少年梦江湖.感谢前人的经验、分享和付出,让我们可以有机会站在巨人的肩膀上眺望星辰大海!

相关文章
|
12天前
|
Java 测试技术 应用服务中间件
常见 Java 代码缺陷及规避方式(下)
常见 Java 代码缺陷及规避方式(下)
39 0
|
13天前
|
安全 算法 Java
深入理解Java并发编程:线程安全与性能优化
【4月更文挑战第11天】 在Java中,高效的并发编程是提升应用性能和响应能力的关键。本文将探讨Java并发的核心概念,包括线程安全、锁机制、线程池以及并发集合等,同时提供实用的编程技巧和最佳实践,帮助开发者在保证线程安全的前提下,优化程序性能。我们将通过分析常见的并发问题,如竞态条件、死锁,以及如何利用现代Java并发工具来避免这些问题,从而构建更加健壮和高效的多线程应用程序。
|
1天前
|
Java 程序员 数据库连接
Java从入门到精通:3.3.2性能优化与调优——内存管理篇
Java从入门到精通:3.3.2性能优化与调优——内存管理篇
Java从入门到精通:3.3.2性能优化与调优——内存管理篇
|
1天前
|
安全 Java 编译器
是时候来唠一唠synchronized关键字了,Java多线程的必问考点!
本文简要介绍了Java中的`synchronized`关键字,它是用于保证多线程环境下的同步,解决原子性、可见性和顺序性问题。从JDK1.6开始,synchronized进行了优化,性能得到提升,现在仍可在项目中使用。synchronized有三种用法:修饰实例方法、静态方法和代码块。文章还讨论了synchronized修饰代码块的锁对象、静态与非静态方法调用的互斥性,以及构造方法不能被同步修饰。此外,通过反汇编展示了`synchronized`在方法和代码块上的底层实现,涉及ObjectMonitor和monitorenter/monitorexit指令。
6 0
|
6天前
|
安全 Java 开发者
Java并发编程:深入理解Synchronized关键字
【4月更文挑战第19天】 在Java多线程编程中,为了确保数据的一致性和线程安全,我们经常需要使用到同步机制。其中,`synchronized`关键字是最为常见的一种方式,它能够保证在同一时刻只有一个线程可以访问某个对象的特定代码段。本文将深入探讨`synchronized`关键字的原理、用法以及性能影响,并通过具体示例来展示如何在Java程序中有效地应用这一技术。
|
8天前
|
Java
浅谈Java的synchronized 锁以及synchronized 的锁升级
浅谈Java的synchronized 锁以及synchronized 的锁升级
8 0
|
9天前
|
设计模式 运维 安全
深入理解Java并发编程:线程安全与性能优化
【4月更文挑战第15天】在Java开发中,多线程编程是提升应用程序性能和响应能力的关键手段。然而,它伴随着诸多挑战,尤其是在保证线程安全的同时如何避免性能瓶颈。本文将探讨Java并发编程的核心概念,包括同步机制、锁优化、线程池使用以及并发集合等,旨在为开发者提供实用的线程安全策略和性能优化技巧。通过实例分析和最佳实践的分享,我们的目标是帮助读者构建既高效又可靠的多线程应用。
|
12天前
|
Java
代码的魔法师:Java反射工厂模式详解
代码的魔法师:Java反射工厂模式详解
26 0
|
12天前
|
监控 安全 Java
常见 Java 代码缺陷及规避方式(中)
常见 Java 代码缺陷及规避方式(中)
26 1
|
22天前
|
Java
使用Java代码打印log日志
使用Java代码打印log日志
77 1