//悲观锁基于synchronized关键字
public synchronized void m1(){
//code logic segment....
}
//悲观锁基于Lock对象实现
ReentrantLock reentrantLock = new ReentrantLock();
public void m2(){
reentrantLock.lock();
try {
//code logic segment....
} finally {
reentrantLock.unlock();
}
}
//乐观锁的调用方式,保证多个线程使用的是同一个AtomicInteger
AtomicInteger atomicInteger = new AtomicInteger();
atomicInteger.incrementAndGet();
a,b 两个线程分别使用同步监视器修饰 a 线程启动,0.2 秒后 b 线程启动 可以预见,先执行 a 后执行 b
class Phone {//资源类
public synchronized void sendEmail() {
System.out.println(Thread.currentThread().getName() + "-----sendEmail");
}
public synchronized void sendSMS() {
System.out.println(Thread.currentThread().getName() + "-----sendSMS");
}
}
...
public static void main(String[] args) {//一切程序的入口
Phone phone = new Phone();
new Thread(() -> {
phone.sendEmail();
}, "a").start();
//暂停毫秒,保证a线程先启动
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone.sendSMS();
}, "b").start();
}
在场景一的资源类中,sendEmail方法中加入暂停3秒钟 由于 synchronized 是悲观锁,并且 sleep 不回释放锁,因此 a 线程先执行,并且执行 sleep 时程序也会阻塞,当 a 线程执行完毕时,b 线程才会执行 可以预见结果和场景一致
public synchronized void sendEmail() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "-----sendEmail");
}
一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用其中的一个synchronized方法了, 其它的线程都只能等待,换句话说,某一个时刻内,只能有唯一的一个线程去访问这些synchronized方法 锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法
添加一个普通的hello方法,先打印邮件还是hello? 普通方法线程共享
class Phone {//资源类
....
public void hello() {
System.out.println("-------hello");
}
....
}
//main方法
public static void main(String[] args) {//一切程序的入口
Phone phone = new Phone();
new Thread(() -> {
phone.sendEmail();
}, "a").start();
//暂停毫秒,保证a线程先启动
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone.hello();
}, "b").start();
}
有两部手机,请问先打印邮件还是短信 两次方法的调用 synchronized 锁住的对象不同
//资源类不变
public static void main(String[] args) {//一切程序的入口
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(() -> {
phone.sendEmail();
}, "a").start();
//暂停毫秒,保证a线程先启动
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone2.sendSMS();
}, "b").start();
}
在场景 一的情况下将两个方法均添加 static 修饰,测试代码同场景一 静态同步方法(类锁)
public static synchronized void sendEmail() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "-----sendEmail");
}
public static synchronized void sendSMS() {
System.out.println(Thread.currentThread().getName() + "-----sendSMS");
}
在场景五的情况下,添加一部手机,用 phone2 调用 sentSMS
//资源类同场景五
//测试代码
public static void main(String[] args) {//一切程序的入口
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(() -> {
phone.sendEmail();
}, "a").start();
//暂停毫秒,保证a线程先启动
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone2.sendSMS();
}, "b").start();
}
对于普通同步方法,锁的是当前实例对象,通常指this,具体的一部部手机,所有的普通同步方法用的都是同一把锁——>实例对象本身(方法调用者), 对于静态同步方法,锁的是当前类的Class对象,如Phone.class唯一的一个模板(调用者所属的类型) 对于同步方法块,锁的是 synchronized 括号内的对象
有1个静态同步方法,有1个普通同步方法,有1部手机,请问先打印邮件还是短信
//将短信方法还原为普通同步方法
public static synchronized void sendSMS() {
System.out.println(Thread.currentThread().getName() + "-----sendSMS");
}
public static void main(String[] args) {//一切程序的入口
Phone phone = new Phone();
new Thread(() -> {
phone.sendEmail();
}, "a").start();
//暂停毫秒,保证a线程先启动
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone.sendSMS();
}, "b").start();
}
类锁与对象锁不是同一个,各种执行,a 线程睡眠 3 秒
有1个静态同步方法,有1个普通同步方法,有2部手机,请问先打印邮件还是短信
//资源类同场景七
//测试代码
public static void main(String[] args) {//一切程序的入口
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(() -> {
phone.sendEmail();
}, "a").start();
//暂停毫秒,保证a线程先启动
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone2.sendSMS();
}, "b").start();
}
当一个线程试图访问同步代码时它首先必须得到锁,正常退出或抛出异常时必须释放锁。 所有的普通同步方法用的都是同一把锁——实例对象本身,就是new出来的具体实例对象本身,本类this 也就是说如果一个实例对象的普通同步方法获取锁后,该实例对象的其他普通同步方法必须等待获取锁的方法释放锁后才能获取锁。 所有的静态同步方法用的也是同一把锁——类对象本身,就是我们说过的唯一模板Class 具体实例对象this和唯一模板Class,这两把锁是两个不同的对象,所以静态同步方法与普通同步方法之间是不会有竞态条件的 但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁。
javap -c ****.class
文件反编译Object object = new Object();
public void m1() {
synchronized (object) {
System.out.println("----hello synchronized code block");
}
}
Object object = new Object();
public void m1() {
synchronized (object) {
System.out.println("----hello synchronized code block");
throw new RuntimeException("-----exp");
}
}
public synchronized void m2() {
System.out.println("----hello synchronized m2");
}
javap -v .\LockSyncDemo.class
进行编译public static synchronized void m3() {
System.out.println("----hello static synchronized m3");
}
// initialize the monitor, exception the semaphore, all other fields
// are simple integers or pointers
ObjectMonitor() {
_header = NULL;
_count = 0; //记录该线程获取锁的次数
_waiters = 0,
recursions = 0; //锁的重入次数
_object = NULL;
_owner = NULL; //指向持有ObjectMonitor对象的线程
_WaitSet = NULL; //存放的处于wait状态的线程队列
_WaitSetLock = 0 ;
_Responsible = NULL ;
_succ = NULL ;
_cxq = NULL ;
FreeNext = NULL ;
_EntryList = NULL ; //存放处于等待锁block状态的线程队列
_SpinFreq = 0 ;
_SpinClock = 0 ;
OwnerIsThread = 0 ;
_previous_owner_tid = 0;
}
公平锁是指多线程按照申请锁的顺序来获取锁,先来先得
Lock lock = new ReentrantLock(true); //true
表示先来先得
非公平锁是指,获取锁的顺序不是按照申请锁的顺序 存在后申请的线程比先申请的线程优先获取锁 在高并发环境下,存在优先级翻转或者锁饥饿的状态 锁饥饿 : 某个线程长时间得不到锁
Lock lock = new ReentrantLock(false); //false
表示非公平锁,并发抢锁Lock lock = new ReentrantLock(); //默认非公平锁
同一个线程在外层方法获取锁的时候,再进入该线程都内层方法会自动获取锁(前提,锁的对象是同一个),不会因为之前已经获取过还没释放而阻塞
private static void reEntryM1() {
final Object object = new Object();
new Thread(() -> {
synchronized (object) {
System.out.println(Thread.currentThread().getName() + "\t ----外层调用");
synchronized (object) {
System.out.println(Thread.currentThread().getName() + "\t ----中层调用");
synchronized (object) {
System.out.println(Thread.currentThread().getName() + "\t ----内层调用");
}
}
}
}, "t1").start();
}
public synchronized void m1() {
//指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁,这样的锁就叫做可重入锁。
System.out.println(Thread.currentThread().getName()+"\t ----come in");
m2();
System.out.println(Thread.currentThread().getName()+"\t ----end m1");
}
public synchronized void m2(){
System.out.println(Thread.currentThread().getName()+"\t ----come in");
m3();
}
public synchronized void m3(){
System.out.println(Thread.currentThread().getName()+"\t ----come in");
}
//main方法调用
public static void main(String[] args){
ReEntryLockDemo reEntryLockDemo = new ReEntryLockDemo();
new Thread(() -> {
reEntryLockDemo.m1();
}, "t1").start();
}
new Thread(() -> {
lock.lock();
try
{
System.out.println(Thread.currentThread().getName()+"\t ----come in外层调用");
lock.lock();
try
{
System.out.println(Thread.currentThread().getName()+"\t ----come in内层调用");
}finally {
lock.unlock();
}
}finally {
// 由于加锁次数和释放次数不一样,第二个线程始终无法获取到锁,导致一直在等待。
lock.unlock();// 正常情况,加锁几次就要解锁几次
}
},"t1").start();
new Thread(() -> {
lock.lock();
try
{
System.out.println(Thread.currentThread().getName()+"\t ----come in外层调用");
}finally {
lock.unlock();
}
},"t2").start();
public static void main(String[] args) {
final Object objectA = new Object();
final Object objectB = new Object();
new Thread(() -> {
synchronized (objectA) {
System.out.println(Thread.currentThread().getName() + "\t 自己持有A锁,希望获得B锁");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (objectB) {
System.out.println(Thread.currentThread().getName() + "\t 成功获得B锁");
}
}
}, "A").start();
new Thread(() -> {
synchronized (objectB) {
System.out.println(Thread.currentThread().getName() + "\t 自己持有B锁,希望获得A锁");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (objectA) {
System.out.println(Thread.currentThread().getName() + "\t 成功获得A锁");
}
}
}, "B").start();
}
jps -l
jstack pid