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方法做同步的调用。

原文地址:https://www.cnblogs.com/edison20161121/p/8000431.html