Java进阶之深入理解synchronized

1 synchronized关键字

synchronized翻译为中文的意思是同步的,它是Java中处理线程安全问题常用的关键字,也有人称其为同步锁

2 synchronized到底锁住的是什么

(1)synchronized锁住的是对象而非代码,实际保护的是同一个对象的方法调用,确保同时只能有一个线程执行。
(2)再具体来说,synchronized锁住的对象有一个锁和一个等待队列,锁只能被一个线程持有,其他试图获得同样锁的线程需要等待,执行synchronized实例方法的过程大概如下:

①尝试获得锁,如果能够获得锁,继续下一步,否则加入等待队列,线程的状态会变为阻塞(BLOCKED)并等待唤醒;
②执行方法体代码;
③释放锁,如果等待队列上有等待的线程,从中取一个并唤醒,如果有多个等待的线程,唤醒哪一个是不一定的,不保证公平性。

3 synchronized(this)、synchronized(class)与synchronized(Object)的区别

3.1 准备

(1)IService接口

public interface IService {
    void serviceMethodA(String param);

    void serviceMethodB(String param);
}

(2)线程A

public class ThreadA extends Thread {
    private IService mService;
    private String mParam;

    public ThreadA(IService service, String param) {
        super();
        this.mService = service;
        this.mParam = param;
    }

    @Override
    public void run() {
        super.run();
        mService.serviceMethodA(mParam);
    }
}

(3)线程B

public class ThreadB extends Thread {
    private IService mService;
    private String mParam;

    public ThreadB(IService service, String param) {
        super();
        this.mService = service;
        this.mParam = param;
    }

    @Override
    public void run() {
        super.run();
        mService.serviceMethodB(mParam);
    }
}

3.2 验证synchronized锁住的代码块间的同步性

(1)ThisService

