多线程学习之解决线程同步的实现方法

news/2024/7/5 5:40:41

 一、卖票的多线程实现

需求:共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票

代码实现:

/**
 * @Author:kkoneone11
 * @name:SellTicket1
 * @Date:2023/8/26 11:32
 */
public class SellTicket1 implements Runnable{

    private int tickets = 100;

    @Override
    public void run() {
        while(true){
            if(tickets < 0){
                break;
            }else {
                try{
                    Thread.sleep(100);
                }catch (Exception e){
                    e.printStackTrace();
                }
                tickets--;
                System.out.println(Thread.currentThread().getName() + "票数还剩余" + tickets);
            }
        }

    }
}



public class SellTicketDemo {
    public static void main(String[] args) {
        SellTicket1 st = new SellTicket1();

        Thread thread1 = new Thread(st, "窗口1");
        Thread thread2 = new Thread(st, "窗口2");
        Thread thread3 = new Thread(st, "窗口3");

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

可以看到这种程序写法的问题有:

  • 相同的票出现了多次

  • 出现了负数的票  

 问题产生的原因分析:这种多线程共享的是同一份数据,线程执行的随机性导致的,可能在卖票过程中丢失cpu的执行权,导致出现问题

二、解决问题的方案

要解决这个问题实际上就是让程序没有安全问题,如何实现其实就是让每次操作的时候只能有一个线程执行成功即可,那么可以实现的方案如下:

同步代码块

实现方法:

synchronized(任意对象) { 
	多条语句操作共享数据的代码 
}

优缺点:

  • 好处:解决了多线程的数据安全问题

  • 弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率

实例:

public class SellTicket1 implements Runnable{

    private int tickets = 100;
    private Object obj = new Object();

    @Override
    public void run() {
        while(true){
            synchronized (obj){
                //当线程进来的时候就会把这段代码锁起来
                if(tickets <= 0){
                    break;
                }else {
                    try{
                        Thread.sleep(100);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    tickets--;
                    System.out.println(Thread.currentThread().getName() + "票数还剩余" + tickets);
                }
            }
            //到此处锁就会释放了
        }

    }
}

public class SellTicketDemo {
    public static void main(String[] args) {
        SellTicket1 st = new SellTicket1();

        Thread thread1 = new Thread(st, "窗口1");
        Thread thread2 = new Thread(st, "窗口2");
        Thread thread3 = new Thread(st, "窗口3");

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

同步方法

实现方法:

锁住的对象是:this

修饰符 synchronized 返回值类型 方法名(方法参数) { 
	方法体;
}

静态同步方法

实现方法:

锁住的对象是:类名.class

修饰符 static synchronized 返回值类型 方法名(方法参数) { 
	方法体;
}

实例:

public class SellTicket1 implements Runnable{

    private static int tickets = 100;

    @Override
    public void run() {
        while(true){
            if("窗口一".equals(Thread.currentThread().getName())){
                //同步方法
                boolean b = synchronizedMthod();
                if(b){
                    break;
                }
            }else if("窗口二".equals(Thread.currentThread().getName())){
                //同步代码块
                synchronized (SellTicket1.class){
                    if(tickets == 0){
                        break;
                    }else{
                        try{
                            Thread.sleep(100);
                        }catch (Exception e){
                            e.printStackTrace();
                        }
                        tickets--;
                        System.out.println(Thread.currentThread().getName() + "票数还剩余" + tickets);
                    }

                }
            }
        }

    }

    private static synchronized boolean synchronizedMthod(){
        if(tickets == 0){
            return true;
        }else{
            try{
                Thread.sleep(100);
            }catch (Exception e){
                e.printStackTrace();
            }
            tickets--;
            System.out.println(Thread.currentThread().getName() + "票数还剩余" + tickets);
            return false;
        }
    }
}



public class SellTicketDemo {
    public static void main(String[] args) {
        SellTicket1 st = new SellTicket1();

        Thread thread1 = new Thread(st, "窗口1");
        Thread thread2 = new Thread(st, "窗口2");
        Thread thread3 = new Thread(st, "窗口3");

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

ReentrantLock()

如果我们想可以直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock

方法名说明
void lock()获得锁
void unlock()释放锁

实例:

public class SellTicket1 implements Runnable{

    //票的数量
    private int tickets = 100;
    private Object obj = new Object();
    private ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            //synchronized (obj){//多个线程必须使用同一把锁.
            try {
                lock.lock();
                if (tickets <= 0) {
                    //卖完了
                    break;
                } else {
                    Thread.sleep(100);
                    tickets--;
                    System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + tickets + "张票");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
            // }
        }
    }
}


public class SellTicketDemo {
    public static void main(String[] args) {
        SellTicket1 st = new SellTicket1();

        Thread thread1 = new Thread(st, "窗口1");
        Thread thread2 = new Thread(st, "窗口2");
        Thread thread3 = new Thread(st, "窗口3");

        thread1.start();
        thread2.start();
        thread3.start();
    }
}


http://lihuaxi.xjx100.cn/news/1490278.html

相关文章

mysql 基本操作2

目录 Update 将小白龙的数学成绩跟新为 80 分 将总成绩后三名的数学成绩30分 Delete 删除沙悟净的成绩 删除倒数第一名的成绩 Truncate 聚合函数 count 查看员工表里面的人数 查看男女员工的个数 查看男员工占所有员工个数的比例 sum 计算所有员工的工资和 计算各…

15个关于AI的Github库

这里是我们精选的创新项目列表&#xff08;排名不分先后&#xff09;&#xff0c;这些项目正在机器学习和人工智能领域蓬勃发展 1&#xff1a;privateGPT 作者&#xff1a;imartinezGithub 星数&#xff1a;16.7K描述&#xff1a;利用LLM的力量&#xff0c;在没有互联网连接的情…

Linux下的系统编程——系统调用(五)

前言&#xff1a; 由操作系统实现并提供给外部应用程序的编程接口。(Application Programming Interface,API)。系统调用就是应用程序同系统之间数据交互的桥梁。 一、open/close函数 1.open函数&#xff1a; &#xff08;1&#xff09;int open(char *pathname, int flags)…

freertos之队列集

队列集就是队列的集合 队列集的长度所包含队列的长度之和 实验 创建两个队列和一个队列集&#xff0c;并把队列添加到队列集&#xff0c;创建3个任务&#xff0c;其中两个任务往队列存数据&#xff0c;另一个任务通过队列集来获取数据。 实验结果&#xff1a;

Base64与cv2读取的图片,格式互转

Base64编码 Base64编码是一种将二进制数据转换为可打印字符的方式&#xff0c;以便在文本格式中传输或存储。它通常用于将二进制数据编码为ASCII字符串&#xff0c;以便在电子邮件、网页或XML文件中传输。 Base64编码的原理是将3个8位字节的数据&#xff08;即24位二进制数据…

nginx配置keepalive长连接

nginx之keepalive详解与其配置_keepalive_timeout_恒者走天下的博客-CSDN博客 为什么要有keepalive? 因为每次建立tcp都要建立三次握手&#xff0c;消耗时间较长&#xff0c;所以为了减少tcp建立连接需要的时间&#xff0c;就可以设置keep_alive长连接。 nginx中keep_alive对…

openCV实战-系列教程9:傅里叶变换(傅里叶概述/频域变换结果/低通与高通滤波)、原理解析、源码解读

OpenCV实战系列总目录 打印图像直接用这个函数&#xff1a; def cv_show(img,name):cv2.imshow(name,img)cv2.waitKey()cv2.destroyAllWindows()1、傅里叶变换 在生活中&#xff0c;我们的大部分事情都是以时间为参照的&#xff0c;用时间为参照的为时域分析&#xff0c;在频…

小小的消火栓 蕴藏着“大智慧”

2023年8月22日14时24分&#xff0c;天津消防救援总队指挥中心接到报警&#xff1a;天津市南开区万兴街道南京路新天地大厦外墙起火。接警后&#xff0c;总队立即调派23个消防站及南开、和平支队全勤指挥部&#xff0c;共计284名消防救援人员、62辆消防车赶赴现场。总队全勤指挥…