–同步的概念:确保代码执行的有序性、事务执行的完整性,数据共享的可靠性和正确性;在多线程并发的情况下,使得在同一个时间点只能有一个线程访问同步方法或同步代码块
–同步方法:使用this作为当前同步方法的同步锁,只有获得此对象锁的线程才能执行同步方法中的操作
this是调用当前方法的对象的引用
–同步代码块:可以灵活定义同步修饰的代码区域,自定义同步锁,也能实现同步代码块的嵌套
注意:会出现同一张火车票给多个线程获取
现实生活中这种情况是不允许出现
京东抢到了序号为;100的车型
飞猪抢到了序号为;100的车型
智行抢到了序号为;100的车型
智行抢到了序号为;97的车型
智行抢到了序号为;96的车型
飞猪抢到了序号为;98的车型
/**
* @author Lantzrung
* @date 2022年8月5日
* @Description
*/
package com.day0805sileo01;
public class TicketTask2 implements Runnable {
// 测试操作
public static void main(String[] args) {
//
TicketTask2 task = new TicketTask2();
// 1、创建线程的对象
Thread jingdong = new Thread(task, "京东");
Thread zhixing = new Thread(task, "智行");
Thread feizhu = new Thread(task, "飞猪");
// 2、启动线程
jingdong.start();
zhixing.start();
feizhu.start();
}
// 定义票数 【资源共享】
public int nums = 100;
// 实现抢票操作
@Override
public void run() {
// 抢票--循环操作
while (nums > 0) {
// 获取线程名称
String name = Thread.currentThread().getName();
System.out.println(name + "抢到了序号为;" + nums + "的车型");
// 票数--
nums--;
}
}
}
调用了sleep方法后,线程会进入阻塞状态,释放了cpu执行权,但是没有释放对象锁
/**
* @author Lantzrung
* @date 2022年8月5日
* @Description
*/
package com.day0805sileo01;
public class TicketTask implements Runnable {
// 测试操作
public static void main(String[] args) {
// 只有一个任务对象 (task的对象锁)
TicketTask task = new TicketTask();
// 1、创建线程的对象
Thread jingdong = new Thread(task, "京东");
Thread zhixing = new Thread(task, "智行");
Thread feizhu = new Thread(task, "飞猪");
// 2、启动线程
jingdong.start();
zhixing.start();
feizhu.start();
// 京东抢到了序号为:100的车票
// 飞猪抢到了序号为:99的车票
// 智行抢到了序号为:98的车票
// 京东抢到了序号为:97的车票
// 飞猪抢到了序号为:96的车票
// 智行抢到了序号为:95的车票
// 飞猪抢到了序号为:94的车票
// 京东抢到了序号为:93的车票
// 智行抢到了序号为:92的车票
// 飞猪抢到了序号为:91的车票
// 京东抢到了序号为:90的车票
// 智行抢到了序号为:89的车票
// 京东抢到了序号为:88的车票
// 飞猪抢到了序号为:87的车票
// 智行抢到了序号为:86的车票
}
// 定义票数 【资源共享】
public int nums = 100;
// 实现抢票操作
@Override
public void run() {
// saleTicket();// 调用方法
// 注意:放在这里只会执行这几个
// 100抢到了序号为:100的车票
// 99抢到了序号为:99的车票
// 98抢到了序号为:98的车票
// 抢票--循环操作
while (nums > 0) {
// 睡眠
try {
saleTicket();// 调用方法
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 定义一个同步方法【限定在同一个时间点,只能有一个线程执行操作】
// 同步锁(对象锁):只有拿到了该对象锁的线程才能执行这个同步操作(this--作为同步方法的对象锁)
// 注意:在同步方法中,循环操作中睡眠不会释放对象锁
// OPEN2
public synchronized void saleTicket() {
if (nums > 0) {
// 获取线程名称
String name = Thread.currentThread().getName();
System.out.println(name + "抢到了序号为:" + nums + "的车票");
// 票数--
nums--;
}
}
// OPEN1: 这个操作是一直循环输出 京东线程 因为使用的是while循环 就算加上睡眠操作还是会一直输出该京东线程的
// public synchronized void saleTicket() {
// while (nums > 0) {
// // 获取线程名称
// String name = Thread.currentThread().getName();
// System.out.println(name + "抢到了序号为;" + nums + "的车型");
// // 票数--
// nums--;
// // 睡眠
// try {
// Thread.sleep(20);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
京东抢到了序号为;25的车型
京东抢到了序号为;24的车型
京东抢到了序号为;23的车型
京东抢到了序号为;22的车型
京东抢到了序号为;21的车型
京东抢到了序号为;20的车型
京东抢到了序号为;19的车型
京东抢到了序号为;18的车型
京东抢到了序号为;17的车型
京东抢到了序号为;16的车型
京东抢到了序号为;15的车型
京东抢到了序号为;14的车型
京东抢到了序号为;13的车型
京东抢到了序号为;12的车型
京东抢到了序号为;11的车型
京东抢到了序号为;10的车型
京东抢到了序号为;9的车型
京东抢到了序号为;8的车型
京东抢到了序号为;7的车型
京东抢到了序号为;6的车型
京东抢到了序号为;5的车型
京东抢到了序号为;4的车型
京东抢到了序号为;3的车型
京东抢到了序号为;2的车型
京东抢到了序号为;1的车型
// }
}
/**
* @author Lantzrung
* @date 2022年8月6日
* @Description
*/
package com.day0806;
public class TicketTask implements Runnable {
public static void main(String[] args) {
// 创建票数对象
TicketTask task = new TicketTask();
// 创建线程对象
Thread jd = new Thread(task, "京东");
Thread zx = new Thread(task, "智行");
Thread fz = new Thread(task, "飞猪");
// 启动线程
jd.start();
zx.start();
fz.start();
task.test();
// test
// 飞猪抢到了序号为:100
// 智行抢到了序号为:99
// 京东抢到了序号为:98
// 京东抢到了序号为:97
// 智行抢到了序号为:96
// 飞猪抢到了序号为:95
}
// 定义票数
public int nums = 100;
// 实现抢票操作
@Override
public void run() {
// 抢票--循环操作
while (nums > 0) {
// 同步代码块【同步监视的区域】 【对象锁是一个对象即可】
// synchronized (this) {
synchronized ("a") {
if (nums > 0) {
// 获取线程名称
String name = Thread.currentThread().getName();
System.out.println(name + "抢到了序号为:" + nums);
// 票数--
nums--;
}
}
// 睡眠
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 同步代码块可以跨方法、跨类进行监听
public void test() {
synchronized ("a") {
System.out.println("test");
}
}
}
/**
* @author Lantzrung
* @date 2022年8月6日
* @Description
*/
package com.day0806;
public class TicketDemo {
// this
public synchronized void demo() {
System.out.println("执行demo操作");
}
// 嵌套上锁 【同步代码块】
public void test1() {
synchronized ("a") {
System.out.println("执行操作a");
synchronized ("b") {
System.out.println("执行操作b");
}
}
}
// 两个类型不一样的,因为一个"a"常量池里面的 一个重新new出来的对象String 两个的情况不一样的
public void drawMoney() {
synchronized ("a") {
}
synchronized (new String("a")) {
}
}
思考:同步锁释放的时机
–1、正常执行完同步代码
–2、遇到return、break结合标签时
–3、抛出异常、出现错误
–4、当线程进行等待时,调用了wait()方法
/**
* @author Lantzrung
* @date 2022年8月6日
* @Description
*/
package com.day0806;
import java.util.Iterator;
import org.omg.PortableServer.ID_ASSIGNMENT_POLICY_ID;
public class TestBlock {
public static void main(String[] args) {
// 创建对象为
TestBlock b = new TestBlock();
// 1、创建线程
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
b.test1();
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
b.test2();
}
});
// 启动线程
t1.start();
t2.start();
}
public synchronized void test1() {
// 进入test1
// 进入test2
System.out.println("进入test1");
// 时机4:当线程进行等待时,调用了wait()方法
// 注意:这里是线程进去等待的状态,而不会释放对象锁
for (int i = 1; i <= 5; i++) {
if (i == 3) {
try {
wait();// 注意:这里是线程进去等待的状态,而不会释放对象锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 时机3:为抛出异常、出现错误
// 直接跳出异常体,不执行里面异常体内的代码块
// 进入test1
// 进入test2
// Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero
// at com.day0806.TestBlock.test1(TestBlock.java:45)
// at com.day0806.TestBlock$1.run(TestBlock.java:21)
// at java.lang.Thread.run(Thread.java:745)
// for (int i = 1; i <= 5; i++) {
// try {
// // 出现异常
// if (i == 3) {
// int item = 3 / 0;
// }
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
// // 时机2:遇到return、break结合标签时
// for (int i = 1; i <= 5; i++) {
// try {
// //
// if (i == 2) {
// break;// break和return都是执行输出一次 test1.......
continue;// continue执行4次 test1.......
// }
// System.out.println("test1.......");
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
// 时机1:正常执行完同步代码
// for (int i = 1; i <= 5; i++) {
// try {
// Thread.sleep(1000);
// System.out.println("test...");
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
// open1:
// 进入test1
// test...
// test...
// test...
// test...
// test...
// 进入test2
}
public synchronized void test2() {
System.out.println("进入test2");
}
}
/**
* @author Lantzrung
* @date 2022年8月6日
* @Description
*/
package com.day0806;
// 手机生产类
public class PhoneProducer {
public static void main(String[] args) {
// 提供生产类的对象
PhoneProducer producer = new PhoneProducer();
// run任务类
Runnable task = new Runnable() {
@Override
public void run() {
// 循环生产手机
while (producer.num > 0) {
producer.produceint();
// 延迟
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
// 创建多个工人【线程】
Thread t1 = new Thread(task, "翠花");
Thread t2 = new Thread(task, "如花");
Thread t3 = new Thread(task, "小平");
// 开始生产
t1.start();
t2.start();
t3.start();
}
// 限定每天100台
int num = 100;
// 提供一个生产方法【this对象要唯一】
public synchronized void produceint() {
if (num >= 1) {
//
String name = Thread.currentThread().getName();
//
System.out.printf("%s生产了No.xs%d的手机!\n", name, num);
//
num--;
}
}
}
–可重入锁特点:
(A) 上锁次数
[1] lock.lock() 可以上锁多次 [可重入]
[2] 你想完全解锁, 必须解够上锁的次数。
解锁次数 == 上锁次数
(B) 上锁与解锁
[1] 你可以在任意位置上锁, 也可以任意位置解锁。
[2] 但是 上锁与解锁的线程必须保证是同一个线程, 否则, 会发生线程处理状态异常。
© 尝试上锁tryLock(),如果有获取到锁则上锁并且操作,没有获取到锁,也不会阻塞线程,也可以设置超时时间
(D) 可以设置公平锁和非公平锁【默认是非公平】
非公平锁:抢夺式,谁抢到就谁执行【可以先直接抢,后排队】
公平锁:每个线程的获取资源的几率是一样的【直接排队】
– 注意事项: [1] 确保lock是同一个对象锁 [2] 防范异常的发生, 发现有如下的问题, 因为发生一个异常,
导致锁无法释放。
应该采取某个策略来防止类似的事情发生。
引入异常处理机制 try{ } catch(){ }finally{ }
将 lock.unlock(); 放入 finally 中。 [3] 为了防止 Lock 引用被修改, 请将 Lock 定为 final 最终变量。
– 使用场景: [1] 需要灵活上锁 [2] 不想在上锁时阻塞,可以定义非阻塞的操作逻辑 [3] 需要设置为公平锁时
注意:要是单是上锁的话,会比平时执行的效率慢一点
注意:加上了unlock就会回到平时执行效率
(A) 上锁次数
[1] lock.lock() 可以上锁多次 [可重入]
[2] 你想完全解锁, 必须解够上锁的次数。
解锁次数 == 上锁次数
/**
* @author Lantzrung
* @date 2022年8月6日
* @Description
*/
package com.day0806;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockDemo {
public static void main(String[] args) {
ReentrantLockDemo reenDemo = new ReentrantLockDemo();
Runnable take = new Runnable() {
@Override
public void run() {
while (reenDemo.num > 0) {
reenDemo.produceing();
// 延迟
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Thread t1 = new Thread(take, "翠花");
Thread t2 = new Thread(take, "如花");
Thread t3 = new Thread(take, "小平");
// 开始生产
t1.start();
t2.start();
t3.start();
}
int num = 100;
// 可重入锁 【确保lock对象是唯一】
Lock lock = new ReentrantLock();
// 提供一个方法生产方法 【可重入锁】
public void produceing() {
// 上锁
lock.lock();// 注意:要是单是上锁的话,会比平时执行的效率慢一点
if (num >= 1) {
//
String name = Thread.currentThread().getName();
//
System.out.printf("%s生产了No.xs%d的手机!\n", name, num);
//
num--;
}
lock.unlock();// 注意:加上了unlock就会回到平时执行效率
}
}
// [同步代码块]
// public void produceing() {
// synchronized ("produceing") {
// if (num >= 1) {
// //
// String name = Thread.currentThread().getName();
// //
// System.out.printf("%s生产了No.xs%d的手机!\n", name, num);
// //
// num--;
// }
// }
// }
(B) 上锁与解锁
[1] 你可以在任意位置上锁, 也可以任意位置解锁。
[2] 但是 上锁与解锁的线程必须保证是同一个线程, 否则, 会发生线程处理状态异常。
open1:
// 提供一个方法生产方法 【可重入锁】
open2 :
// 加上try上后出现多次的异常
// 认为的异常进行操作
/**
* @author Lantzrung
* @create 2022-08-06
* @Description
*/
/**
* @author Lantzrung
* @date 2022年8月6日
* @Description
*/
package com.day0806;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockDemo2 {
public static void main(String[] args) {
ReentrantLockDemo2 reenDemo = new ReentrantLockDemo2();
Runnable take = new Runnable() {
@Override
public void run() {
while (reenDemo.num > 0) {
reenDemo.produceing();
// 延迟
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Thread t1 = new Thread(take, "翠花");
Thread t2 = new Thread(take, "如花");
Thread t3 = new Thread(take, "小平");
// 开始生产
t1.start();
t2.start();
t3.start();
}
// 限定每天100台的操作
int num = 100;
// 可重入锁 【确保lock对象是唯一】【注意:出现异常不会释放锁,所以要在finall去释放可重入锁】
Lock lock = new ReentrantLock();
// open2:
// 加上try上后出现多次的异常
// 认为的异常进行操作
// public void produceing() {
// // 上锁
// lock.lock();
// try {
// if (num >= 1) {
// //
// String name = Thread.currentThread().getName();
// //
// System.out.printf("%s生产了No.xs%d的手机!", name, num);
// //
// num--;
// //
// int item = 3 / 0;
小平生产了No.xs12的手机!java.lang.ArithmeticException: / by zero
at com.day0806.ReentrantLockDemo2.produceing(ReentrantLockDemo2.java:58)
at com.day0806.ReentrantLockDemo2$1.run(ReentrantLockDemo2.java:20)
at java.lang.Thread.run(Thread.java:745)
java.lang.ArithmeticException: / by zero
如花生产了No.xs11的手机! at com.day0806.ReentrantLockDemo2.produceing(ReentrantLockDemo2.java:58)
at com.day0806.ReentrantLockDemo2$1.run(ReentrantLockDemo2.java:20)
at java.lang.Thread.run(Thread.java:745)
翠花生产了No.xs10的手机!java.lang.ArithmeticException: / by zero
at com.day0806.ReentrantLockDemo2.produceing(ReentrantLockDemo2.java:58)
at com.day0806.ReentrantLockDemo2$1.run(ReentrantLockDemo2.java:20)
at java.lang.Thread.run(Thread.java:745)
小平生产了No.xs9的手机!java.lang.ArithmeticException: / by zero
at com.day0806.ReentrantLockDemo2.produceing(ReentrantLockDemo2.java:58)
at com.day0806.ReentrantLockDemo2$1.run(ReentrantLockDemo2.java:20)
at java.lang.Thread.run(Thread.java:745)
如花生产了No.xs8的手机!java.lang.ArithmeticException: / by zero
at com.day0806.ReentrantLockDemo2.produceing(ReentrantLockDemo2.java:58)
at com.day0806.ReentrantLockDemo2$1.run(ReentrantLockDemo2.java:20)
at java.lang.Thread.run(Thread.java:745)
小平生产了No.xs7的手机!java.lang.ArithmeticException: / by zero
at com.day0806.ReentrantLockDemo2.produceing(ReentrantLockDemo2.java:58)
at com.day0806.ReentrantLockDemo2$1.run(ReentrantLockDemo2.java:20)
at java.lang.Thread.run(Thread.java:745)
翠花生产了No.xs6的手机!java.lang.ArithmeticException: / by zero
at com.day0806.ReentrantLockDemo2.produceing(ReentrantLockDemo2.java:58)
at com.day0806.ReentrantLockDemo2$1.run(ReentrantLockDemo2.java:20)
at java.lang.Thread.run(Thread.java:745)
如花生产了No.xs5的手机!java.lang.ArithmeticException: / by zero
at com.day0806.ReentrantLockDemo2.produceing(ReentrantLockDemo2.java:58)
at com.day0806.ReentrantLockDemo2$1.run(ReentrantLockDemo2.java:20)
at java.lang.Thread.run(Thread.java:745)
翠花生产了No.xs4的手机!java.lang.ArithmeticException: / by zero
at com.day0806.ReentrantLockDemo2.produceing(ReentrantLockDemo2.java:58)
at com.day0806.ReentrantLockDemo2$1.run(ReentrantLockDemo2.java:20)
at java.lang.Thread.run(Thread.java:745)
小平生产了No.xs3的手机!java.lang.ArithmeticException: / by zero
at com.day0806.ReentrantLockDemo2.produceing(ReentrantLockDemo2.java:58)
at com.day0806.ReentrantLockDemo2$1.run(ReentrantLockDemo2.java:20)
at java.lang.Thread.run(Thread.java:745)
如花生产了No.xs2的手机!java.lang.ArithmeticException: / by zero
at com.day0806.ReentrantLockDemo2.produceing(ReentrantLockDemo2.java:58)
at com.day0806.ReentrantLockDemo2$1.run(ReentrantLockDemo2.java:20)
at java.lang.Thread.run(Thread.java:745)
翠花生产了No.xs1的手机!java.lang.ArithmeticException: / by zero
at com.day0806.ReentrantLockDemo2.produceing(ReentrantLockDemo2.java:58)
at com.day0806.ReentrantLockDemo2$1.run(ReentrantLockDemo2.java:20)
at java.lang.Thread.run(Thread.java:745)
//
// }
// } catch (Exception e) {
// e.printStackTrace();
// } finally {
// // 解锁
// lock.unlock();
// }
// }
// open1:
// // 提供一个方法生产方法 【可重入锁】
// public void produceing() {
// // 上锁
// lock.lock();
// if (num >= 1) {
// //
// String name = Thread.currentThread().getName();
// //
// System.out.printf("%s生产了No.xs%d的手机!\n", name, num);
// //
// num--;
// // open1;人为的异常操作
// // 此操作出现异常后不会释放锁进行后续的操作。
int item = 3 / 0;
// // 翠花生产了No.xs100的手机!
Exception in thread "翠花" java.lang.ArithmeticException: / by zero
at com.day0806.ReentrantLockDemo2.produceing(ReentrantLockDemo2.java:58)
at com.day0806.ReentrantLockDemo2$1.run(ReentrantLockDemo2.java:20)
at java.lang.Thread.run(Thread.java:745)
// }
// // 解锁
// lock.unlock();
// }
}
/**
* @author Lantzrung
* @date 2022年8月6日
* @Description
*/
package com.day0805sil02;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TestBlock02 {
public static void main(String[] args) {
// 创建对象
TestBlock02 b = new TestBlock02();
// 1、创建线程
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
b.test1();
}
});
t1.start();
// 先确保启动线程一 再启动线程二
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
b.test2();
}
});
t2.start();
}
// 创建上锁对象
Lock lock = new ReentrantLock();
// 第一个方法
public void test1() {
// 第一次上锁
lock.lock();
System.out.println("进入test1方法...");
// 第二次上锁
lock.lock();
// 延迟
for (int i = 0; i <= 5; i++) {
try {
// 输出
System.out.println("test1...");
Thread.sleep(200);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
// 解第一把锁
lock.unlock();
// 解第二把锁
lock.unlock();
open1
// 正常的输出操作是输出这个的
// 进入test1方法...
// test1...
// test1...
// test1...
// test1...
// test1...
// test1...
// 进入test2
open2
// 要是不解除就不会执行test2了
// 进入test1方法...
// test1...
// test1...
// test1...
// test1...
// test1...
// test1...
}
public void test2() {
// 上锁
lock.lock();
System.out.println("进入test2");
// 解锁
lock.unlock();
}
}
尝试上锁tryLock(),如果有获取到锁则上锁并且操作,没有获取到锁,也不会阻塞线程,也可以设置超时时间
/**
* @author Lantzrung
* @date 2022年8月6日
* @Description
*/
package com.day0805sil02;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TestBlock3 {
public static void main(String[] args) {
// 创建对象
TestBlock3 b = new TestBlock3();
// 1、创建线程
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
b.test1();
}
});
t1.start();
// 先确保启动线程一 再启动线程二
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
b.test2();
}
});
t2.start();
}
// 创建上锁对象
Lock lock = new ReentrantLock();
// 第一个方法
public void test1() {
// 第一次上锁
lock.lock();
System.out.println("进入test1方法...");
// 第二次上锁
lock.lock();
// 延迟
for (int i = 0; i <= 5; i++) {
try {
// 输出
System.out.println("test1...");
Thread.sleep(200);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
// 解第一把锁
lock.unlock();
// 解第二把锁
lock.unlock();
}
public void test2() {
// 尝试获取锁 【非阻塞地获取锁】
// open1
// 进入test1方法...
// test1...
// 进入test2
// Exception in thread "Thread-1" java.lang.IllegalMonitorStateException
// at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:151)
// at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1261)
// at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:457)
// at com.day0805sil02.TestBlock3.test2(TestBlock3.java:70)
// at com.day0805sil02.TestBlock3$2.run(TestBlock3.java:33)
// at java.lang.Thread.run(Thread.java:745)
// test1...
// test1...
// test1...
// test1...
// test1...
// lock.tryLock();
// System.out.println("进入test2");
// // 解锁
// lock.unlock();
// open2
// 进入test1方法...
// test1...
// 进入test2,先做其他事情
// test1...
// test1...
// test1...
// test1...
// test1...
// boolean isLocked = lock.tryLock();
// if (isLocked) {
// System.out.println("进入test2");
// // 解锁
// lock.unlock();
// } else {
// System.out.println("进入test2,先做其他事情");
// }
// 添加超时时间
boolean isLocked;
try {
isLocked = lock.tryLock(1, TimeUnit.SECONDS);
if (isLocked) {
System.out.println("进入test2");
// 解锁
lock.unlock();
} else {
System.out.println("进入test2,先做其他事情");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
可以设置公平锁和非公平锁【默认是非公平】非公平锁:
抢夺式,谁抢到就谁执行【可以先直接抢,后排队】
公平锁:每个线程的获取资源的几率是一样的【直接排队】
/**
* @author Lantzrung
* @date 2022年8月6日
* @Description
*/
package com.day0806;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
// 手机生产类
public class PhoneProducer {
public static void main(String[] args) {
// 提供生产类的对象
PhoneProducer producer = new PhoneProducer();
// run任务类
Runnable task = new Runnable() {
@Override
public void run() {
// 循环生产手机
while (producer.num > 0) {
producer.produceint();
// 延迟
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
// 创建多个工人【线程】
Thread t1 = new Thread(task, "翠花");
Thread t2 = new Thread(task, "如花");
Thread t3 = new Thread(task, "小平");
// 开始生产
t1.start();
t2.start();
t3.start();
}
int num = 15;
final Lock lock = new ReentrantLock(true);
// 公平锁的源码 默认的就是非公平的
// public ReentrantLock() {
// sync = new NonfairSync();
// }
// 然后下面还有一个源码 true设置为公平锁 false设置不公平锁
// public ReentrantLock(boolean fair) {
// sync = fair ? new FairSync() : new NonfairSync();
// }
// 可重入锁
public void produceint() {
// 上锁
lock.lock();
if (num >= 1) {
//
String name = Thread.currentThread().getName();
//
System.out.printf("%s生产了No.xs%d的手机!\n", name, num);
//
num--;
}
lock.unlock();
}
}
※–什么是死锁 ?(笔试题)
两个或多个线程, 相互之间互持对方想获取的资源, 在没释放自身资源时之前, 又去试图获取其它线程持有的资源,而造成多个线程同时阻塞,
无法解除。
比如: A 线程持有 “1” 这个资源, 在没有释放 “1” 时, 又试图获取 “2”。 B 线程持有 “2” 这个资源, 在没有释放
“2” 时, 又试图获取 “1”。
这样就形成死锁。
为了尽量避免死锁的发生, 在持有一个资源的同时, 少点去获取其它资源。
/**
* @author Lantzrung
* @date 2022年8月7日
* @Description
*/
package com.day0806;
public class PersonTask implements Runnable {
public static void main(String[] args) {
PersonTask task = new PersonTask();
// 创建线程对象
Thread A = new Thread(task, "Aperson");
Thread B = new Thread(task, "Bperson");
// 启动线程
A.start();
B.start();
// [Aperson]尝试进入[A]密室.........
// [Aperson]尝试进入[A]密室!!!
// [Aperson]尝试进入[B]密室.........
// [Aperson]尝试进入[B]密室!!!
// [Bperson]尝试进入[B]密室.........
// [Bperson]尝试进入[B]密室!!!
// [Bperson]尝试进入[A]密室.........
// [Bperson]尝试进入[A]密室!!!
}
@Override
public void run() {
// 1、判断是Aperson、Bperson
String name = Thread.currentThread().getName();
// 2、判断
if (name.equals("Aperson")) { // Aperson
// A走的路径
runWay(new String[] { "A", "B" });
} else { // Bperson
// B走的路径
runWay(new String[] { "B", "A" });
}
}
// 路线
public void runWay(String[] keys) { // 入参锁对象 、密室{A,B} {B,A}
// 1、获取名称
String name = Thread.currentThread().getName();
System.out.printf("[%s]尝试进入[%s]密室.........\n", name, keys[0]);
// 先进入第一个密室
synchronized (keys[0]) {
System.out.printf("[%s]尝试进入[%s]密室!!!\n", name, keys[0]);
// 尝试进入第二个密室
System.out.printf("[%s]尝试进入[%s]密室.........\n", name, keys[1]);
// 进入第二个密室
synchronized (keys[1]) {
// 尝试进入B密室
System.out.printf("[%s]尝试进入[%s]密室!!!\n", name, keys[1]);
}
}
}
}
–API: wait();导致当前线程等待; notify();唤醒在此监视器对象的单个线程 notifyAll();唤醒在此监视器对象的所有线程
–使用wait和notify时需要同步锁修饰,同步修饰的对象锁要一致
–//强调:调用wait和notify方法的对象是当前的对象锁【对象锁和调用方法的对象是一样的】;明确调用wait和notify的对象一致,能够确保通信操作是对应的线程
/**
* @author Lantzrung
* @date 2022年8月7日
* @Description
*/
package com.day0806;
public class PhoneBuffer {
// 手机经销商
public static void main(String[] args) {
// 1、创建生产者和消费者
PhoneBuffer buffer = new PhoneBuffer();
// 1、生产者
ProducerThread pro = new ProducerThread();
pro.setBuffer(buffer);
pro.setName("生产者");
// 2、消费者
ConsumerThread con = new ConsumerThread();
con.setBuffer(buffer);
con.setName("消费者");
// 3、启动
pro.start();
con.start();
// OPEN1:
// 不在doNotify和dowait加上同步代码块是会出现报错的
// Exception in thread "消费者" Exception in thread "Thread-1" java.lang.IllegalMonitorStateException
// at java.lang.Object.wait(Native Method)
// at java.lang.Object.wait(Object.java:502)
// at com.day0806.PhoneBuffer.dowait(PhoneBuffer.java:45)
// at com.day0806.ProducerThread.run(PhoneBuffer.java:79)
//java.lang.NullPointerException
// at com.day0806.ConsumerThread.run(PhoneBuffer.java:103)
}
// 缓存的手机
int num = 0;
// 定义一个标志位,判断是否为空
// boolean isEmpty = false;// 默认为空
boolean isEmpty = true;// 默认为空
// OPEN2
// 唤醒同一监视器下(同步锁)的线程
// 通知方法 [同步修饰] 通知在该对象锁监视下的等待的线程根据同步锁唤醒的唤醒 等待线程【this】
public synchronized void doNotify() {
this.notify();
}
// 等待方法 [同步修饰] [对象锁]
public synchronized void dowait() {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// OPEN2
// // 通知方法
// public void doNotify() {
// this.notify();
// }
//
// // 等待方法
// public void dowait() {
// try {
// this.wait();
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
}
// 生产者线程
class ProducerThread extends Thread {
// 提供缓冲区的引用
PhoneBuffer buffer;
// 提供getset方法
public PhoneBuffer getBuffer() {
return buffer;
}
public void setBuffer(PhoneBuffer buffer) {
this.buffer = buffer;
}
@Override
public void run() {
while (true) {
// 1、判断缓冲区是否为空
if (buffer.isEmpty) {
// 空--则生产
buffer.num = (int) (Math.random() * 100 + 1);
System.out.println(this.getName() + "生产了:" + buffer.num + "台手机");
// 生产完,标志位【不为空】
buffer.isEmpty = false;
// 通知消费者进行消费
buffer.doNotify();
} else {
// 不空,则等待
buffer.dowait();
}
}
}
}
// 消费者线程
class ConsumerThread extends Thread {
// 提供缓冲区的引用
PhoneBuffer buffer;
// 提供getset方法
public PhoneBuffer getBuffer() {
return buffer;
}
public void setBuffer(PhoneBuffer buffer) {
this.buffer = buffer;
}
@Override
public void run() {
while (true) {
// 1、判断缓冲区是否为空
if (buffer.isEmpty) {
// --空 则等待 【监视器--同步锁】
buffer.dowait();
} else {
// 不空,则消费
System.out.println(this.getName() + "消费了:" + buffer.num + "台手机");
// 生产完,标志位【不为空】
buffer.isEmpty = true;
// 通知消费者进行消费
buffer.doNotify();
}
}
}
}
要确保对象锁是一致的,否则不能唤醒