在Java中,对于synchronized关键字,大家看到的第一反应就是这个关键字是进行同步操作的,即得名“同步锁”。

  • 当用它来修饰方法和代码块时,默认当前的对象为锁的对象,即对象锁。

  • 当用来修饰类和静态方法时,默认当前的类为锁的对象

对象锁

修饰在方法上时,多个线程调用同一对象的同步方法时会阻塞,调用不同对象的同步方法时不会阻塞。

在多线程环境下,调用不同对象的同步方法:

public class SynchronizedDemo {

    public synchronized void synTest(){
int i = 5;
while (i-- > 0){
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static void main(String[] args) {
SynchronizedDemo demo1 = new SynchronizedDemo();
SynchronizedDemo demo2 = new SynchronizedDemo(); Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
demo1.obj3();
}
}); Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
demo2.obj3();
}
}); t1.start();
t2.start();
}
}

Output:

Thread-0 : 4
Thread-1 : 4
Thread-0 : 3
Thread-1 : 3
Thread-0 : 2
Thread-1 : 2
Thread-0 : 1
Thread-1 : 1
Thread-0 : 0
Thread-1 : 0


在多线程环境下,调用同一对象的同步方法:

public class SynchronizedDemo {

    public synchronized void synTest(){
int i = 5;
while (i-- > 0){
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static void main(String[] args) {
SynchronizedDemo demo1 = new SynchronizedDemo();
SynchronizedDemo demo2 = new SynchronizedDemo(); Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
demo1.synTest();
}
}); Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
demo1.synTest();
}
}); t1.start();
t2.start();
}
}

Output:

Thread-0 : 4
Thread-0 : 3
Thread-0 : 2
Thread-0 : 1
Thread-0 : 0
Thread-1 : 4
Thread-1 : 3
Thread-1 : 2
Thread-1 : 1
Thread-1 : 0


在多线程环境下,调用不同对象通过this修饰的局部代码块

public class SynchronizedDemo {

    public void synTest(){
synchronized (this){
int i = 5;
while (i-- > 0){
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} } public static void main(String[] args) { SynchronizedDemo demo1 = new SynchronizedDemo();
SynchronizedDemo demo2 = new SynchronizedDemo(); Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
demo1.synTest();
}
}); Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
demo2.synTest();
}
}); t1.start();
t2.start();
}
}

Output:

Thread-0 : 4
Thread-1 : 4
Thread-0 : 3
Thread-1 : 3
Thread-0 : 2
Thread-1 : 2
Thread-0 : 1
Thread-1 : 1
Thread-0 : 0
Thread-1 : 0

对于this修饰的其实指的就是类的实例,所以它也属于对象锁,并不是类锁。


在多线程环境下,调用不同对象通过其他实例类修饰的局部代码块

public class SynchronizedDemo {

    public void synTest(){
String str = new String("lock");
synchronized (str){
int i = 5;
while (i-- > 0){
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} } public static void main(String[] args) { SynchronizedDemo demo1 = new SynchronizedDemo();
SynchronizedDemo demo2 = new SynchronizedDemo(); Thread t1 = new Thread(() -> {
demo1.synTest();
}); Thread t2 = new Thread(() -> {
demo2.synTest();
}); t1.start();
t2.start();
}
}

Output:

Thread-0 : 4
Thread-1 : 4
Thread-1 : 3
Thread-0 : 3
Thread-1 : 2
Thread-0 : 2
Thread-1 : 1
Thread-0 : 1
Thread-1 : 0
Thread-0 : 0

我们可以看到,我们通过每次调用时实例一个String来进行同步代码块,但是并没有发生阻塞,因为每次生成的是一个实例String,锁的是String,每次都是不一样的,所以不会发生阻塞。


可以通过上述的运行结果可以得到一下结论:

在多线程环境下:

  • 调用不同对象的同步方法,不会发生阻塞
  • 调用相同对象的同步方法,会发生阻塞
  • 调用不同对象通过this修饰的局部代码块,不会发生阻塞
  • 调用不同对象通过其他实例类修饰的同步代码块,不会发生阻塞


类锁

在多线程环境下,多次调用类的静态同步方法:

public class SynchronizedDemo {

