1、实体类
1 package com.cn.donleo.thread.phone;
2
3 import java.util.concurrent.locks.Lock;
4 import java.util.concurrent.locks.ReentrantLock;
5
6 /**
7 * @author liangd
8 * date 2020-11-02 08:53
9 * code
10 */
11 public class MyPhone implements Runnable {
12
13 private int phoneNum = 100;
14 /*
15 * 同步锁的两种方式:
16 * 1、锁对象 synchronized (this)
17 * 2、锁方法 public synchronized void run() 锁方法其实是锁的当前对象,synchronized (this) ,synchronized (MyPhone.class)
18 * 同步锁优缺点:
19 * 1、解决了多线程安全问题
20 * 2、增加了线程同步锁后,必须一个一个执行,浪费资源,降低了程序的运行效率
21 */
22 /**
23 * 1、对不同的runnable传入同一个lock
24 * 2、lock和synchronized类似,只不过这个是手动上锁,手动释放锁
25 * synchronized属于自动上锁和释放
26 * 3、Lock和synchronized的区别
27 * 1)synchronized试用于并发量小的,lock更适用于并发量大的
28 * 2)lock更灵活,可以自由定义多把锁的枷锁解锁顺序
29 * 3)Lock还有中断锁和定时锁。
30 * 4)当线程运行到synchronized同步方法中,就会拥有obj对象的对象锁
31 * 5)Wait会释放对象锁,sleep不会释放对象锁
32 * 6)Synchronized用对象锁:
33 */
34 private Lock lock = new ReentrantLock();
35
36 @Override
37 public synchronized void run() {
38 //while(true)是一个无穷循环语句 我们必须在他的循环语句内部加入一个判断 当他达到了什么要求就会跳出
39 while (true) {
40 //锁对象,指当前对象
41 // synchronized (this){
42 // synchronized ("Myphone"){
43 if (phoneNum > 0) {
44 lock.lock();
45 try {
46 /*
47 1、卖手机的时候不可能都是同时卖出或者被预定在MyPhone类里面增加sleep
48 休眠100毫秒
49 2、淘宝最后一部手机进来
50 京东也有人预定手机,现在没有--,也可以进来
51 同理,拼多多也有人预定手机,现在没有--,也可以进来
52 3、会出现负数
53 */
54 Thread.sleep(100);
55 } catch (InterruptedException e) {
56 e.printStackTrace();
57 } finally {
58 //lock.unlock()要放在finally里面,因为如果上面报异常的情况下锁就不会手动释放,放在finally里面始终要执行
59 lock.unlock();
60 }
61 System.out.println(Thread.currentThread().getName() + "卖出了一部手机,还剩余" + --phoneNum + "部手机");
62 } else {
63 System.out.println("手机已经全部卖完,请下次再来");
64 return;
65 }
66 // }
67
68 }
69 }
70 }
2、测试同步锁
1 package com.cn.donleo.thread.phone;
2
3 /**
4 * @author liangd
5 * date 2020-11-02 08:59
6 * code 线程同步测试
7 */
8 public class TestMyPhone {
9 /**
10 * 手机属于预定模式,分别由京东,淘宝,拼多多三个商家在卖
11 * 现在有华为P40PRO手机100部,怎么来实现
12 * 思考:
13 * 1.有一百部手机
14 * 2.京东,淘宝,拼多多是同时卖手机,属于三个线程
15 * 3.有一个仓库进行发货,这个可以理解为main方法
16 * @param args
17 */
18 public static void main(String[] args){
19 MyPhone myPhone = new MyPhone();
20 Thread tb = new Thread(myPhone,"淘宝");
21 Thread jd = new Thread(myPhone,"京东");
22 Thread pdd = new Thread(myPhone,"拼多多");
23 tb.start();
24 jd.start();
25 pdd.start();
26
27 }
28 }
3、测试死锁
1 package com.cn.donleo.thread.phone;
2
3 /**
4 * @author liangd
5 * date 2020-11-02 09:42
6 * code 死锁测试
7 */
8 public class TestSynchronized {
9 /**
10 * 1、死锁4个必要条件
11 * (1) 互斥条件:一个资源每次只能被一个进程使用。
12 * (2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
13 * (3) 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺。
14 * (4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
15 * 2、如何避免死锁
16 * (1)加锁顺序(线程按照一定的顺序加锁)
17 * (2)加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
18 * (3)死锁检测
19 * 最简单的方式:不要写嵌套锁就好
20 */
21 public static void main(String[] args) {
22 /*
23 * 多次测试,出现死锁,程序一直在运行,A和B都没有吃到饭
24 */
25 //匿名内部类,直接new接口必须实现接口中的抽象方法
26 new Thread(new Runnable() {
27 @Override
28 public void run() {
29 synchronized ("A") {
30 System.out.println(Thread.currentThread().getName() + "拿到A筷子了");
31 //嵌套锁
32 synchronized ("B") {
33 System.out.println(Thread.currentThread().getName() + "拿到A筷子了,同时拿到B筷子了,可以吃饭了");
34 }
35 }
36 }
37 }, "男朋友").start();
38 //再开一个线程
39 new Thread(new Runnable() {
40 @Override
41 public void run() {
42 synchronized ("B") {
43 System.out.println(Thread.currentThread().getName() + "拿到B筷子了");
44 //嵌套锁
45 synchronized ("A") {
46 System.out.println(Thread.currentThread().getName() + "拿到B筷子了,同时拿到A筷子了,可以吃饭了");
47 }
48 }
49 }
50 }, "女朋友").start();
51 }
52 }