💁 个人主页:黄小黄的博客主页
❤️ 支持我:👍 点赞 🌷 收藏 🤘关注
🎏 格言:All miracles start from sometime somewhere, make it right now.
本文来自专栏:JavaSE从入门到精通
在前面的多线程篇的学习中,我们可以尝试对售票活动进行模拟,将每个售票窗口看成一个进程。但是,前面由于没有学习过线程的同步,有可能会出现超卖的问题。比如只剩最后一张票,但是,两个窗口此时都在同时卖,就会由于数据更新不及时,导致多卖出票。
🐱何为线程同步机制?
1️⃣ 同步代码块:
synchronized(对象){//得到对象的锁,才能操作同步代码
//需要被同步的代码
}
2️⃣ 同步方法:
public synchronized void method(参数列表){
//需要被同步的代码
}
在下面的案例中,我们模拟售票问题,通过synchronized解决超卖问题。
package syn;
/**
* @author 兴趣使然黄小黄
* @version 1.0
* 售票问题
*/
public class SellTicket {
public static void main(String[] args) {
SellTicketToCustomer sellTicketToCustomer = new SellTicketToCustomer();
//模拟三个售票窗口
new Thread(sellTicketToCustomer).start();
new Thread(sellTicketToCustomer).start();
new Thread(sellTicketToCustomer).start();
}
}
class SellTicketToCustomer implements Runnable{
/**
* 票数
*/
private int ticketNum = 100;
/**
* 控制run中的while,用于以通知的形式终止线程
*/
private boolean loop = true;
@Override
public void run() {
while (loop){
sellTicket();
}
}
/**
* 售票的方法
*/
public synchronized void sellTicket(){ //同步方法, 在统一时刻, 只能有一个线程来执行sellTicket方法
if (ticketNum <= 0){
System.out.println("售票结束...");
loop = false;
return;
}
try {
Thread.sleep(50);
}catch (InterruptedException e){
e.getStackTrace();
}
System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票, "
+ "剩余票数 = " + (--ticketNum));
}
}
🐰 说明:
在上述例子中,sellTicket方法被设置为同步方法,在同一时刻,只允许一个线程进入,解决了超卖的情况。运行结果如下:
synchronized
用来与对象的互斥锁联系。当某个对象用synchronized
修饰时,表示该对象在任一时刻只能由一个线程访问;售票问题我们既可以使用同步方法,也可以使用对象锁,给代码块上锁。在之前的案例中,我们演示的就是使用了同步方法:
这里,我们尝试 使用对象锁解决, 解决代码如下:
其实也不一定必须使用this,只要对象为同一个就行,比如下面的代码中,在该线程类中,初始化了一个对象,加锁代码块用的也是同一对象,因此,结果一致:
/**
* @author 兴趣使然黄小黄
* @version 1.0
* 售票问题
*/
public class SellTicket {
public static void main(String[] args) {
SellTicketToCustomer sellTicketToCustomer = new SellTicketToCustomer();
//模拟三个售票窗口
new Thread(sellTicketToCustomer).start();
new Thread(sellTicketToCustomer).start();
new Thread(sellTicketToCustomer).start();
}
}
class SellTicketToCustomer implements Runnable{
/**
* 票数
*/
private int ticketNum = 100;
/**
* 控制run中的while,用于以通知的形式终止线程
*/
private boolean loop = true;
/**
* 用于加锁
*/
Object obj = new Object();
@Override
public void run() {
while (loop){
sellTicket();
}
}
/**
* 售票的方法
*/
public void sellTicket(){
synchronized (obj) {
if (ticketNum <= 0) {
System.out.println("售票结束...");
loop = false;
return;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.getStackTrace();
}
System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票, "
+ "剩余票数 = " + (--ticketNum));
}
}
}
互斥锁小结一下:
多个线程都占用了对方的锁资源,但是不肯相让,就导致了死锁。 在实际编程开发中,一定要避免死锁的发生。
🐦 举个例子:
话说,在某一药店门口… …
👮 你好,请您佩戴口罩!
👦我没有口罩!
👮 你好,没有口罩是不能入内的哦~
👦我就是没有口罩才来买口罩啊!
👮 没有佩戴口罩是不能进入的呢…
👦… …
👮 … …
在上述例子中,我们可以简单的把口罩看作锁,把保安放人与客户通过分别看成一个线程。此时,保安和客户都占用口罩这一锁资源,互不相让,就导致了死锁的情况。
/**
* @author 兴趣使然黄小黄
* @version 1.0
* 模拟线程死锁
*/
public class DeadLockTest {
public static void main(String[] args) {
DeadDemo deadDemo1 = new DeadDemo(true);
DeadDemo deadDemo2 = new DeadDemo(false);
Thread thread1 = new Thread(deadDemo1);
Thread thread2 = new Thread(deadDemo2);
thread1.start();
thread2.start();
}
}
/**
* 线程
*/
class DeadDemo implements Runnable{
static Object o1 = new Object();
static Object o2 = new Object();
boolean flag;
public DeadDemo(boolean flag){
this.flag = flag;
}
@Override
public void run() {
if (flag){
synchronized (o1){
System.out.println(Thread.currentThread().getName() + "进入1");
synchronized (o2){
System.out.println(Thread.currentThread().getName() + "进入2");
}
}
}else {
synchronized (o2){
System.out.println(Thread.currentThread().getName() + "进入3");
synchronized (o1){
System.out.println(Thread.currentThread().getName() + "进入4");
}
}
}
}
}
🐰 说明:
thread0与thread1两个线程运行时,thread0首先拿到o1锁,而后请求o2锁,这时,thread1已经拿到了o2锁,请求o1锁,由于各自请求的锁都被其他线程占用,就导致了死锁的情况, 运行结果就像卡住了一样,不再动弹。
🌟以上便是本文的全部内容啦,后续内容将会持续免费更新,如果文章对你有所帮助,麻烦动动小手点个赞 + 关注,非常感谢 ❤️ ❤️ ❤️ !
如果有问题,欢迎私信或者评论区!
共勉:“你间歇性的努力和蒙混过日子,都是对之前努力的清零。”
以上图片出自一位重要的朋友,嘿嘿,祝愿一起学习的码友们:“前程似锦,万事胜意!”