1、锁的八个问题演示
以下为资源类:
class Phone {
public static synchronized void sendSMS() throws Exception {
//停留4秒
TimeUnit.SECONDS.sleep(4);
System.out.println("------sendSMS");
}
public synchronized void sendEmail() throws Exception {
System.out.println("------sendEmail");
}
public void getHello() {
System.out.println("------getHello");
}
}
(1)、标准访问,先打印短信还是邮件 ------sendSMS ------sendEmail
(2)、 停4秒在短信方法内,先打印短信还是邮件 ------sendSMS ------sendEmail
(3)、 新增普通的hello方法,是先打短信还是hello ------getHello ------sendSMS
(4)、现在有两部手机,先打印短信还是邮件 ------sendEmail ------sendSMS
(5)、两个静态同步方法,1部手机,先打印短信还是邮件 ------sendSMS ------sendEmail
(6)、 两个静态同步方法,2部手机,先打印短信还是邮件 ------sendSMS ------sendEmail
(7)、1个静态同步方法,1个普通同步方法,1部手机,先打印短信还是邮件 ------sendEmail ------sendSMS
(8)、1个静态同步方法,1个普通同步方法,2部手机,先打印短信还是邮件 ------sendEmail ------sendSMS
结论:
一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用其中的一个synchronized方法
其它的线程都只能等待,换句话说,某一个时刻内,只能有唯一一个线程去访问这些synchronized方法,锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法
加个普通方法后发现和同步锁无关
换成两个对象后,不是同一把锁了,情况立刻变化。
synchronized实现同步的基础:Java中的每一个对象都可以作为锁。
具体表现为以下3种形式。
对于普通同步方法,锁是当前实例对象。
对于静态同步方法,锁是当前类的Class对象。
对于同步方法块,锁是Synchonized括号里配置的对象
当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁。
也就是说如果一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁,
可是别的实例对象的非静态同步方法因为跟该实例对象的非静态同步方法用的是不同的锁,
所以毋须等待该实例对象已获取锁的非静态同步方法释放锁就可以获取他们自己的锁。
所有的静态同步方法用的也是同一把锁——类对象本身,这两把锁是两个不同的对象,所以静态同步方法与非静态同步方法之间是不会有竞态条件的。
但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,而不管是同一个实例对象的静态同步方法之间,还是不同的实例对象的静态同步方法之间,只要它们同一个类的实例对象!
2、公平锁与非公平锁
公平锁:雨露均沾,效率相对较低
非公平锁:线程饿死,效率较高
源码中:
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
可以看到ReentrantLock的这个有参构造,当为true时为公平锁,false为非公平锁。在公平锁中,在获取锁的是否会先进行判断,所以效率相对较低
3、可重入锁(递归锁)
synchronized与lock都是可重入锁,一个是隐式(自动加锁,自动释放锁),一个是显式(手动加锁,手动释放锁)
package com.study.demo;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SynchLock {
public static void main(String[] args) {
Lock lock = new ReentrantLock();
// Object ob = new Object();
// new Thread(() -> {
// synchronized (ob) {
// System.out.println(Thread.currentThread().getName()+"外层");
// synchronized (ob) {
// System.out.println(Thread.currentThread().getName()+"中层");
// synchronized (ob) {
// System.out.println(Thread.currentThread().getName()+"内存层");
// }
// }
// }
// },"AA").start();
new Thread(() -> {
lock.lock();
try {
System.out.println("BBBBBB");
try {
lock.lock();
System.out.println("CCCCC");
} finally {
lock.unlock();
}
} finally {
lock.unlock();
}
},"BB").start();
}
}
也就说,从外层到内层可以自由出入,使用的实际是同一把锁
4、死锁
列子:
package com.study.demo;
public class ClosedLock {
public static void main(String[] args) {
Object ob1 = new Object();
Object ob2 = new Object();
new Thread(() -> {
synchronized (ob1) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"持有ob1锁,试图获取ob2锁");
synchronized(ob2) {
System.out.println("获取ob2锁");
}
}
},"AA").start();
new Thread(() -> {
synchronized (ob2) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"持有ob2锁,试图获取ob1锁");
synchronized(ob1) {
System.out.println("获取ob1锁");
}
}
},"AA").start();
}
}