「 代码性能优化 」作为一名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各种使用方法

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

相关文章
|
3天前
|
设计模式 Java
Java设计模式:组合模式的介绍及代码演示
组合模式是一种结构型设计模式,用于将多个对象组织成树形结构,并统一处理所有对象。例如,统计公司总人数时,可先统计各部门人数再求和。该模式包括一个通用接口、表示节点的类及其实现类。通过树形结构和节点的通用方法,组合模式使程序更易扩展和维护。
Java设计模式:组合模式的介绍及代码演示
|
3天前
|
Java 程序员 API
Java中的Lambda表达式:简化代码的秘密武器
在Java 8中引入的Lambda表达式是一种强大的编程工具,它可以显著简化代码,提高可读性。本文将介绍Lambda表达式的基本概念、优势以及在实际开发中的应用。通过具体示例,您将了解如何使用Lambda表达式来简化集合操作、线程编程和函数式编程。让我们一起探索这一革命性的特性,看看它是如何改变Java编程方式的。
17 4
|
3天前
|
Java 开发者
探索Java中的Lambda表达式:简化你的代码
【8月更文挑战第49天】在Java 8的发布中,Lambda表达式无疑是最令人兴奋的新特性之一。它不仅为Java开发者提供了一种更加简洁、灵活的编程方式,而且还极大地提高了代码的可读性和开发效率。本文将通过实际代码示例,展示如何利用Lambda表达式优化和重构Java代码,让你的编程之旅更加轻松愉快。
|
2天前
|
安全 Java 调度
Java 并发编程中的线程安全和性能优化
本文将深入探讨Java并发编程中的关键概念,包括线程安全、同步机制以及性能优化。我们将从基础入手,逐步解析高级技术,并通过实例展示如何在实际开发中应用这些知识。阅读完本文后,读者将对如何在多线程环境中编写高效且安全的Java代码有一个全面的了解。
|
8天前
|
SQL JavaScript 前端开发
基于Java访问Hive的JUnit5测试代码实现
根据《用Java、Python来开发Hive应用》一文,建立了使用Java、来开发Hive应用的方法,产生的代码如下
28 6
|
6天前
|
Java 开发者
探索Java中的Lambda表达式:简化代码,提升效率
【9月更文挑战第14天】本文旨在揭示Java 8中引入的Lambda表达式如何革新了我们编写和管理代码的方式。通过简洁明了的语言和直观的代码示例,我们将一起走进Lambda表达式的世界,了解其基本概念、语法结构以及在实际编程中的应用。文章不仅会展示Lambda表达式的魅力所在,还会指导读者如何在日常工作中有效利用这一特性,以提高编码效率和程序可读性。
|
Java 程序员
java程序员,如何坚持学习下去?
java程序员,如何坚持学习下去?
|
Kubernetes 安全 Java
Java程序员2021年应该学习的11项技能
Java程序员2021年应该学习的11项技能
143 0
Java程序员2021年应该学习的11项技能
|
机器学习/深度学习 分布式计算 Cloud Native
Java 程序员可以学习的技术方向,精通任何一个都可以成为专家
Hello 大家好,相信大家跟阿粉一样,在成为卓越的Java 程序员的路上从未停止过学习,作为一个 Java 程序员还有很多我们需要学习的东西,特别是在这样一个技术快速发展的时期可能昨天还在流行的技术,转眼就已经落后了。那么在 2021 年已经接近尾声的时候有哪些技术我们可以继续不断的学习呢?
Java 程序员可以学习的技术方向,精通任何一个都可以成为专家