JUC(四)多线程锁

2023-06-14,

目录
多线程
Synchronized锁的八种情况
公平锁和非公平锁
可重入锁
synchronized
Lock
死锁
检查死锁

多线程锁

Synchronized锁的八种情况

以一个手机类为例,包含两个synchronized方法和一个普通方法。

public class Phone {
public synchronized void sendSMS() throws InterruptedException {
System.out.println("-----send SMS-----");
} public synchronized void sendEmail() {
System.out.println("-----send email-----");
} public void getHello() {
System.out.println("-----get hello-----");
}
}

1 标准访问

    public static void main(String[] args) {
Phone phone = new Phone();
new Thread(() -> {
phone.sendSMS();
}).start(); new Thread(() -> {
phone.sendEmail();
}).start();
}
-----send SMS-----
-----send email-----

2 短信方法设置4s停止

public class Phone {
public synchronized void sendSMS() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("-----send SMS-----");
} public synchronized void sendEmail() {
System.out.println("-----send email-----");
} public void getHello() {
System.out.println("-----get hello-----");
}
}

-----send SMS-----

-----send email-----

会先等待四秒,短信发送结束才会发送邮件

3 短信停止四秒,同时运行getHello

public class Lock_8 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(() -> {
phone.sendSMS();
}).start(); new Thread(() -> {
phone.getHello();
}).start();
}
}

-----get hello-----

4s。。。

-----send SMS-----

4 两部手机,一部邮件另一部短信

public static void main(String[] args) {
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(() -> {
phone.sendSMS();
}).start(); new Thread(() -> {
phone2.sendEmail();
}).start();
}

-----send email-----

4s...

-----send SMS-----

5 两个静态方法,一部手机,先打印邮件还是短信(短信停4s,然后邮件)
6 两个静态方法,两部手机,先打印邮件还是短信(短信停4s,然后邮件)
7 一个静态短信方法,一个邮件方法,1部手机(先邮件,停4s短信)
8 一个静态短信方法,一个邮件方法,2部手机(先邮件,停4s短信)

总结

synchronized加在普通方法上,是对this进行加锁,作用范围为当前实例对象
synchronized加在静态方法上,是对类的类加载的Class对象加锁,因此作用范围是全部的实例对象

公平锁和非公平锁

当锁被一个线程释放的时候,其他线程抢占锁的机会是否是公平的,分为公平锁和非公平锁。如之前卖票的例子,导致ABC卖票全被A卖了:

公平锁可以防止线程饥饿,但是效率低

A sale 1 ticket, there are 29 tickets left
A sale 1 ticket, there are 28 tickets left
A sale 1 ticket, there are 27 tickets left
A sale 1 ticket, there are 26 tickets left
A sale 1 ticket, there are 25 tickets left
A sale 1 ticket, there are 24 tickets left
A sale 1 ticket, there are 23 tickets left
A sale 1 ticket, there are 22 tickets left
... Process finished with exit code 0

通过ReetranLock构造器设置公平锁

lock = new ReentrantLock(true);
A sale 1 ticket, there are 29 tickets left
B sale 1 ticket, there are 28 tickets left
C sale 1 ticket, there are 27 tickets left
A sale 1 ticket, there are 26 tickets left
B sale 1 ticket, there are 25 tickets left
C sale 1 ticket, there are 24 tickets left
A sale 1 ticket, there are 23 tickets left
B sale 1 ticket, there are 22 tickets left
C sale 1 ticket, there are 21 tickets left

可重入锁

可重入锁:可以重复使用的锁,即某个线程已经获得了某个锁,再次获取锁而不会出现死锁,可重入锁有:

synchronized
ReentrantLock:ReentrantLock 和 synchronized 不一样,需要手动释放锁,所以使用 ReentrantLock的时候一定要手动释放锁,并且加锁次数和释放次数要一样

synchronized
public static void main(String[] args) {
var obj = new Object();
new Thread(() -> {
synchronized (obj) {
System.out.println("外层");
synchronized (obj) {
System.out.println("中层");
synchronized (obj) {
System.out.println("内层");
}
}
}
}).start();
}

可重入锁又称为递归锁,能够实现同步方法的递归调用而不产生死锁

    static synchronized void add() {
add();
}
public static void main(String[] args) {
new Thread(ThreadDemo4::add).start();
}
Lock
class ShareSource {
private final Lock lock = new ReentrantLock();
public void lockTest() {
lock.lock();
System.out.println("waiceng");
lock.lock();
System.out.println("zhongceng");
lock.lock();
System.out.println("neiceng");
lock.unlock();
lock.unlock();
}
} public class ThreadDemo4 { public static void main(String[] args) {
ShareSource shareSource = new ShareSource();
new Thread(shareSource::lockTest).start();
new Thread(shareSource::lockTest).start();
}
}

Lock不会自动释放锁,上锁和解锁不对应的话会导致死锁的发生,如这里的第二个线程就一直执行不了

死锁

def:两个或两个以上的线程在执行过程中,由于竞争资源而造成的相互等待的现象,如果没有外力作用,则会一直等待下去。

死锁产生的原因

    竞争资源
    进程间推进顺序非法
    资源分配不当
public static void main(String[] args) {
var a = new Object();
var b = new Object(); new Thread(() -> {
synchronized (a) {
System.out.println("已获取锁A,尝试获取锁B");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (b) {
System.out.println("已获取锁B");
}
}
}).start(); new Thread(() -> {
synchronized (b) {
System.out.println("已获取锁B,尝试获取锁A");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (a) {
System.out.println("已获取锁A");
}
}
}).start();
}
检查死锁
    jsp:查看系统中运行的java进程
    jstack:追踪java进程的堆栈情况
"Thread-0":
at com.hikaru.juc.lock.DeadLockDemo.lambda$main$0(DeadLockDemo.java:19)
- waiting to lock <0x0000000711818d28> (a java.lang.Object)
- locked <0x0000000711818d18> (a java.lang.Object)
at com.hikaru.juc.lock.DeadLockDemo$$Lambda$14/0x00000008010031f0.run(Unknown Source)
at java.lang.Thread.run(java.base@19.0.2/Thread.java:1589)
"Thread-1":
at com.hikaru.juc.lock.DeadLockDemo.lambda$main$1(DeadLockDemo.java:33)
- waiting to lock <0x0000000711818d18> (a java.lang.Object)
- locked <0x0000000711818d28> (a java.lang.Object)
at com.hikaru.juc.lock.DeadLockDemo$$Lambda$15/0x0000000801003400.run(Unknown Source)
at java.lang.Thread.run(java.base@19.0.2/Thread.java:1589) Found 1 deadlock.

JUC(四)多线程锁的相关教程结束。

《JUC(四)多线程锁.doc》

下载本文的Word格式文档,以方便收藏与打印。