【并发编程的艺术读书笔记】synchronized关键字与对象锁、类锁

目录

      • 简介
      • 一、synchronized关键字
      • 二、synchronized的类锁、对象锁案例
        • 1、作用在普通同步方法
        • 2、作用在静态方法上
        • 3、作用在同步方法块

简介

本文介绍了synchronized关键字,并给出了对象锁,类锁的案例以及对书中死锁案例的分析。

一、synchronized关键字

synchronized关键字是java中的一个关键字,是一种同步锁,而其实现的基础就是java中的每一个对象都可以作为锁。

表现形式如下:

  • 对于普通同步方法,锁是当前实例对象。
  • 对于静态同步方法,锁是当前类的Class对象。
  • 对于同步方法块,锁是Synchonized括号里配置的对象。

二、synchronized的类锁、对象锁案例

1、作用在普通同步方法

package com.thread;

/**
 * 同步测试
 *
 * @author ez4sterben
 * @date 2023/07/24
 */
public class SyncTest {
    public static void main(String[] args) {
        SyncMethod syncMethod1 = new SyncMethod();
        SyncMethod syncMethod2 = new SyncMethod();

        Thread thread1 = new Thread(){
            @Override
            public void run() {
                try {
                    syncMethod1.syncMethod();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        };
        Thread thread2 = new Thread(){
            @Override
            public void run() {
                try {
                    syncMethod2.syncMethod();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        };

        thread1.start();
        thread2.start();
    }


}

class SyncMethod{

    public synchronized void syncMethod() throws InterruptedException {
        System.out.println("syncMethod开始执行");
        System.out.println(System.currentTimeMillis() + " - syncMethod");
        Thread.sleep(1000);
        System.out.println("syncMethod执行完毕");
    }
    
}

输出结果

syncMethod开始执行
syncMethod开始执行
1690183653777 - syncMethod
1690183653777 - syncMethod
syncMethod执行完毕
syncMethod执行完毕

稍作改动,我们让thread1与thread2争抢对象syncMethod1

Thread thread2 = new Thread(){
            @Override
            public void run() {
                try {
                    syncMethod1.syncMethod();
                    syncMethod2.syncMethod();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        };

输出结果如下

syncMethod开始执行
1690183794778 - syncMethod
syncMethod执行完毕
syncMethod开始执行
1690183795793 - syncMethod
syncMethod执行完毕
syncMethod开始执行
1690183796796 - syncMethod
syncMethod执行完毕

因为两个线程争抢syncMethod1对象,我们的调用的方法是带有synchronized关键字的普通方法,这样会锁住对象导致没有拿到锁的线程阻塞,在方法执行完拿到锁后再执行。

2、作用在静态方法上

package com.thread;

/**
 * 同步测试
 *
 * @author ez4sterben
 * @date 2023/07/24
 */
public class SyncTest {
    public static void main(String[] args) {
        SyncMethod syncMethod1 = new SyncMethod();
        SyncMethod syncMethod2 = new SyncMethod();

        Thread thread1 = new Thread(){
            @Override
            public void run() {
                try {
                    SyncMethod.staticSyncMethod();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        };
        Thread thread2 = new Thread(){
            @Override
            public void run() {
                try {
                    SyncMethod.staticSyncMethod();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        };

        thread1.start();
        thread2.start();
    }


}

class SyncMethod{

    public synchronized void syncMethod() throws InterruptedException {
        System.out.println("syncMethod开始执行");
        System.out.println(System.currentTimeMillis() + " - syncMethod");
        Thread.sleep(1000);
        System.out.println("syncMethod执行完毕");
    }

    public synchronized static void staticSyncMethod() throws InterruptedException {
        System.out.println("staticSyncMethod开始执行");
        System.out.println(System.currentTimeMillis() + " - staticSyncMethod");
        Thread.sleep(1000);
        System.out.println("staticSyncMethod执行完毕");
    }
}

结果如下:

staticSyncMethod开始执行
1690184362260 - staticSyncMethod
staticSyncMethod执行完毕
staticSyncMethod开始执行
1690184363261 - staticSyncMethod
staticSyncMethod执行完毕

对代码作出修改,创建一个新的静态同步方法,两个线程分别调用不同的静态同步方法

package com.thread;

/**
 * 同步测试
 *
 * @author ez4sterben
 * @date 2023/07/24
 */
public class SyncTest {
    public static void main(String[] args) {
        SyncMethod syncMethod1 = new SyncMethod();
        SyncMethod syncMethod2 = new SyncMethod();

        Thread thread1 = new Thread(){
            @Override
            public void run() {
                try {
                    SyncMethod.staticSyncMethod();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        };
        Thread thread2 = new Thread(){
            @Override
            public void run() {
                try {
                    SyncMethod.staticSyncMethod2();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        };

        thread1.start();
        thread2.start();
    }


}

class SyncMethod{

    public synchronized void syncMethod() throws InterruptedException {
        System.out.println("syncMethod开始执行");
        System.out.println(System.currentTimeMillis() + " - syncMethod");
        Thread.sleep(1000);
        System.out.println("syncMethod执行完毕");
    }

    public synchronized static void staticSyncMethod() throws InterruptedException {
        System.out.println("staticSyncMethod开始执行");
        System.out.println(System.currentTimeMillis() + " - staticSyncMethod");
        Thread.sleep(1000);
        System.out.println("staticSyncMethod执行完毕");
    }
    public synchronized static void staticSyncMethod2() throws InterruptedException {
        System.out.println("staticSyncMethod2开始执行");
        System.out.println(System.currentTimeMillis() + " - staticSyncMethod2");
        Thread.sleep(1000);
        System.out.println("staticSyncMethod2执行完毕");
    }
}

结果如下

staticSyncMethod2开始执行
1690184559625 - staticSyncMethod2
staticSyncMethod2执行完毕
staticSyncMethod开始执行
1690184560628 - staticSyncMethod
staticSyncMethod执行完毕

可以看出,整个类中的静态同步方法无法被同时调用,有线程拿到类锁,其他线程就要进入阻塞状态等待类锁。

再对代码作出修改,我们开启三个线程同时调用同步方法以及静态同步方法。

SyncMethod syncMethod1 = new SyncMethod();
        SyncMethod syncMethod2 = new SyncMethod();

        Thread thread1 = new Thread(){
            @Override
            public void run() {
                try {
                    syncMethod1.syncMethod();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        };
        Thread thread2 = new Thread(){
            @Override
            public void run() {
                try {
                    SyncMethod.staticSyncMethod();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        };
        Thread thread3 = new Thread(){
            @Override
            public void run() {
                try {
                    syncMethod1.syncMethod();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        };

        thread1.start();
        thread2.start();
        thread3.start();

syncMethod开始执行
staticSyncMethod开始执行
1690184465603 - staticSyncMethod
1690184465603 - syncMethod
staticSyncMethod执行完毕
syncMethod执行完毕
syncMethod开始执行
1690184466613 - syncMethod
syncMethod执行完毕

从这里我们可以发现,对象锁与类锁其实并不是互斥的,二者可以同时生效

3、作用在同步方法块

这里的案例我们使用书中的死锁案例

public class DeadLockDemo {
    private static String A = "A";
    private static String B = "B";
    public static void main(String[] args) {
        new DeadLockDemo().deadLock();
    }
    private void deadLock() {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (A) {
                    try {
                        Thread.currentThread().sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (B) {
                        System.out.println("1");
                    }
                }
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (B) {
                    synchronized (A) {
                        System.out.println("2");
                    }
                }
            }
        });
        t1.start();
        t2.start();
    }
}

t1线程锁住A后让线程沉睡2s,假设cpu分配了30ms的时间片,t1线程显然无法执行完,这时时间片分配给t2线程,t2对B进行加锁并尝试对A加锁,但此时A已经被t1线程锁住,t2线程只能进行等待,直到t1线程执行完,但当t1线程执行完后尝试对B进行加锁,此时B已经被t2锁住,发生死锁。

你可能感兴趣的:(读书笔记,java,synchronized,对象锁,类锁,死锁,并发编程)