6.1、演示synchronized关键字关于锁的8种情况
package com.ae.juc.multiThreadLock; import java.util.concurrent.TimeUnit; class Phone{ public synchronized void sendSMS() throws InterruptedException { //停4秒 TimeUnit.SECONDS.sleep(4); System.out.println("------sendSMS"); } public synchronized void sendEmail(){ System.out.println("------sendEmail"); } public void hello(){ System.out.println("------hello"); } } /** * synchronized关键字关于锁的8种情况演示 * 分析地目的主要以下两点: * 弄清楚synchronized是否是用的同一把锁? * synchronized锁的范围到底是怎么样的? * * 1、标准访问,先打印短信还是邮件 【用的同一把锁this】 * ------sendSMS * ------sendEmail * * 2、停4秒在短信方法内,先打印短信还是邮件 【用的同一把锁this】 * ------sendSMS * ------sendEmail * * 3、新增普通方法hello方法,是先打印短信还是hello 【hello与锁无关,所以先执行】 * ------hello * ------sendSMS * * 4、现在有两部手机,先打印短信还是邮件 【用的不是同一把锁】 * ------sendEmail * ------sendSMS * * 5、两个静态同步方法,1部手机,先打印短信还是邮件 【锁的对象是当前类的Class】 * ------sendSMS * ------sendEmail * * 6、两个静态同步方法,2部手机,先打印短信还是邮件 【锁的对象是当前类的Class】 * ------sendSMS * ------sendEmail * * 7、1个静态同步方法,1个普通同步方法,1部手机,先打印短信还是邮件 【sendSMS使用Class锁,sendEmail使用this锁】 * ------sendEmail * ------sendSMS * * 8、1个静态同步方法,1个普通同步方法,2部手机,先打印短信还是邮件【sendSMS使用Class锁,sendEmail使用this锁】 * ------sendEmail * ------sendSMS */ public class Lock_8 { public static void main(String[] args) throws InterruptedException { Phone phone = new Phone(); Phone phone2 = new Phone(); new Thread(()->{ try { phone.sendSMS(); } catch (InterruptedException e) { e.printStackTrace(); } },"AA").start(); Thread.sleep(100); new Thread(()->{ //phone.sendEmail(); //phone.hello(); phone2.sendEmail(); },"BB").start(); } }
6.2、对于synchronized特点总结
synchronized实现同步的基础: Java中的每一个对象都可以作为锁。
具体表现为以下3种形式:
- 对于普通同步方法,锁是当前实例对象。
- 对于静态同步方法,锁是当前类的Class对象。
- 对于同步方法块,锁是synchonized括号里配置的对象。
6.3、公平锁和非公平锁
1、代码演示
非公平锁【吃独食】
package com.ae.juc.lock; import java.util.concurrent.locks.ReentrantLock; //第一步:创建资源类,在资源类中创建属性和方法。 class Ticket{ //定义属性:票数 private int number = 30; //创建一个可重入锁 private final ReentrantLock lock = new ReentrantLock(); //方法:卖票 public void sale(){ //上锁 lock.lock(); try { if(number > 0){ System.out.println("线程 "+Thread.currentThread().getName()+ " 卖出 "+ (number--) + " 剩下 "+number); } }finally { //解锁 lock.unlock(); } } } public class SaleTicket { public static void main(String[] args) { //第二步:创建多个线程,调用资源类的操作方法。 final Ticket ticket = new Ticket(); //创建三个线程 new Thread(new Runnable() { public void run() { //调用卖票方法 for (int i = 0; i < 40; i++) { ticket.sale(); } } },"aa").start(); new Thread(new Runnable() { public void run() { //调用卖票方法 for (int i = 0; i < 40; i++) { ticket.sale(); } } },"bb").start(); new Thread(new Runnable() { public void run() { //调用卖票方法 for (int i = 0; i < 40; i++) { ticket.sale(); } } },"cc").start(); } }
公平锁【见着有份,每一次执行前需要查看一下】
package com.ae.juc.lock; import java.util.concurrent.locks.ReentrantLock; //第一步:创建资源类,在资源类中创建属性和方法。 class Ticket{ //定义属性:票数 private int number = 30; //创建一个可重入锁 private final ReentrantLock lock = new ReentrantLock(true); //方法:卖票 public void sale(){ //上锁 lock.lock(); try { if(number > 0){ System.out.println("线程 "+Thread.currentThread().getName()+ " 卖出 "+ (number--) + " 剩下 "+number); } }finally { //解锁 lock.unlock(); } } } public class SaleTicket { public static void main(String[] args) { //第二步:创建多个线程,调用资源类的操作方法。 final Ticket ticket = new Ticket(); //创建三个线程 new Thread(new Runnable() { public void run() { //调用卖票方法 for (int i = 0; i < 40; i++) { ticket.sale(); } } },"aa").start(); new Thread(new Runnable() { public void run() { //调用卖票方法 for (int i = 0; i < 40; i++) { ticket.sale(); } } },"bb").start(); new Thread(new Runnable() { public void run() { //调用卖票方法 for (int i = 0; i < 40; i++) { ticket.sale(); } } },"cc").start(); } }
解释说明
//默认情况下,new ReentrantLock()就是一个非公平锁 private final ReentrantLock lock = new ReentrantLock(false);//非公平锁 private final ReentrantLock lock = new ReentrantLock(true);//公平锁
2、源码分析
public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); } //公平锁 static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; final void lock() { acquire(1); } /** * Fair version of tryAcquire. Don't grant access unless * recursive call or no waiters or is first. */ protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } } //非公平锁 static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; /** * Performs lock. Try immediate barge, backing up to normal * acquire on failure. */ final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } }
3、公平锁与非公平锁的总结
6.4、可重入锁
概念介绍
synchronized 和 Lock 都是可重入锁,synchronized 是自动上锁和解锁,所以是隐式的;Lock是手动上锁和解锁,所以是显式的。它们用的都是一把锁,只要拿到锁,可以自由进入里面的所有区域。
案例演示Synchronized实现可重入锁
package com.ae.juc.reentrantLock; /** * 演示 synchronized可重入锁的特性 */ public class SynchronizedReentrantLock { /** * 特性二:可重入锁又叫递归锁 */ public synchronized void add(){ add(); } public static void main(String[] args) { new SynchronizedReentrantLock().add(); /** * 特性一:同一把锁,可以自由进入里面的所有区域 */ Object o = new Object(); new Thread(() -> { synchronized (o){ System.out.println(Thread.currentThread().getName() + " 外层"); synchronized (o){ System.out.println(Thread.currentThread().getName() + " 中层"); synchronized (o){ System.out.println(Thread.currentThread().getName() + " 内层"); } } } },"AA").start(); } }
案例演示Lock实现可重入锁
package com.ae.juc.reentrantLock; import java.util.concurrent.locks.ReentrantLock; /** * Lock 实现可重入锁 */ public class LockImplReentrantLock { public static void main(String[] args) { ReentrantLock lock = new ReentrantLock(); new Thread(() ->{ try { lock.lock(); System.out.println(Thread.currentThread().getName()+" 外层"); try { lock.lock(); System.out.println(Thread.currentThread().getName()+" 内层"); }finally { lock.unlock(); } }finally { lock.unlock(); } },"BB").start(); } }
6.5、死锁
什么是死锁?
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
产生死锁的原因?
- 系统资源不足
- 进程运行推进顺序不合适
- 资源分配不当
验证是否是死锁
- jps
- jstack (jvm 自带的命令)