Synchronized

两个线程执行a++;
得到不我们想要的结果,比正确结果少。
原因a++不是原子操作,a++包含三个操作。

对像锁:
包括方法锁(默认锁对象为this当前实例对象) 和同步代码块锁(自己指定锁对象)。

类锁:
指synchronize修饰静态的方法或指定锁为Class对象。

public class demo implements Runnable{
	
	Object lock1 = new Object();
	Object lock2 = new Object();
	
	@Override
	public void run() {
		synchronized(lock1) {
		System.out.println("我是lock1。我叫"+Thread.currentThread().getName());
		try {
			Thread.sleep(3000);
			
		}catch(InterruptedException e){
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()+"运行结束");
		}
		synchronized(lock2) {
			System.out.println("我是lock2。我叫"+Thread.currentThread().getName());
			try {
				Thread.sleep(3000);
				
			}catch(InterruptedException e){
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"运行结束");
		}
		
	}
	
	static demo demoinstance = new demo();
	
	public static void main(String[] args) {
		Thread t1 =new Thread(demoinstance);
		Thread t2 =new Thread(demoinstance);
		t1.start();
		t2.start();
		
		while(t1.isAlive()||t2.isAlive()) {
			
		}
		System.out.println("finished");
		
	}
	
}

1.两个线程同时访问一个对象的同步方法。
相互等待,只能有一个持有。

2.两个线程访问两个对象的同步方法。
同时并行的进行,不会相互干扰,因为锁对象不是同一个。

3.两个线程访问的是synchronized的静态方法。
对应的锁对象是同一把,故要相互等待,只能一个持有。

4.同时访问同步方法与非同步方法。
同时并行的进行,不会相互干扰,synchronized只影响被他修饰的方法,非同步方法不受影响。

5.同时访问一个类的不同的普通同步方法。
对于同一个实例,及时没有指定锁对象是哪一个,但是默认都是this对象,故相互等待。

6.同时访问静态synchronized和非静态synchronized方法。
同时并行的进行,不会相互干扰,因为锁的对象不一样,静态synchronized的锁对象是*.class,非静态synchronized方法的锁对象是this。
http://www.cnblogs.com/owenma/p/8609348.html

7.方法抛出异常后,会释放锁。

3点核心思想

  1. 一把锁只能被一个线程获取,没有拿到锁的线程必须等待;
  2. 每个实例都对应有自己的一把锁,不同实例之间互不影响;例外: 锁对象是*.class以及synchronized修饰的的是static方法的时候,所有对象公用一把锁;
  3. 无论是方法正常运行完毕或者方法抛出异常,都会释放锁。

性质:可重入
指同一线程的外层函数获得锁之后,内程函数可以直接再次获取。
好处: 避免重锁,提升封装性。
粒度: 线程而非调用。

情况一:同一个方法是可重入的。

/*描述: 可重入粒度测试:递归调用本方法*/
public class SynchronizedDemo  {
	int a = 0 ;
	public static void main(String[] args) {
		SynchronizedDemo synchronizedDemo = new SynchronizedDemo();
		synchronizedDemo.method1();
	
	}
	private void method1() {
		System.out.println("这是method1,a= "+a);
		if(a==0) {
			a++;
			method1();
		}
	}
}

情况二:可重入不要求是同一个方法。

/*描述: 可重入粒度测试:调用本类其他方法*/
public class SynchronizedDemo2  {	
	
	public static void main(String[] args) {
		SynchronizedDemo2 synchronizedDemo = new SynchronizedDemo2();
		synchronizedDemo.method1();
	}

	private synchronized void method1() {
		System.out.println("我是method1");
		method2();
	}

	private synchronized void method2() {
		System.out.println("我是method2");		
	}
	
}

情况三:可重入不要求是同一个类中的。

class SynchronizedDemoSuperClass3{
	public synchronized void doSometing() {
		System.out.println("我是父类方法");
	}
}

public class SynchronizedDemo3 extends SynchronizedDemoSuperClass3{
	
	public synchronized void doSomething() {
		System.out.println("我是子类方法");
		super.doSometing();
	}
	
	public static void main(String[] args) {
		SynchronizedDemo3 synchronizedDemo = new SynchronizedDemo3();
		synchronizedDemo.doSomething();
	}	
}


性质:不可中断

加锁和释放锁的原理:

/*方法一和方法二是等价的*/
public class SynchronizedDemo2  {	
	Lock domelock = new ReentrantLock();
	public static void main(String[] args) {
		SynchronizedDemo2 synchronizedDemo = new SynchronizedDemo2();
		synchronizedDemo.method1();
		synchronizedDemo.method2();
	}

	private synchronized void method1() {
		System.out.println("我是synchronized形式的锁");
	}

	private  void method2() {
		domelock.lock();
		try {
			System.out.println("我是lock形式的锁");
		}finally {
			domelock.unlock();
		}
	}
}


public class strive {
	private Object object = new Object();
	
	public void insert(Thread thread) {
		synchronized (object) {
			
		}
	}
}

注意:一个monitorenter,两个monitorexit

一个monitorenter,两个monitorexit


可重入原理:加锁次数计数器

  1. JVM负责跟踪对象被加锁的次数
  2. 线程第一次给对象加锁的时候,计数变为1.每当这个相同的线程在此对象上再次获得锁时,计数会递增。
  3. 每当任务离开时,计数递减,当计数为0的时候,锁被完全释放。

可见性原理:
需要了解java内存模型(JMM):通过主内存这个共享变量存储地址,保证线程之间通讯。(线程都是由本地内存的,各个线程想要通信,必须通过主内存)


synchronized 的缺陷:

  1. 效率低:锁的释放情况少,试图获得锁时不能设定超时,不能中断一个正在试图获得锁的线程。
  2. 不够灵活(读写锁更灵活):加锁和释放的时机单一,每个锁仅有单一的条件(某个对象),可能是不够的。
  3. 无法知道是否成功获取到锁。

lock的方法

	Lock.lock();
	Lock.unlock();
	Lock.trylock();//返回是boolean
	Lock.trylock(long timeout,TimeUnit unit);//可以设置超时时间

常见面试问题:

1.使用注意点:
  1. 锁对象不能为空
  2. 作用域不宜过大
  3. 避免死锁
2.如何选择Lock和synchronized关键字?
  1. 建议都不使用,可以使用java.util.concurrent包中的Automic类,countDown等类
  2. 优先使用现成工具,如果没有就优先使用synchronized关键字,好处是写尽量少的代码就能实现功能。如果需要灵活的加解锁机制,则使用Lock接口。

3.多线程访问同步方法的各种具体情况。


思考题

  1. 多个线程等待同一个synchronized锁的时候,JMV如何选择下一个获取锁的是哪个线程?(调度算法)
  2. Synchronized使得同时只有一个线程可以执行,性能较差,有什么办法可以提升性能?
  3. 我想更灵活地控制锁的获取和释放,怎么办?(自己去实现一个锁)
  4. 什么是锁的升级、降级?什么是JVM里的偏斜锁,轻量级锁,重量级锁。

总结

一句话介绍synchronized
JVM会自动通过使用monitor来加锁和解锁,保证了同时只有一个线程可以执行指定代码,从而保证了线程安全,同时具有可重入和不可中断的性质。


全部内容来源:https://www.imooc.com/learn/1086

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值