• 2.2.7将任意对象作为对象监视器


    多个线程调用同一个对象中的不同名称的synchronized同步方法或者synchronized(this)同步代码块时,调用的效果就是按照顺序执行,也就是同步,阻塞的。

    这说明synchronized同步方法和synchronized(this)同步代码块分别有两种作用。

    a. synchronized同步方法

    (1)对其他同步方法或者同步代码块调用呈阻塞状态。

    (2)同一时间只有一个线程可以执行synchronized同步方法中的代码。

    b.synchronized同步代码块

    (1)对其他同步方法或者同步代码块调用呈阻塞状态。

    (2)同一时间只有一个线程可以执行synchronized同步方法中的代码。

    使用synchronized(this)格式来同步代码块,其实还可以用任意对象作为对象监视器来实现同步。这个任意对象大多数是实例变量及方法中的参数,

    使用格式为synchronized(非this对象)

    a:在多个线程中持有对象监视器作为同一个对象的前提下,同一时间只有一个线程可以执行synchronized(非this对象x)同步代码块中的代码。

    b:当持有对象监视器为同一个对象的前提下,同一时间只有一个线程可以执行synchronized(非this对象x)同步代码块中的代码。

    如下验证a

    package com.cky.utils;
    
    /**
     * Created by chenkaiyang on 2017/12/7.
     */
    public class Service {
        private String usrName;
        private String pwd;
        private String anyString = new String();
        public void setUsrNameAndPwd(String usrName, String pwd) {
            try {
                synchronized (anyString) {
                    System.out.println("线程名:"+Thread.currentThread().getName() +"进入同步代码块");
                    Thread.sleep(2000);
                    System.out.println("线程名:"+Thread.currentThread().getName() +"离开同步代码块");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    package com.cky.thread;
    
    import com.cky.utils.Service;
    
    /**
     * Created by chenkaiyang on 2017/12/5.
     */
    public class ThreadA extends Thread{
        private Service service;
        public ThreadA(Service service) {
            super();
            this.service = service;
        }
    
        @Override
        public void run() {
            super.run();
            service.setUsrNameAndPwd("a", "aa");
        }
    }
    package com.cky.thread;
    
    import com.cky.utils.Service;
    
    /**
     * Created by chenkaiyang on 2017/12/5.
     */
    public class ThreadB extends  Thread{
        private Service service;
        public ThreadB(Service service) {
            super();
            this.service = service;
        }
    
        @Override
        public void run() {
            super.run();
            service.setUsrNameAndPwd("b", "bb");
        }
    }
    package com.cky.test;
    
    import com.cky.thread.ThreadA;
    import com.cky.thread.ThreadB;
    import com.cky.utils.Service;
    
    /**
     * Created by chenkaiyang on 2017/12/6.
     */
    public class Test2 {
        public static void main(String[] args) throws InterruptedException {
           Service task = new Service();
            ThreadA threadA = new ThreadA(task);
            threadA.start();
            ThreadB threadB = new ThreadB(task);
            threadB.start();
    
        }
    }
    D:itjdk1.8injava -Didea.launcher.port=7537 "-Didea.launcher.bin.path=D:itideaIntelliJ IDEA 2016.3.3in" -Dfile.encoding=UTF-8 -classpath "D:itjdk1.8jrelibcharsets.jar;D:itjdk1.8jrelibdeploy.jar;D:itjdk1.8jrelibextaccess-bridge-64.jar;D:itjdk1.8jrelibextcldrdata.jar;D:itjdk1.8jrelibextdnsns.jar;D:itjdk1.8jrelibextjaccess.jar;D:itjdk1.8jrelibextjfxrt.jar;D:itjdk1.8jrelibextlocaledata.jar;D:itjdk1.8jrelibext
    ashorn.jar;D:itjdk1.8jrelibextsunec.jar;D:itjdk1.8jrelibextsunjce_provider.jar;D:itjdk1.8jrelibextsunmscapi.jar;D:itjdk1.8jrelibextsunpkcs11.jar;D:itjdk1.8jrelibextzipfs.jar;D:itjdk1.8jrelibjavaws.jar;D:itjdk1.8jrelibjce.jar;D:itjdk1.8jrelibjfr.jar;D:itjdk1.8jrelibjfxswt.jar;D:itjdk1.8jrelibjsse.jar;D:itjdk1.8jrelibmanagement-agent.jar;D:itjdk1.8jrelibplugin.jar;D:itjdk1.8jrelib
    esources.jar;D:itjdk1.8jrelib
    t.jar;F:springboot	hreaddemooutproduction	hreaddemo;D:itideaIntelliJ IDEA 2016.3.3libidea_rt.jar" com.intellij.rt.execution.application.AppMain com.cky.test.Test2
    线程名:Thread-0进入同步代码块
    线程名:Thread-0离开同步代码块
    线程名:Thread-1进入同步代码块
    线程名:Thread-1离开同步代码块

    结果分析:

    锁非this对象具有一定的优点如果在一个类中有很多个synchronized方法,那么虽然能实现同步,但是会受到阻塞,所以影响执行效率,但如果使用同步代码块非this对象

    则synchronized(非this)代码块中的程序与同步方法是异步的,不与其他锁this同步方法争抢this锁,大大提高效率

     更改Service类中的代码(将anyString放入方法中)

    package com.cky.utils;
    
    /**
     * Created by chenkaiyang on 2017/12/7.
     */
    public class Service {
        private String usrName;
        private String pwd;
    
        public void setUsrNameAndPwd(String usrName, String pwd) {
            try {
                String anyString = new String();
                synchronized (anyString) {
                    System.out.println("线程名:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis() +"进入同步代码块");
                    Thread.sleep(2000);
                    System.out.println("线程名:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis() +"离开同步代码块");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    D:itjdk1.8injava -Didea.launcher.port=7538 "-Didea.launcher.bin.path=D:itideaIntelliJ IDEA 2016.3.3in" -Dfile.encoding=UTF-8 -classpath "D:itjdk1.8jrelibcharsets.jar;D:itjdk1.8jrelibdeploy.jar;D:itjdk1.8jrelibextaccess-bridge-64.jar;D:itjdk1.8jrelibextcldrdata.jar;D:itjdk1.8jrelibextdnsns.jar;D:itjdk1.8jrelibextjaccess.jar;D:itjdk1.8jrelibextjfxrt.jar;D:itjdk1.8jrelibextlocaledata.jar;D:itjdk1.8jrelibext
    ashorn.jar;D:itjdk1.8jrelibextsunec.jar;D:itjdk1.8jrelibextsunjce_provider.jar;D:itjdk1.8jrelibextsunmscapi.jar;D:itjdk1.8jrelibextsunpkcs11.jar;D:itjdk1.8jrelibextzipfs.jar;D:itjdk1.8jrelibjavaws.jar;D:itjdk1.8jrelibjce.jar;D:itjdk1.8jrelibjfr.jar;D:itjdk1.8jrelibjfxswt.jar;D:itjdk1.8jrelibjsse.jar;D:itjdk1.8jrelibmanagement-agent.jar;D:itjdk1.8jrelibplugin.jar;D:itjdk1.8jrelib
    esources.jar;D:itjdk1.8jrelib
    t.jar;F:springboot	hreaddemooutproduction	hreaddemo;D:itideaIntelliJ IDEA 2016.3.3libidea_rt.jar" com.intellij.rt.execution.application.AppMain com.cky.test.Test2
    线程名:Thread-0在1512646079651进入同步代码块
    线程名:Thread-1在1512646079652进入同步代码块
    线程名:Thread-0在1512646081652离开同步代码块
    线程名:Thread-1在1512646081652离开同步代码块

    如何不是同步执行,而是异步交叉执行的,因为对象监视器不是同一个对象,所以异步交叉调用。

    package com.cky.utils;
    
    /**
     * Created by chenkaiyang on 2017/12/7.
     */
    public class Service {
       private  String anyString = new String();
        public void a() {
            try {
    
                synchronized (anyString) {
                    System.out.println("a begin");
                    Thread.sleep(2000);
                    System.out.println("a end");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        public void b() {
    
                System.out.println("b begin");
                System.out.println("b end");
    
        }
    }
    package com.cky.thread;
    
    import com.cky.utils.Service;
    
    /**
     * Created by chenkaiyang on 2017/12/5.
     */
    public class ThreadA extends Thread{
        private Service service;
        public ThreadA(Service service) {
            super();
            this.service = service;
        }
    
        @Override
        public void run() {
            super.run();
            service.a();
        }
    }
    package com.cky.thread;
    
    import com.cky.utils.Service;
    
    /**
     * Created by chenkaiyang on 2017/12/5.
     */
    public class ThreadB extends  Thread{
        private Service service;
        public ThreadB(Service service) {
            super();
            this.service = service;
        }
    
        @Override
        public void run() {
            super.run();
            service.b();
        }
    }
    package com.cky.test;
    
    import com.cky.thread.ThreadA;
    import com.cky.thread.ThreadB;
    import com.cky.utils.Service;
    
    /**
     * Created by chenkaiyang on 2017/12/6.
     */
    public class Test2 {
        public static void main(String[] args) throws InterruptedException {
           Service task = new Service();
            ThreadA threadA = new ThreadA(task);
            threadA.start();
            ThreadB threadB = new ThreadB(task);
            threadB.start();
    
        }
    }
    D:itjdk1.8injava -Didea.launcher.port=7539 "-Didea.launcher.bin.path=D:itideaIntelliJ IDEA 2016.3.3in" -Dfile.encoding=UTF-8 -classpath "D:itjdk1.8jrelibcharsets.jar;D:itjdk1.8jrelibdeploy.jar;D:itjdk1.8jrelibextaccess-bridge-64.jar;D:itjdk1.8jrelibextcldrdata.jar;D:itjdk1.8jrelibextdnsns.jar;D:itjdk1.8jrelibextjaccess.jar;D:itjdk1.8jrelibextjfxrt.jar;D:itjdk1.8jrelibextlocaledata.jar;D:itjdk1.8jrelibext
    ashorn.jar;D:itjdk1.8jrelibextsunec.jar;D:itjdk1.8jrelibextsunjce_provider.jar;D:itjdk1.8jrelibextsunmscapi.jar;D:itjdk1.8jrelibextsunpkcs11.jar;D:itjdk1.8jrelibextzipfs.jar;D:itjdk1.8jrelibjavaws.jar;D:itjdk1.8jrelibjce.jar;D:itjdk1.8jrelibjfr.jar;D:itjdk1.8jrelibjfxswt.jar;D:itjdk1.8jrelibjsse.jar;D:itjdk1.8jrelibmanagement-agent.jar;D:itjdk1.8jrelibplugin.jar;D:itjdk1.8jrelib
    esources.jar;D:itjdk1.8jrelib
    t.jar;F:springboot	hreaddemooutproduction	hreaddemo;D:itideaIntelliJ IDEA 2016.3.3libidea_rt.jar" com.intellij.rt.execution.application.AppMain com.cky.test.Test2
    a begin
    b begin
    b end
    a end

    结果分析:

    因为对象监视器的不同,所以运行结果是异步的。

    同步代码块放在非同步方法中进行声明,并不能保证调用方法的线程的执行顺序是同步的,容易引发脏读。

    先看如下例子

    package com.cky.utils;
    
    /**
     * Created by chenkaiyang on 2017/12/7.
     */
    public class Service {
        synchronized public void a(String str) {
            try {
                    System.out.println("name=" + Thread.currentThread().getName() + "执行了a方法");
                    Thread.sleep(2000);
                    System.out.println("name=" + Thread.currentThread().getName() + "退出了a方法");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        synchronized  public void b() {
    
                System.out.println("b begin");
                System.out.println("b end");
    
        }
    }
    package com.cky.thread;
    
    import com.cky.utils.Service;
    
    /**
     * Created by chenkaiyang on 2017/12/5.
     */
    public class ThreadA extends Thread{
        private Service service;
        public ThreadA(Service service) {
            super();
            this.service = service;
        }
    
        @Override
        public void run() {
            super.run();
            for (int i = 0; i < 100000; i++) {
                service.a("threadA"+ (i+1));
            }
    
        }
    }
    package com.cky.thread;
    
    import com.cky.utils.Service;
    
    /**
     * Created by chenkaiyang on 2017/12/5.
     */
    public class ThreadB extends  Thread{
        private Service service;
        public ThreadB(Service service) {
            super();
            this.service = service;
        }
    
        @Override
        public void run() {
            super.run();
            for (int i = 0; i < 100000; i++) {
                service.a("threadB"+ (i+1));
            }
        }
    }
    package com.cky.test;
    
    import com.cky.thread.ThreadA;
    import com.cky.thread.ThreadB;
    import com.cky.utils.Service;
    
    /**
     * Created by chenkaiyang on 2017/12/6.
     */
    public class Test2 {
        public static void main(String[] args) throws InterruptedException {
           Service task = new Service();
            ThreadA threadA = new ThreadA(task);
            threadA.start();
            ThreadB threadB = new ThreadB(task);
            threadB.start();
    
        }
    }
    name=Thread-1执行了a方法
    name=Thread-1退出了a方法
    name=Thread-0执行了a方法
    name=Thread-0退出了a方法
    name=Thread-0执行了a方法
    name=Thread-0退出了a方法
    name=Thread-0执行了a方法
    name=Thread-0退出了a方法

    从结果分析:

    同步代码块中的代码是同步打印的,当前执行和退出是成对出现的,但是线程A和线程B是异步执行的,这有可能造成脏读,因为顺序不确定。

    下面演示出现脏读的例子,

    package com.cky.utils;
    
    /**
     * Created by chenkaiyang on 2017/12/7.
     */
    public class MyService {
    
        public MyList addServiceMethod(MyList list, String data) {
            try {
                if (list.getSize() <1) {
                    Thread.sleep(2000);//模拟从远程花费2秒
                    list.add(data);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            return list;
        }
    
    }
    package com.cky.utils;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * Created by chenkaiyang on 2017/12/7.
     */
    public class MyList {
        private List list = new ArrayList();
        synchronized  public void add(String str) {
            list.add(str);
        }
        synchronized  public int getSize() {
            return list.size();
        }
    }
    package com.cky.thread;
    
    import com.cky.utils.MyList;
    import com.cky.utils.MyService;
    
    /**
     * Created by chenkaiyang on 2017/12/5.
     */
    public class ThreadA extends Thread{
        private MyList list;
        public ThreadA(MyList list) {
            super();
            this.list = list;
        }
    
        @Override
        public void run() {
            super.run();
            MyService myService = new MyService();
            myService.addServiceMethod(list, "a");
        }
    }
    package com.cky.thread;
    
    import com.cky.utils.MyList;
    import com.cky.utils.MyService;
    
    /**
     * Created by chenkaiyang on 2017/12/5.
     */
    public class ThreadB extends  Thread{
    
        private MyList list;
        public ThreadB(MyList list) {
            super();
            this.list = list;
        }
    
        @Override
        public void run() {
            super.run();
            MyService myService = new MyService();
            myService.addServiceMethod(list, "b");
        }
    }
    package com.cky.test;
    
    import com.cky.thread.ThreadA;
    import com.cky.thread.ThreadB;
    import com.cky.utils.MyList;
    
    /**
     * Created by chenkaiyang on 2017/12/6.
     */
    public class Test2 {
        public static void main(String[] args) throws InterruptedException {
            MyList myList = new MyList();
            ThreadA threadA = new ThreadA(myList);
            threadA.start();
            ThreadB threadB = new ThreadB(myList);
            threadB.start();
            Thread.sleep(5000);
            System.out.println(myList.getSize());
    
        }
    }
    D:itjdk1.8injava -Didea.launcher.port=7533 "-Didea.launcher.bin.path=D:itideaIntelliJ IDEA 2016.3.3in" -Dfile.encoding=UTF-8 -classpath "D:itjdk1.8jrelibcharsets.jar;D:itjdk1.8jrelibdeploy.jar;D:itjdk1.8jrelibextaccess-bridge-64.jar;D:itjdk1.8jrelibextcldrdata.jar;D:itjdk1.8jrelibextdnsns.jar;D:itjdk1.8jrelibextjaccess.jar;D:itjdk1.8jrelibextjfxrt.jar;D:itjdk1.8jrelibextlocaledata.jar;D:itjdk1.8jrelibext
    ashorn.jar;D:itjdk1.8jrelibextsunec.jar;D:itjdk1.8jrelibextsunjce_provider.jar;D:itjdk1.8jrelibextsunmscapi.jar;D:itjdk1.8jrelibextsunpkcs11.jar;D:itjdk1.8jrelibextzipfs.jar;D:itjdk1.8jrelibjavaws.jar;D:itjdk1.8jrelibjce.jar;D:itjdk1.8jrelibjfr.jar;D:itjdk1.8jrelibjfxswt.jar;D:itjdk1.8jrelibjsse.jar;D:itjdk1.8jrelibmanagement-agent.jar;D:itjdk1.8jrelibplugin.jar;D:itjdk1.8jrelib
    esources.jar;D:itjdk1.8jrelib
    t.jar;F:springboot	hreaddemooutproduction	hreaddemo;D:itideaIntelliJ IDEA 2016.3.3libidea_rt.jar" com.intellij.rt.execution.application.AppMain com.cky.test.Test2
    2

    上面出现了脏读:两个线程以异步的方式返回list参数的size大小,解决方法就是同步化。

    package com.cky.utils;
    
    /**
     * Created by chenkaiyang on 2017/12/7.
     */
    public class MyService {
    
        public MyList addServiceMethod(MyList list, String data) {
            try {
                synchronized (list) {
                    if (list.getSize() <1) {
                        Thread.sleep(2000);//模拟从远程花费2秒
                        list.add(data);
                    }
                }
    
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            return list;
        }
    
    }
    D:itjdk1.8injava -Didea.launcher.port=7534 "-Didea.launcher.bin.path=D:itideaIntelliJ IDEA 2016.3.3in" -Dfile.encoding=UTF-8 -classpath "D:itjdk1.8jrelibcharsets.jar;D:itjdk1.8jrelibdeploy.jar;D:itjdk1.8jrelibextaccess-bridge-64.jar;D:itjdk1.8jrelibextcldrdata.jar;D:itjdk1.8jrelibextdnsns.jar;D:itjdk1.8jrelibextjaccess.jar;D:itjdk1.8jrelibextjfxrt.jar;D:itjdk1.8jrelibextlocaledata.jar;D:itjdk1.8jrelibext
    ashorn.jar;D:itjdk1.8jrelibextsunec.jar;D:itjdk1.8jrelibextsunjce_provider.jar;D:itjdk1.8jrelibextsunmscapi.jar;D:itjdk1.8jrelibextsunpkcs11.jar;D:itjdk1.8jrelibextzipfs.jar;D:itjdk1.8jrelibjavaws.jar;D:itjdk1.8jrelibjce.jar;D:itjdk1.8jrelibjfr.jar;D:itjdk1.8jrelibjfxswt.jar;D:itjdk1.8jrelibjsse.jar;D:itjdk1.8jrelibmanagement-agent.jar;D:itjdk1.8jrelibplugin.jar;D:itjdk1.8jrelib
    esources.jar;D:itjdk1.8jrelib
    t.jar;F:springboot	hreaddemooutproduction	hreaddemo;D:itideaIntelliJ IDEA 2016.3.3libidea_rt.jar" com.intellij.rt.execution.application.AppMain com.cky.test.Test2
    1

    由于list参数对象在项目中实一份单例,也正需要对list参数的getSize方法做同步的调用。

  • 相关阅读:
    SQL Server 2012提供的OFFSET/FETCH NEXT与Row_Number()对比测试
    sql 知识点
    javascript基础拾遗——词法作用域
    Linux 软件包管理
    涉略spring
    WebReBuild年会流水记
    javascript面向对象学习笔记(一)——继承
    算法学习——动态规划策略入门
    编程之美读书笔记——2.3寻找水王
    Linux 引导流程解析
  • 原文地址:https://www.cnblogs.com/edison20161121/p/8000431.html
Copyright © 2020-2023  润新知