Java多线程编程04 -锁重入&对象监视器

-锁重入

关键字synchronized拥有锁重入的功能,也就是当一个线程获得对象锁之后,再次请求该对象锁(调用这个对象加锁的方法)时可以直接获得锁。也就是说在一个synchronized 方法/块内调用本类的其他synchronized方法/块时永远得到锁。
例子:

public class SyncReUseService {
    synchronized public void service1(){
        System.out.println("service1");
        service2();
    }

    synchronized public void service2(){
        System.out.println("service2");
        service3();
    }

    synchronized public void service3(){
        System.out.println("service3");
    }
}

public class SyncReUseServiceThread extends Thread {
    @Override
    public void run() {
        super.run();
        SyncReUseService service = new SyncReUseService();
        service.service1();
    }
}

public class ThreadRunMain {
    public static void main(String[] args) {
        testSyncReUseServiceThread();
    }

    public static void testSyncReUseServiceThread(){
        SyncReUseServiceThread t = new SyncReUseServiceThread();
        t.start();
    }
}
打印:
service1
service2
service3

- 可重入锁:自己可以再次获得自己的内部锁。如果不能锁重入的话,就会造成死锁。
- 可重入锁也支持继承: 子类完全可以通过可重入锁调用父类的同步方法。
- 同步不能被继承: 父类的方法加锁了,子类重载的话,是不会自动继承锁的,必须显示的加锁。
- 出现异常,锁自动释放

- 同步代码块

在一个方法中,可以用 synchronized关键字来修饰一个代码块:

public class SyncReUseService {
	private int a;
	private int b;
     public void service1(){
        System.out.println("begin");
        int aa = 0, bb = 1;
        synchronized(this) {
        	a = aa;
        	b = bb;
        }
		System.out.println(a);
		System.out.println(b);
    }
}

当一个线程访问object的同步代码块的时候,另一个线程仍然可以访问该object对象中的非synchronized(this)代码块。和synchronized修饰的方法一样,synchronized(this)也是锁定的当前对象。

这里说一下这个对象:
当两个进程只用同一个业务对象实例化的时候,才会涉及到共享的问题。而这个共享也是线程安全的根本和问题所在。

Server serve = new Serve();
Mythread myThread1 = new MyThread(serve);// 传递同一个对象
Mythread myThread2 = new MyThread(serve);
在Mythread类构造函数里:
this.serve = serve;(数据字段声明了 private Serve serve;)

这样才会涉及资源共享,这个资源就是对象serve。
如果你new 两个Serve对象,分别传给myThread1、2 那么上面所有的锁什么的都没有意义了,因为两个线程都拥有一个完整且独占的资源对象serve,不能产生共享,进而没有线程安全的问题。

- 任意对象当作对象监视器
  • synchronized修饰方法:
    锁住类内其他synchronized修饰的方法和synchronized(this)同步代码块
  • synchronized(this) 同步代码块 (跟上面一样的阻塞效果)
    锁住类内其他synchronized修饰的方法和synchronized(this)同步代码块

我们还可以用 非this对象 来当作 “对象监视器”:

public class SyncReUseService {
	private int a;
	private String anyString;
     public void service1(){
        System.out.println("begin");
        int aa = 0, bb = 1;
        
        // String anyString = new String();
        synchronized(anyString) { // 用的是 非this对象: anyString
        	a = aa;
        }
		System.out.println(a);
    }
}

但是上面这个效果还是锁this对象差不多,因为虽然anyString不是this对象,但是它是类的一个公共字段— 还是共享的资源。你访问的时候,我进不去。
如果把上面代码修改一下:把private String anyString;这个对象放到方法内部声明(如注释那样)这样他就变成一个临时变量— 非共享资源。那么不同的线程调用这个方法的时候,都会得到一个"不同"的anyString临时对象,也就都可以获得锁了,又变成异步的了。
所以:使用 synchronized(非this对象)同步代码块的时候,对象监视器必须还是同一个对象。再比如下面这种情况,另外一个方法service2被锁住,但是因为这个锁是this对象,但是service里面代码块的锁是临时变量anyString,两个锁不是同一个对象,显然还是异步的效果,很容易出现脏读。

public class SyncReUseService {
	private int a;
     public void service1(){
        String anyString = new String();
        synchronized(anyString) { // 用的是 非this对象: anyString
        	System.out.println(a);
        }
    }
    synchronized public void service2(){
		System.out.println(a);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值