多线程锁
常见锁介绍
synchronized锁的八中情况
package com.shaonian.juc.more_thread_lock;
import java.util.concurrent.TimeUnit;
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");
}
}
/**
* @Description: 8锁
*
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
*/
public class Lock8QuestionBySynchronized {
public static void main(String[] args) throws Exception {
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(() -> {
try {
phone.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
}, "AA").start();
Thread.sleep(100);
new Thread(() -> {
try {
// phone.sendEmail();
// phone.getHello();
phone2.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
}, "BB").start();
}
}
代码结果原因分析,见博客中synchronized关键字回顾。
ReentrantLock读写锁分析
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync();//默认是非公平锁
}
/**
* 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) {//传入false,定义的就是非公平的读写锁
sync = fair ? new FairSync() : new NonfairSync();
}
非公平锁(ReentrantLock实现)
非公平锁是多个线程加锁时直接尝试获取锁,能抢到锁到直接占有锁,抢不到才会到等待队列的队尾等待。确定可能会出现线程饿死现象(锁一直被一个线程持有,某些线程一直拿不到锁),优点是执行效率高,可以直接获取锁。
static final class NonfairSync extends Sync {//ReentrantLock中的静态内部类
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);
}
}
公平锁(ReentrantLock实现)
公平锁是指多个线程按照申请锁的顺序来获取锁,线程直接进入队列中排队,队列中的第一个线程才能获得锁。保证每个线程都能拿到锁,优点是执行效率比非公平锁低,优点是不会出现线程饿死现象。
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;
}
}
可重入锁(ReentrantLock实现)
synchronized(隐式)和Lock(显式)都是可重入锁,也叫递归锁。特点,类似于,有了第一把锁,此锁内部锁的代码都可以访问。像开了大门,开剩下的门就不用再用钥匙开门了。
/**
* @author 长名06
* @version 1.0
* 可重入锁案例
*/
public class ReentrantLockDemo {
private synchronized void add(){
add();
}
public static void main(String[] args) {
//new ReentrantLockDemo().add();//会出现StackOverflowError
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();
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();
}
}).start();
//结果
//Thread-0外层操作
//Thread-0内层操作
}
}
死锁
基本介绍
死锁,是一种多个线程在执行过程中,因竞争资源时,导致线程互相持有其他线程执行需要的锁,从而引起线程等待的现象。在没有外力的作用下,程序会崩溃。
引起死锁的原因
- 1.系统资源不足。
- 2.进程运行顺序不合适。
- 3.资源分配不当。
/**
* @author 长名06
* @version 1.0
* 死锁代码
*/
public class DeadLock {
static Object a = new Object();
static Object b = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (a){
System.out.println(Thread.currentThread().getName() + "持有锁a,试图后去锁b");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (b){
System.out.println(Thread.currentThread().getName() + "获取锁b");
}
}
},"a").start();
new Thread(() -> {
synchronized (b){
System.out.println(Thread.currentThread().getName() + "持有锁b,试图后去锁a");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (a){
System.out.println(Thread.currentThread().getName() + "获取锁a");
}
}
},"b").start();
}
}
验证线程一直等待是否是死锁
- 1.jps -l 命令类似linux下的ps - ef 可以查询java的线程
正在执行的死锁代码的进程id - 2.jstack工具(JVM 自带的堆栈工具) jstack 线程id 可以查询对应线程的信息
执行jstack pid指令
提示语句中会有 Found 1 deadlock
只是为了记录自己的学习历程,且本人水平有限,不对之处,请指正。