Java多线程研究02-对象锁,synchronized关键字详解

对象锁的概念

通过图理解一下,多个线程要操作同一个对象的场景
图1:
1:
图2:
2:
图3:
[图片]

解释:

● 钥匙是什么,可以理解为线程的执行资格,拥有cpu时间片等资源。

● 可是对象的‘锁芯’(对象独占权)只有一个,那么可以打开这把锁的多个‘钥匙’同一时间内只能有一把‘钥匙’进行操作;其他持有‘钥匙’的线程(或者没有持有钥匙的线程)都要进入等待状态;直到某把‘钥匙’从‘锁眼’中退出,操作系统会决定哪一把‘钥匙’重新插入‘锁芯’,这就是常说的操作系统的线程切换。

● 某一个线程拥有一个对象的‘锁’的‘钥匙’,并不代表这个线程的‘钥匙’是插入了‘锁芯’的(有这个对象的控制权);但是,有权抢占‘锁芯’控制权的线程,必定拥有这个对象的‘钥匙’。

●请一定注意wait(time)的用法。很多人的理解都是:‘等待一段时间time,然后该线程激活继续工作’;但是实际上time更准确的含义应该是:到时检查。而整个wait(time)更准确的理解应该是:释放这个线程独占的X对象的锁芯(独占权),以便其它可以抢占‘锁芯’(独占权)的线程能够进行抢占,但是本线程继续持有X对象的锁的钥匙,等待time的时间后,重新参与‘锁芯’抢占(虽然不一定能够抢占得到)。

两个线程操作同一个对象时的流程描述

1,线程1首先访问对象A,进入synchronized并拿到锁,拥有A的独占权。

2,线程2此时也访问对象A,但发现此时发现A的独占权已被其他线程占有,所以只能堵塞等待。

3,线程1对A的操作完成,归还钥匙并释独占资源,线程1对A的操作结束。

4,线程2发现A的独占权被释放,此时线程2由堵塞状态变为就绪状态,通过操作系统线程切换,线程2获得cpu执行资源(获得钥匙),并插入A的锁芯,拥有对A的独占权。

5,线程2执行完毕,归还钥匙释放A的独占权,线程2对A的操作结束。

通过代码来理解一下

package com.zczpeng.thread;

public class SynchronizeTest {

    private static final Object THRAD_LOCK = new Object();

    public static void main(String[] args) {

        Thread threadA = new Thread(new Runnable() {

            @Override
            public void run() {
                synchronized (SynchronizeTest.THRAD_LOCK) {
                    System.out.println("线程A执行了");
                }
            }
        });
       Thread threadB = new Thread(new Runnable() {

            @Override
            public void run() {
                synchronized (SynchronizeTest.THRAD_LOCK) {
                    System.out.println("线程B执行了");
                }
            }
        });

       threadA.start();
       threadB.start();
    }
}

synchronized可标注的位置

在JAVA中synchronized关键字可以加载很多位置。可以在一个方法定义上加synchronized关键字、也可以在方法体中加synchronized关键字、还可以在static块中加synchronized关键字。以下的代码都是正确的:

// 代码片段1
static {
    synchronized(ThreadLock.class) {

    }
}

// 代码片段2
public synchronized void someMethod() {

}

// 代码片段3
public synchronized static void someMethod() {

}

// 代码片段4
public static void someMethod() {
    synchronized (ThreadLock.class) {

    }
}

// 代码片段5
public void someMethod() {
    synchronized (ThreadLock.class) {

    }
}

但是不同位置的synchronized的关键字,代表的含义是不一样的

● synchronized关键字加载在非静态方法上时:
其代表的含义和synchronized(this){}的意义相同。即对所拥有这个方法的对象进行锁状态检查。

● synchronized关键字加载在静态方法上时:
其代表的含义和synchronized(Class.class)的意义相类似。即对所拥有这个方法的类进行锁状态检查

synchronized关键字的使用

在前面的文章中我们主要讲解的是线程中“对象锁”的工作原理和操作方式。在讲解synchronized关键字的时候,我们还提到了synchronized关键字可以标注的位置。我们经常可以看到如下代码,将synchronized关键字加载到代码的方法体上,然后说,这个操作是线程安全的。

/**
 * 这个类的class对象进行检查。
 */
public static synchronized void doSomething() {

}

/**
 * 对这个类的实例化对象进行检查
 */
public synchronized void doOtherthing() {

}

但事实上,一个对象是否是线程安全的除了添加synchronized关键字以外,更重要的还要看如何进行这个对象的操作。如下代码中,我们展示了在两个线程的doOtherthing方法(所谓的线程安全方法),去操作一个对象VALUE:

public class SyncThread implements Runnable {


    private Integer value;

    private static Integer VALUE;


    public SyncThread(int value) {
        this.value = value;
    }

    /**
     * 对这个类的实例化对象进行检查
     */
    private synchronized void doOtherthing() {
        VALUE= this.value;

        System.out.println("当前VALUE的值"+VALUE);
    }

    @Override
    public void run() {
        Thread currentThread = Thread.currentThread();
        Long id = currentThread.getId();
        this.doOtherthing();
    }

    public static void main(String[] args) throws Exception {
        Thread syncThread1 = new Thread(new SyncThread(10));
        Thread syncThread2 = new Thread(new SyncThread(100));

        syncThread1.start();
        syncThread2.start();
    }
}

从Debug的情况来看,可能出现静态对象VALUE的值出现了脏读的情况:
[Thread-1]当前VALUE的值:100
[Thread-0] 当前VALUE的值:100

以下是代码出现bug的原因:
● syncThread1对象和syncThread2对象是SyncThread类的两个不同实例。“private synchronized void doOtherthing()”方法中的synchronized关键字实际上进行同步检查目标是不一样的。

● 当然为了对这个类(SyncThread)的class对象进行同步检查,无需在静态方法上标注synchronized关键字,只要标注SyncThread的class对象进行同步检查。

private void doOtherthing() {
    synchronized (SyncThread.class) {
        VALUE = this.value;
        System.out.println("当前VALUE的值"+VALUE);
    }
}

所以,一个对象是否是线程安全的除了添加synchronized关键字以外,更重要的还要看如何进行这个对象的操作;标注了synchronized关键字的方法中,针对某个对象的操作不一定是线程安全的!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值