    public static synchronized void synTest(){
int i = 5;
while (i-- > 0){
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static void main(String[] args) { Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
SynchronizedDemo.synTest();
}
}); Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
SynchronizedDemo.synTest();
}
}); t1.start();
t2.start();
}

Output:

Thread-0 : 4
Thread-0 : 3
Thread-0 : 2
Thread-0 : 1
Thread-0 : 0
Thread-1 : 4
Thread-1 : 3
Thread-1 : 2
Thread-1 : 1
Thread-1 : 0

在多线程环境下,多次调用被类锁的代码块:

public class SynchronizedDemo {

    public void synTest(){
synchronized (SynchronizedDemo.class){
int i = 5;
while (i-- > 0){
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} } public static void main(String[] args) { SynchronizedDemo demo1 = new SynchronizedDemo();
SynchronizedDemo demo2 = new SynchronizedDemo(); Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
demo1.synTest();
}
}); Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
demo2.synTest();
}
}); t1.start();
t2.start();
}
}

Output:

Thread-1 : 4
Thread-1 : 3
Thread-1 : 2
Thread-1 : 1
Thread-1 : 0
Thread-0 : 4
Thread-0 : 3
Thread-0 : 2
Thread-0 : 1
Thread-0 : 0

对于对象SynchronizedDemo.class,实际上就是SynchronizedDemo这个类,也就是对类进行加锁。

可以通过上述的运行结果可以得到一下结论:

在多线程环境下:

  • 多次调用静态的同步方法,会进行阻塞
  • 不同对象调用被类锁的同步代码块,会进行阻塞


类锁和对象锁同时存在

在多线程环境下,同时调用同一对象的类锁和对象锁

public class SynchronizedDemo {

