并发编程大师系列之:Synchronized的类锁和对象锁

说到并发编程,感觉跟大多数人一样,谈之色变,说它简单把,其实很有内容,说难吧,用起来也挺容易,最近我硬着头皮,决心要把并发编程好好的搞一遍。以前,面试的时候,面试官问,并发编程会吗?嗯,接触过,就加一个synchronized关键字就好了,面试官微笑着说,嗯好。特喵的现在感觉来说,这俩low逼。本来写了几行的软文,但感觉在技术文章里面体现,有失风度,明明可以靠文采吃饭,而我却非要靠技术,任性!上代码!

1.对象锁概念:

java的所有对象都含有1个互斥锁,这个锁由JVM自动获取和释放。线程进入synchronized方法的时候获取该对象的锁,当然如果已经有线程获取了这个对象的锁,那么当前线程会等待;synchronized方法正常返回或者抛异常而终止,JVM会自动释放对象锁。这里也体现了用synchronized来加锁的1个好处,方法抛异常的时候,锁仍然可以由JVM来自动释放。

代码格式:

    // 对象锁:形式1(方法锁)
    public synchronized void Method1() {
        System.out.println("我是对象锁也是方法锁");
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    // 对象锁:形式2(代码块形式)
    public void Method2() {
        synchronized (this) {
            System.out.println("我是对象锁");
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

2.类锁的概念:

由于一个class不论被实例化多少次,其中的静态方法和静态变量在内存中都只有一份。所以,一旦一个静态的方法被申明为synchronized。此类所有的实例化对象在调用此方法,共用同一把锁,我们称之为类锁。

代码格式:

    public synchronized static void Method3() {
        System.out.println("我是类锁");
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

3.代码演示类锁和对象锁

下面这段代码是两个类锁和一个对象锁,拿到锁后,睡1秒钟。

    // 类锁A
    public synchronized static void classLockA() {
        System.out.println("name = " + Thread.currentThread().getName() + ", begain");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("name = " + Thread.currentThread().getName() + ", end");
    }

    // 类锁B
    public synchronized static void classLockB() {
        System.out.println("name = " + Thread.currentThread().getName() + ", begain");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("name = " + Thread.currentThread().getName() + ", end");
    }

    // 对象锁
    public synchronized void objectLock() {
        System.out.println("name = " + Thread.currentThread().getName() + ", begain");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("name = " + Thread.currentThread().getName() + ", end");

    }

创建三个线程类:分别调用一个资源中的三个方法

class ThreadA extends Thread {
    private Test02 test02;
    public ThreadA(Test02 tk) {
        test02 = tk;
    }
    // 调用类锁
    public void run() {
        test02.classLockA();
    }
}

class ThreadB extends Thread {
    private Test02 test02;
    public ThreadB(Test02 tk) {
        test02 = tk;
    }
    // 调用类锁
    public void run() {
        test02.classLockB();
    }
}

class ThreadC extends Thread {
    private Test02 test02;
    public ThreadC(Test02 tk) {
        test02 = tk;
    }
    // 调用对象锁
    public void run() {
        test02.objectLock();
    }
}

main方法:起了三个线程,共同访问一个Test02对象

    public static void main(String[] args){

        Test02 test02 = new Test02();
        ThreadA ta = new ThreadA(test02);
        ThreadB tb = new ThreadB(test02);
        ThreadC tc = new ThreadC(test02);

        ta.setName("A");
        tb.setName("B");
        tc.setName("C");

        ta.start();
        tb.start();
        tc.start();
    }

执行的结果:

name = A, begain
name = C, begain
name = A, end
name = B, begain
name = C, end
name = B, end

可以看出由于 classLockA和classLockB都是类锁,即同一个锁,所以 A和B是按顺序执行,即同步的。而C是对象锁,和A/B不是同一种锁,所以C和A、B是 异步执行的。

分析:

对象锁要想保持同步执行,那么锁住的必须是同一个对象,举个例子:

Test02类不变,重起两个线程类:均对对象锁进行了调用

class ThreadA extends Thread {
    private Test02 test02;
    public ThreadA(Test02 tk) {
        test02 = tk;
    }
    // 调用类锁
    public void run() {
        test02.objectLock();
    }
}

class ThreadB extends Thread {
    private Test02 test02;
    public ThreadB(Test02 tk) {
        test02 = tk;
    }
    // 调用类锁
    public void run() {
        test02.objectLock();
    }
}

main方法:创建两个不同的资源对象,启动两个线程,分别对加锁的方法进行调用

    public static void main(String[] args){

        Test02 test02 = new Test02();
        Test02 test03 = new Test02();
        ThreadA ta = new ThreadA(test02);
        ThreadB tb = new ThreadB(test03);

        ta.setName("A");
        tb.setName("B");

        ta.start();
        tb.start();
    }

结果如下:

name = A, begain
name = B, begain
name = A, end
name = B, end

可见,是异步执行的,没有达到同步的作用。

改进:只需对类锁进行调用,代码如下:

class ThreadA extends Thread {
    private Test02 test02;
    public ThreadA(Test02 tk) {
        test02 = tk;
    }
    // 调用类锁
    public void run() {
//        test02.objectLock();
        test02.classLockA();
    }
}

class ThreadB extends Thread {
    private Test02 test02;
    public ThreadB(Test02 tk) {
        test02 = tk;
    }
    // 调用类锁
    public void run() {
//        test02.objectLock();
        test02.classLockA();
    }
}

main方法:同样是创建了多个对象

    public static void main(String[] args){

        Test02 test02 = new Test02();
        Test02 test03 = new Test02();
        ThreadA ta = new ThreadA(test02);
        ThreadB tb = new ThreadB(test03);

        ta.setName("A");
        tb.setName("B");

        ta.start();
        tb.start();
    }

结果:

name = A, begain
name = A, end
name = B, begain
name = B, end

达到了同步的效果!

总结:

1. 如果多线程同时访问同一类的 类锁(synchronized 修饰的静态方法)以及对象锁(synchronized 修饰的非静态方法)这两个方法执行是异步的,原因:类锁和对象锁是2中不同的锁。 
2. 类锁对该类的所有对象都能起作用,而对象锁不能。

原文地址:https://www.cnblogs.com/zhangjianbing/p/9251712.html

时间: 2024-08-25 20:35:21

并发编程大师系列之:Synchronized的类锁和对象锁的相关文章

并发编程大师系列之:wait/notify/notifyAll/condition

先把例子写出来,有空再补充... /** * @author itachi * @Title: Express * @Description: 快递类 * @date 2018/7/4下午10:27 */ public class Express { // 始发地 private final static String CITY = "ShangHai"; // 里程变化 private int km; // 地点变化 private String site; Express() {

并发编程大师系列之:线程的定义和中断 interrupt

1.启动线程的三种方式: 1.1继承Thread类 public static class UseThread extends Thread { public void run() { System.out.println("thread run 执行!"); } } 启动线程: UseThread ut = new UseThread(); ut.start(); 1.2实现Runable接口 public static class UseRun implements Runnabl

并发编程大师系列之:你真的了解ThreadLocal吗?

总记得在面试的时候被问到过,今天终于轮到你了ThreadLocal,从表面上读英文的意思为线程本地变量,这样也许更好理解了,就是每个线程自己独有的,不与其它线程共享的变量呗. 首先翻开源码,这个author的名字真的熟悉,对,就是dog李(Doug Lea),貌似lang包下的很多都是由这位哥编写的,看一下这个类中的结构(JDK1.8) 常用的就这几个,俩内部类,四个方法. 1. get()方法是用来获取ThreadLocal在当前线程中保存的变量副本. 2. set()用来设置当前线程中变量的

并发编程之关键字(synchronized、volatile)

并发编程主要设计两个关键字:一个是synchronized,另一个是volatile.下面主要讲解这两个关键字,并对这两个关机进行比较. synchronized synchronized是通过JMV种的monitorenter和monitorexit指令实现同步.monitorenter指令是在编译后插入到同步代码的开始位置,而monitorexit插入到同步代码的结束位置和异常位置.每一个对象都与一个monitor相关联,当monitor被只有后,它将处于锁定状态. 当一个线程试图访问同步代

java synchronized类锁,对象锁详解(转载)

觉得还不错 留个记录,转载自http://zhh9106.iteye.com/blog/2151791 在java编程中,经常需要用到同步,而用得最多的也许是synchronized关键字了,下面看看这个关键字的用法. 因为synchronized关键字涉及到锁的概念,所以先来了解一些相关的锁知识. java的内置锁:每个java对象都可以用做一个实现同步的锁,这些锁成为内置锁.线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁.获得内置锁的唯一途径就是进入这个锁的

类锁和对象锁,synchronized修饰static方法与非static方法的区别

当synchronized修饰一个static方法时,多线程下,获取的是类锁(即Class本身,注意:不是实例), 作用范围是整个静态方法,作用的对象是这个类的所有对象. 当synchronized修饰一个非static方法时,多线程下,获取的是对象锁(即类的实例对象), 作用范围是整个方法,作用对象          是调用该方法的对象 结论: 类锁和对象锁不同,它们之间不会产生互斥 原文地址:https://www.cnblogs.com/alter888/p/9840402.html

java 类锁与对象锁(实例锁)同步问题

众所周知,synchronized可修饰方法和代码块,可作用于类或者对象. 当修饰代码块时,synchronized(object) 作用于对象,只约束该对象. synchronized(class)作用于类,约束类所有的对象. 修饰方法时,synchronized 修饰static方法时,作用于类.修饰非static方法时作用于对象. 注意类锁和对象锁是两个不同的锁,二者不会发生同步关系. 由于static变量,可被static方法调用,也可被非static方法调用,当二者同时被synchro

Java类锁和对象锁实践(good)

一.前言 之前对类锁和对象锁是否是互斥的不是太确定,因此决定编写相关的程序进行实践一下.编写前对相关定义约定约定如下: 1. 类锁:在代码中的方法上加了static和synchronized的锁,或者synchronized(xxx.class)的代码段,如下文中的increament(): 2.对象锁:在代码中的方法上加了synchronized的锁,或者synchronized(this)的代码段,如下文中的synOnMethod()和synInMethod(): 3.私有锁:在类内部声明一

Java类锁和对象锁实践和内部私有锁关联

Java类锁和对象锁实践 感谢[jiehao]同学的投稿,投稿可将文章发送到[email protected] 类锁和对象锁是否会冲突?对象锁和私有锁是否会冲突?通过实例来进行说明. 一.相关约定 为了明确后文的描述,先对本文涉及到的锁的相关定义作如下约定: 1. 类锁:在代码中的方法上加了static和synchronized的锁,或者synchronized(xxx.class)的代码段,如下文中的increament(): 2.对象锁:在代码中的方法上加了synchronized的锁,或者