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

Posted zczpeng

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java多线程研究02-对象锁,synchronized关键字详解相关的知识,希望对你有一定的参考价值。

对象锁的概念

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

图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关键字的方法中,针对某个对象的操作不一定是线程安全的!

以上是关于Java多线程研究02-对象锁,synchronized关键字详解的主要内容,如果未能解决你的问题,请参考以下文章

java多线程8.性能与活跃性问题

深入研究 Java Synchronize 和 Lock 的区别与用法

[转] 深入研究 Java Synchronize 和 Lock 的区别与用法

多线程的几种状态

并发技术12线程锁技术的使用

锁的种类