    public static synchronized void synTestStatic() {
int i = 5;
while (i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public synchronized void synTest() {
int i = 5;
while (i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static void main(String[] args) { SynchronizedDemo demo1 = new SynchronizedDemo(); Thread t1 = new Thread(() -> {
demo1.synTest();
}); Thread t2 = new Thread(() -> {
SynchronizedDemo.synTestStatic();
}); t1.start();
t2.start();
}
}

Output:

Thread-1 : 4
Thread-0 : 4
Thread-1 : 3
Thread-0 : 3
Thread-1 : 2
Thread-0 : 2
Thread-1 : 1
Thread-0 : 1
Thread-1 : 0
Thread-0 : 0

我们可以到看到,在多线程环境下,类锁和对象锁同时存在的情况下,多线程访问时不会阻塞,因为他们不是同一个锁。


可以通过上述的运行结果可以得到一下结论:

在多线程环境下:

  • 类锁和对象锁同时存在的情况下,不会发生阻塞


总结

Java的类锁、对象锁和方法锁的更多相关文章

  1. 第31节:Java基础-类与对象

    前言 Java基础-类与对象,方法的重载,构造方法的重载,static关键字,main()方法,this关键字,包,访问权限,类的继承,继承性,方法的重写,super变量. 方法的重载:成员方法的重载 ...

  2. Java面向对象-类与对象

    Java面向对象-类与对象 类与对象的关系 我们通俗的举个例子,比如人类是一种类,张三这个人就是人类的具体的一个个体,也就是java中的对象:这就是一个类与对象的关系: 类的定义 下面看实例 类的创建 ...

  3. 关于Java构造类与对象的思考

    简单记录一下Java构造类与对象时的流程以及this和super对于特殊例子的分析. 首先,接着昨天的问题,我做出了几个变形: Pic1.原版: Pic2.去掉了T.foo方法中的this关键字: P ...

  4. java基础---类和对象(4)

    一. static关键字 使用static关键字修饰成员变量表示静态的含义,此时成员变量由对象层级提升为类层级,整个类共享一份静态成员变量,该成员变量随着类的加载准备就绪,与是否创建对象无关 使用st ...

  5. day 23 对象的名称空间 类,对象属性和方法 封装 接口提供

    一.对象的特有名称空间 # 对象独有的名称空间:在产生对象时就赋初值 '''class ted: def func(): 当func里不存在参数时,调用时不需要给值 print('hah')ted.f ...

  6. 类的封装,property特性,类与对象的绑定方法和非绑定方法,

    类的封装 就是把数据或者方法封装起来 为什么要封装 封装数据的主要原因是:保护隐私 封装方法的主要原因是:隔离复杂度(快门就是傻瓜相机为傻瓜们提供的方法,该方法将内部复杂的照相功能都隐藏起来了,比如你 ...

  7. java类与对象(属性,方法)的使用

    ---恢复内容开始--- 类和对象是java编程中很重要的应该面向对象的一课,实际上可以将类看作对象的载体,它定义了对象所具有的功能.Java是面向对象的语言,因此掌握类与对象是学习Java语言的基础 ...

  8. Java面向对象——类,对象和方法

    1.类的概念 在生活中,说到类,可以联想到类别,同类,会想到一类人,一类事物等等.而这一类人或事物都是具有相同特征或特点和行为的,我们根据不同的特征或特点和行为将他们归类或分类.同时,当我们认识一个新 ...

  9. Java面向对象~类和对象&方法,类方法

    面向对象 概念:     1.同一类事物的抽象描述,不是具体的    2.类和对象的关系:        类 是抽象的.        对象 是具体的.    3.对象的体征,称为"属性&q ...

  10. JS创建类和对象(好多方法哟!)

    http://www.cnblogs.com/tiwlin/archive/2009/08/06/1540161.html 这是别人写的~~~我借来看看 JavaScript 创建类/对象的几种方式 ...

随机推荐

  1. JAVA中基础类型和字串类型之间的相互转换

    转自:https://www.imooc.com/code/2251 仅做个人学习记录之用,侵删. 在程序开发中,我们经常需要在基本数据类型和字符串之间进行转换. 其中,基本类型转换为字符串有三种方法 ...

  2. VSCode设置大小写转换的快捷键

    本文已同步到专业技术网站 www.sufaith.com, 该网站专注于前后端开发技术与经验分享, 包含Web开发.Nodejs.Python.Linux.IT资讯等板块. VSCode在默认情况下没 ...

  3. String 对象-->toLowerCase() 方法

    1.定义和用法 将字符串中所有的大写字符转换成小写字符,小写字符不变 返回转换后的结果字符串 语法: string.toLowerCase() 注意:不会改变字符串本身,仅以返回值的形式返回结果 举例 ...

  4. Python设计模式(11)-状态模式

    # coding=utf-8 # *状态模式:一个方法的判断逻辑太长,就不容易修改.方法过长,其本质就是,# * 就是本类在不同条件下的状态转移.状态模式,就是将这些判断分开到各个能# * 表示当前状 ...

  5. 当Spring Cloud Alibaba Sentinel碰上Spring Cloud Sleuth会擦出怎样的火花

    前言 今年主要会做一个比较完整的微服务项目开源出来.目前已经开始了,刚兴趣的先Star一个吧. 项目:https://github.com/yinjihuan/kitty-cloud 基础框架:htt ...

  6. Python程序设计实验报告四:循环结构程序设计(设计型实验)

    安徽工程大学 Python程序设计 实验报告 班级   物流191   姓名  姚彩琴  学号3190505129 成绩 日期     2020.4.8     指导老师       修宇 [实验名称 ...

  7. Labyrinth 树的直径加DFS

    The northern part of the Pyramid contains a very large and complicated labyrinth. The labyrinth is d ...

  8. Redis安装部署(一主二从三哨兵)

    需求:根据当前客户的生产环境,模拟安装部署Redis的测试环境,方便后续的功能测试. 1.准备工作 2.安装编译Redis 3.Redis运行环境配置 4.Redis启动和关闭 1.准备工作 Redi ...

  9. Jmeter系列(1)- 环境部署

    如果你想从头学习Jmeter,可以看看这个系列的文章哦 https://www.cnblogs.com/poloyy/category/1746599.html 官网下载Jmeter http://j ...

  10. Java中的字符串操作(比较String,StringBuiler和StringBuffer)

    一.前言 刚开始学习Java时,作为只会C语言的小白,就为其中的字符串操作而感到震撼.相比之下,C语言在字节数组中保存一个结尾的\0去表示字符串,想实现字符串拼接,还需要调用strcpy库函数或者自己 ...