synchronized将任意对象作为对象监视器

Posted 霓裳梦竹

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了synchronized将任意对象作为对象监视器相关的知识,希望对你有一定的参考价值。

多个线程调用同一个对象中的不同名称的synchronized同步方法或synchronized(this)同步代码块时,调用的效果就是按顺序执行,也就是同步的,阻塞的。
这说明synchronized同步方法或synchronized(this)同步代码块分别有两种作用
(1)synchronized同步方法
1)对其他synchronized同步方法或synchronized(this)同步代码块调用呈阻塞状态
2)同一时间只有一个线程可以执行synchronized同步方法中的代码
(2)synchronized(this)同步代码块
1)对其他synchronized同步方法或synchronized(this)同步代码块调用呈阻塞状态
2)同一时间只有一个线程可以执行synchronized(this)同步代码块中的代码
Java还支持对“任意对象”作为“对象监视器”来实现同步的功能。这个“任意对象”大多数是实例变量及方法的参数,使用synchronized(非this对象)
synchronized(非this对象)格式的作用只有1中:synchronized(非this对象x)同步代码块
1)在多个线程持有“对象监视器”为同一个对象的前提下,同一时间只有一个线程可以执行synchronized(非this对象x)同步代码块中的代码
2)当持有“对象监视器”为同一个对象的前提下,同一时间只有一个线程可以执行synchronized(非this对象x)同步代码块中的代码。
package synBlockString;

/**
 * Created by Administrator on 2017/1/19 0019.
 */
public class Service {
    private String usernameParam;
    private String passwordParam;
    private String anyString = new String();
    public void setUsernamePassword(String username,String password){
        try{
            synchronized (anyString){
                System.out.println("线程名称为:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+"进入同步");
                usernameParam = username;
                Thread.sleep(2000);
                passwordParam = password;
                System.out.println("线程名称为:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+"离开同步");
            }
        }catch(InterruptedException e){
            e.printStackTrace();
        }
    }
}
package synBlockString;

/**
 * Created by Administrator on 2017/1/19 0019.
 */
public class ThreadA extends Thread {
    private Service service;
    public ThreadA(Service service){
        super();
        this.service = service;
    }

    public void run(){
        service.setUsernamePassword("a","aa");
    }
}
package synBlockString;

/**
 * Created by Administrator on 2017/1/19 0019.
 */
public class ThreadB extends Thread {
    private Service service;
    public ThreadB(Service service){
        super();
        this.service = service;
    }
    public void run(){
        service.setUsernamePassword("b","bb");
    }

}
package synBlockString;



/**
 * Created by Administrator on 2017/1/19 0019.
 */
public class Run {
    public static void main(String [] args){
        Service service = new Service();
        ThreadA threadA = new ThreadA(service);
        threadA.setName("A");
        threadA.start();
        ThreadB threadB = new ThreadB(service);
        threadB.setName("B");
        threadB.start();
        threadB.start();

    }
}
线程名称为:A在1484823811547进入同步
线程名称为:A在1484823813559离开同步
线程名称为:B在1484823813559进入同步
线程名称为:B在1484823815573离开同步

如果修改service.java为

package synBlockString;

/**
 * Created by Administrator on 2017/1/19 0019.
 */
public class Service {
    private String usernameParam;
    private String passwordParam;

    public void setUsernamePassword(String username,String password){
        try{
            String anyString = new String();
            synchronized (anyString){
                System.out.println("线程名称为:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+"进入同步");
                usernameParam = username;
                Thread.sleep(2000);
                passwordParam = password;
                System.out.println("线程名称为:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+"离开同步");
            }
        }catch(InterruptedException e){
            e.printStackTrace();
        }
    }
}
线程名称为:A在1484823870625进入同步
线程名称为:B在1484823870625进入同步
线程名称为:B在1484823872638离开同步
线程名称为:A在1484823872638离开同步

所以,使用synchronized(非this对象)同步代码块格式进行同步操作时,对象监视器必须是同一个对象,如果不是同一个对象监视器,运行的结果就是异步调用了,就会交叉运行。

再看下一个示例:

package synBlockString2;


/**
 * Created by Administrator on 2017/1/19 0019.
 */
public class Service  {
    private String anyThing = new String();
    public void a(){
        try {
            synchronized (anyThing){
                System.out.println("a begin");
                Thread.sleep(2000);
                System.out.println("a end");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    synchronized  public void b(){
        System.out.println(" b begin");
        System.out.println(" b end");
    }
}
package synBlockString2;

/**
 * Created by Administrator on 2017/1/19 0019.
 */
public class ThreadA extends Thread {
    private Service service;
    public ThreadA(Service service){
        super();
        this.service = service;
    }
    public  void run(){
        service.a();
    }
}
package synBlockString2;

/**
 * Created by Administrator on 2017/1/19 0019.
 */
public class ThreadB extends Thread {
    private Service service;
    public ThreadB(Service service){
        super();
        this.service = service ;
    }
    public void run(){
        service.b();
    }
}
package synBlockString2;

/**
 * Created by Administrator on 2017/1/19 0019.
 */
public class Run {
    public static void main(String [] args){
        Service service = new Service();
        ThreadA threadA = new ThreadA(service);
        threadA.setName("A");
        threadA.start();
        ThreadB threadB = new ThreadB(service);
        threadB.setName("B");
        threadB.start();
    }
}
运行结果:
a begin
 b begin
 b end
a end

由于对象监视器不同,所以运行结果就是异步的

同步代码块放在非同步synchronized方法中进行声明,并不能保证调用方法的线程的执行同步/顺序性,也就是线程调用方法的顺序是无序的,虽然在同步块中执行的顺序是同步的,这样就容易出现脏读问题。

以上是关于synchronized将任意对象作为对象监视器的主要内容,如果未能解决你的问题,请参考以下文章

Java synchronized关键字学习二

内置锁synchronized的几个要注意的对象监视器

2.2.8细化验证3个结论

Java多线程-synchronized(非this对象)

java并发条件阻塞Condition的应用

synchronized原理是啥?