public class ThisService implements IService {
    @Override
    public void serviceMethodA(String param) {
        try {
            synchronized (this) {
                System.out.println("A begin time=" + System.currentTimeMillis());
                Thread.sleep(2000);
                System.out.println("A end   time=" + System.currentTimeMillis());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void serviceMethodB(String param) {
        synchronized (this) {
            System.out.println("B begin time=" + System.currentTimeMillis());
            System.out.println("B end   time=" + System.currentTimeMillis());
        }
    }
}

(2)执行代码:验证synchronized锁住的代码块间的同步性

    // 执行验证synchronized锁住的代码块间的同步性
    public void textSynchronizeThis() {
        ThisService service = new ThisService();
        ThreadA threadA = new ThreadA(service, "");
        threadA.setName("a");
        threadA.start();
        ThreadB threadB = new ThreadB(service, "");
        threadB.setName("b");
        threadB.start();
    }

(3)结果

 /***
    * 结果:ThreadA和ThreadB同步执行了
     * 2019-08-25 14:45:56.400 15246-15268/com.seniorlibs.thread I/System.out: B begin time=1566715556400
     * 2019-08-25 14:45:56.400 15246-15268/com.seniorlibs.thread I/System.out: B end   time=1566715556400
     * 2019-08-25 14:45:56.400 15246-15267/com.seniorlibs.thread I/System.out: A begin time=1566715556400
     * 2019-08-25 14:45:58.402 15246-15267/com.seniorlibs.thread I/System.out: A end   time=1566715558402
     */

(4)结论
当一个线程访问ThisService的一个synchronized (this)同步代码块时,其它线程对同一个ThisService中其它的synchronized (this)同步代码块的访问将是堵塞,这说明synchronized (this)使用的对象监视器是同一个

3.3 验证synchronized(this)代码块是锁定当前对象

(1)LocalThisService

public class LocalThisService implements IService{

    /**
     * 结果:ThreadA和ThreadB异步执行了
     * 2019-08-25 14:49:51.328 15628-15649/com.seniorlibs.thread I/System.out: synchronized thread name:b-->i=1
     * 2019-08-25 14:49:51.329 15628-15648/com.seniorlibs.thread I/System.out: run----serviceMethodA
     * 2019-08-25 14:49:52.344 15628-15649/com.seniorlibs.thread I/System.out: synchronized thread name:b-->i=2
     * 2019-08-25 14:49:53.345 15628-15649/com.seniorlibs.thread I/System.out: synchronized thread name:b-->i=3
     * 2019-08-25 14:49:54.347 15628-15649/com.seniorlibs.thread I/System.out: synchronized thread name:b-->i=4
     * 2019-08-25 14:49:55.348 15628-15649/com.seniorlibs.thread I/System.out: synchronized thread name:b-->i=5
     * @param param
     */
//    @Override
//    public void serviceMethodA(String param) {
//        System.out.println("run----serviceMethodA");
//    }

    /**
     * 加上同步synchronized
     * 结果:ThreadA和ThreadB同步执行了
     * 2019-08-25 14:51:19.941 15753-15773/com.seniorlibs.thread I/System.out: synchronized thread name:b-->i=1
     * 2019-08-25 14:51:20.948 15753-15773/com.seniorlibs.thread I/System.out: synchronized thread name:b-->i=2
     * 2019-08-25 14:51:21.950 15753-15773/com.seniorlibs.thread I/System.out: synchronized thread name:b-->i=3
     * 2019-08-25 14:51:22.951 15753-15773/com.seniorlibs.thread I/System.out: synchronized thread name:b-->i=4
     * 2019-08-25 14:51:23.953 15753-15773/com.seniorlibs.thread I/System.out: synchronized thread name:b-->i=5
     * 2019-08-25 14:51:24.953 15753-15772/com.seniorlibs.thread I/System.out: run----serviceMethodA
     * @param param
     */
    @Override
    public synchronized void serviceMethodA(String param){
        System.out.println("run----serviceMethodA");
    }

    @Override
    public void serviceMethodB(String param) {
        synchronized (this) {
            try {
                for (int i = 1; i <= 5; i++) {
                    System.out.println("synchronized thread name:" + Thread.currentThread().getName() + "-->i=" + i);
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

(2)执行代码:验证synchronized锁住的代码块间的同步性

  public void textSynchronizeLocalThis() {
        LocalThisService service = new LocalThisService();
        ThreadA threadA = new ThreadA(service, "");
        threadA.setName("a");
        threadA.start();
        ThreadB threadB = new ThreadB(service, "");
        threadB.setName("b");
        threadB.start();
    }

(3)结果,在(1)中

(4)结论
多个线程调用同一个对象中的“不同名称的synchronized方法”和“synchronized(this)代码块”时,是同步的。因为synchronized方法锁住的是当前对象,所以synchronized(this)代码块也是锁定当前对象。

3.4 验证synchronized(任意对象)代码块是锁定此对象

(1)ObjectService

public class ObjectService implements IService {
    private final StringBuilder lock = new StringBuilder();

    /**
     * 结果:ThreadA和ThreadB异步执行了
     * System.out: thread name=b 进入代码快:1566717388490
     * System.out: thread name=a 进入代码快:1566717388492
     * System.out: thread name=b 进入代码快:1566717391491 入参:ThreadB
     * System.out: thread name=a 进入代码快:1566717391494 入参:ThreadA
     * @param param
     */
//    @Override
//    public void serviceMethodA(String param) {
//        try {
//            StringBuilder lock = new StringBuilder();
//            synchronized (lock) {
//                String threadName = Thread.currentThread().getName();
//                System.out.println("thread name=" + threadName + " 进入代码快:" + System.currentTimeMillis());
//                Thread.sleep(3000);
//                System.out.println("thread name=" + threadName + " 进入代码快:" + System.currentTimeMillis() + " 入参:" + param);
//            }
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
//    }

    /**
     * 结果:ThreadA和ThreadB同步执行了
     * System.out: thread name=a 进入代码快:1566717191405
     * System.out: thread name=a 进入代码快:1566717194406 入参:ThreadA
     * System.out: thread name=b 进入代码快:1566717194407
     * System.out: thread name=b 进入代码快:1566717197407 入参:ThreadB
     * @param param
     */
    @Override
    public void serviceMethodA(String param) {
        try {
            synchronized (lock) {
                String threadName = Thread.currentThread().getName();
                System.out.println("thread name=" + threadName + " 进入代码快:" + System.currentTimeMillis());
                Thread.sleep(3000);
                System.out.println("thread name=" + threadName + " 进入代码快:" + System.currentTimeMillis() + " 入参:" + param);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void serviceMethodB(String param) {
        serviceMethodA(param);
    }
}

(2)附加:修改ObjectService实现synchronized(任意对象)与synchronized同步方法共用

public class ObjectService implements IService {
    private final StringBuilder lock = new StringBuilder();

    @Override
    public void serviceMethodA(String param) {
        try {
            synchronized (lock) {
                String threadName = Thread.currentThread().getName();
                System.out.println("thread name=" + threadName  + " 进入代码快:" + System.currentTimeMillis());
                Thread.sleep(3000);
                System.out.println("thread name=" + threadName + " 进入代码快:" + System.currentTimeMillis() + " 入参:" + param);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 结果:ThreadA和ThreadB异步执行了
     * System.out: thread name=b 进入代码快:1566717724148
     * System.out: thread name=a 进入代码快:1566717724151
     * System.out: thread name=b 进入代码快:1566717727149 入参:ThreadB
     * System.out: thread name=a 进入代码快:1566717727152 入参:ThreadA
     * @param param
     */
    @Override
    public synchronized void serviceMethodB(String param) {
        try {
            System.out.println("thread name=" + Thread.currentThread().getName() + " 进入代码快:" + System.currentTimeMillis());
            Thread.sleep(3000);
            System.out.println("thread name=" + Thread.currentThread().getName() + " 进入代码快:" + System.currentTimeMillis() + " 入参:" + param);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

(3)执行代码:验证synchronized(任意对象)代码块是锁定此对象

public void textSynchronizeObject() {
        ObjectService service = new ObjectService();
        ThreadA threadA = new ThreadA(service, "ThreadA");
        threadA.setName("a");
        threadA.start();
        ThreadB threadB = new ThreadB(service, "ThreadB");
        threadB.setName("b");
        threadB.start();
    }

(4)结果,在(1)中

(5)结论
使用synchronized(任意对象)进行同步操作,对象必须是同一个对象;如果不是同一个对象,运行就是异步执行了。

多个线程调用同一个对象中的“不同名称的synchronized方法”和“synchronized(任意对象)代码块”时,是异步的。因为synchronized方法锁住的是当前对象,所以synchronized(任意对象)代码块是锁定此对象。

3.5 验证静态synchronized方法是对当前对应的*.Class进行持锁

(1)StaticService

public class StaticService implements IService {

    @Override
    public void serviceMethodA(String param) {
        methodA(param);
    }

//    private synchronized static void methodA(String param) {
//        try {
//            System.out.println("A begin time=" + System.currentTimeMillis());
//            Thread.sleep(3000);
//            System.out.println("A end   time=" + System.currentTimeMillis());
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
//    }

//    private synchronized void methodA(String param) {
//        try {
//            System.out.println("A begin time=" + System.currentTimeMillis());
//            Thread.sleep(3000);
//            System.out.println("A end   time=" + System.currentTimeMillis());
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
//    }

    private void methodA(String param) {
        try {
            synchronized (StaticService.class) {
                System.out.println("A begin time=" + System.currentTimeMillis());
                Thread.sleep(3000);
                System.out.println("A end   time=" + System.currentTimeMillis());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void serviceMethodB(String param) {
        methodB(param);
    }

//    private synchronized static void methodB(String param) {
//        try {
//            System.out.println("B begin time=" + System.currentTimeMillis());
//            Thread.sleep(1000);
//            System.out.println("B end   time=" + System.currentTimeMillis());
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
//    }

//    private synchronized void methodB(String param) {
//        try {
//            System.out.println("B begin time=" + System.currentTimeMillis());
//            Thread.sleep(1000);
//            System.out.println("B end   time=" + System.currentTimeMillis());
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
//    }

    private void methodB(String param) {
        try {
            synchronized (StaticService.class) {
                System.out.println("B begin time=" + System.currentTimeMillis());
                Thread.sleep(1000);
                System.out.println("B end   time=" + System.currentTimeMillis());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 结果:synchronized static + 一个/多个StaticService对象 --->ThreadA和ThreadB同步执行了
     * 2019-08-25 15:39:30.711 18410-18429/com.seniorlibs.thread I/System.out: B begin time=1566718770711
     * 2019-08-25 15:39:31.719 18410-18429/com.seniorlibs.thread I/System.out: B end   time=1566718771719
     * 2019-08-25 15:39:31.719 18410-18428/com.seniorlibs.thread I/System.out: A begin time=1566718771719
     * 2019-08-25 15:39:34.721 18410-18428/com.seniorlibs.thread I/System.out: A end   time=1566718774721
     *
     * 结果:synchronized (StaticService.class) + 一个/多个StaticService对象 --->ThreadA和ThreadB同步执行了
     * 2019-08-25 15:49:15.626 18996-19017/com.seniorlibs.thread I/System.out: B begin time=1566719355626
     * 2019-08-25 15:49:16.630 18996-19017/com.seniorlibs.thread I/System.out: B end   time=1566719356630
     * 2019-08-25 15:49:16.631 18996-19016/com.seniorlibs.thread I/System.out: A begin time=1566719356631
     * 2019-08-25 15:49:19.632 18996-19016/com.seniorlibs.thread I/System.out: A end   time=1566719359632
     *
     * 结果:synchronized + 一个StaticService对象 --->ThreadA和ThreadB同步执行了
     * 2019-08-25 15:39:30.711 18410-18429/com.seniorlibs.thread I/System.out: B begin time=1566718770711
     * 2019-08-25 15:39:31.719 18410-18429/com.seniorlibs.thread I/System.out: B end   time=1566718771719
     * 2019-08-25 15:39:31.719 18410-18428/com.seniorlibs.thread I/System.out: A begin time=1566718771719
     * 2019-08-25 15:39:34.721 18410-18428/com.seniorlibs.thread I/System.out: A end   time=1566718774721
     *
     * 结果:synchronized + 多个StaticService对象 --->ThreadA和ThreadB异步执行了
     * 2019-08-25 15:42:16.522 18724-18743/com.seniorlibs.thread I/System.out: B begin time=1566718936522
     * 2019-08-25 15:42:16.528 18724-18742/com.seniorlibs.thread I/System.out: A begin time=1566718936527
     * 2019-08-25 15:42:17.525 18724-18743/com.seniorlibs.thread I/System.out: B end   time=1566718937525
     * 2019-08-25 15:42:19.528 18724-18742/com.seniorlibs.thread I/System.out: A end   time=1566718939528
     */
}

(2)执行代码:验证静态synchronized方法是对当前对应的*.Class进行持锁

 public void textSynchronizeLocalStatic() {
        StaticService service = new StaticService();
        ThreadA threadA = new ThreadA(service, "");
        threadA.setName("a");
        threadA.start();
        StaticService service2 = new StaticService();
        ThreadB threadB = new ThreadB(service2, "");
        threadB.setName("b");
        threadB.start();
    }

(3)结果,在(1)中

(4)结论
同步synchronized(*.class)代码块的作用其实和synchronized static方法作用一样。Class锁对类的所有对象实例起作用。

3.6 学习链接

ynchronized(this)、synchronized(class)与synchronized(Object)的区别

4 synchronized应用场景

4.1 以下5个例子都是等价的

Counter是一个简单的计数器类,incr方法和getCount方法都加了synchronized修饰。加了synchronized后,方法内的代码就变成了原子操作,当多个线程并发更新同一个Counter对象的时候,也不会出现问题。
(1)synchronized方法

public class Counter {
    private int count;

    public synchronized void incr(){
        count ++;
    }
    
    public synchronized int getCount() {
        return count;
    }
}

(2)synchronized(this)代码块

public class Counter {
    private int count;

    public void incr(){
        synchronized(this){
            count ++;    
        }
    }
    
    public int getCount() {
        synchronized(this){
            return count;
        }
    }
}

(3)synchronized static方法(注意:count是static)

public class StaticCounter {
    private static int count = 0;

    public static synchronized void incr() {
        count++;
    }

    public static synchronized int getCount() {
        return count;
    }
}

(4)synchronized(.class)方法(注意:count是static)

public class StaticCounter {
    private static int count = 0;

    public static void incr() {
        synchronized(StaticCounter.class){
            count++;    
        }    
    }

    public static int getCount() {
        synchronized(StaticCounter.class){
            return count;    
        }
    }
}

(5)synchronized(任意对象),任意对象都有一个锁和等待队列。或者说,任何对象都可以作为锁对象

public class Counter {
    private int count;
    private Object lock = new Object();
    
    public void incr(){
        synchronized(lock){
            count ++;    
        }
    }
    
    public int getCount() {
        synchronized(lock){
            return count;
        }
    }
} 

4.4 学习链接

Java编程的逻辑----理解synchronized

5 synchronized实现原理

???

发布了185 篇原创文章 · 获赞 207 · 访问量 59万+

猜你喜欢

转载自blog.csdn.net/chenliguan/article/details/100064561