java synchronized 顺序_JAVA学习——synchronized

synchronized可以用于修饰类的实例方法、静态方法和代码块。

1,实例方法

synchronized实例方法实际保护的是同一个对象的方法调用,确保同时只有一个线程执行。synchronized实例方法保护的是当前实例对象,即this,this对象有一个锁和一个等待队列,锁只能被一个线程持有,其他试图获得同样锁的线程需要等待。执行synchronized实例方法的过程大致如下:

1,尝试获得锁,如果能够获得锁,继续下一步,否则加入等待队列,阻塞并等待唤醒。

2,执行实例方法体代码。

3,释放锁,如果等待队列上有等待的线程,从中取一个并唤醒,如果有多个等待的线程,唤醒哪一个是不一定的,不保证公平性,

当前线程不能获得锁的时候,它会加入等待队列等待,线程的状态会变为BLOCKED。

多个线程是可以同时执行同一个synchronized实例方法的,只要它们访问的对象时不同的即可。

synchronized保护的是对象而非代码,只要访问的是同一个对象的synchronized方法,即使是不同的代码,也会被同步顺序访问。比如,访问一个实例的两个synchronized修饰的实例方法methodA和methodB,一个线程执行methodA,另一个线程执行methodB,它们是不能同时执行的,会被synchronized同步顺序执行。

synchronized方法不能防止非synchronized方法被同时执行,比如,访问一个实例的synchronized修饰的实例方法methodA和非synchronized修饰实例方法methodB,一个线程执行methodA,另一个线程执行methodB,它们可以同时执行的,一般在保护变量时,需要在所有访问该变量的方法上加上synchronized。

2,静态方法

synchronized修饰的静态方法保护的是类对象,即xxx.class。跟实例对象一样,每个类对象都有一个锁和一个等待队列。

synchronized静态方法和synchronized实例方法保护的是不同的对象,不同的线程,可以一个执行synchronized静态方法,另一个执行synchronized实例方法。

3,代码块

伪代码

//实例对象synchronized(this)

//类对象synchronized(test.class)

//任意对象Object lock = new Object();

synchronized(lock)

synchronized同步的对象可以是任意对象,任意对象都有一个锁和等待队列,任何对象都可以作为锁对象。

进一步理解synchronized

可重入性

synchronized是可重入的,对同一个执行线程,它在获得了锁之后,在调用其他需要同样锁的代码时,可以直接调用。

可重入是通过记录锁的持有线程和持有数量来实现的,当调用synchronized保护的代码时,检查对象是否已被锁,如果是,再检查是否被当前线程锁定,如果是,增加持有数量,如果不是被当前线程锁定,才加入等待队列,当释放锁时,减少持有数量,当数量为0时才释放整个锁。

内存可见性

synchronized可以保证原子操作外,还可以保证内存可见性,在释放锁时,所有写入都会写回内存,而获得锁后,都会从内存中读最新数据。

如果只是为了保证内存可见性,使用synchronized的成本有点高,可以使用轻量级的修饰符volatile。

死锁

使用synchronized或者其他锁,要注意死锁。

public class DeadLockDemo {

private static Object lockA = new Object();

private static Object lockB = new Object();

private static void startThreadA() {

Thread aThread = new Thread() {

@Override

public void run() {

synchronized (lockA) {

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

}

synchronized (lockB) {

}

}

}

};

aThread.start();

}

private static void startThreadB() {

Thread bThread = new Thread() {

@Override

public void run() {

synchronized (lockB) {

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

}

synchronized (lockA) {

}

}

}

};

bThread.start();

}

public static void main(String[] args) {

startThreadA();

startThreadB();

}

}

死锁出现的原因就是多个线程互相持有锁。

应该尽量避免在持有一个锁的同时去申请另一个锁,如果确实需要多个锁,所有代码都应该按照相同的顺序去申请锁。

如果需要更细粒度的锁控制,可以使用显式锁Lock。

java自带的jstack命令会报告发现的死锁。如何使用jstack分析线程状态 - Jessica程序猿 - 博客园​www.cnblogs.com6b92fcf4d68b7d65b0a437e1fe21f321.png

参考:

《java编程的逻辑》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值