java多线程【线程安全问题】

(一)问题描述

        我们 先通过代码案例看线程执行过程中会发生那些安全问题

        模拟电影院卖票:

        某电影院目前正在上映国产大片,共有100张票,而它有3个平台卖票,请设计一个程序模拟该电影院卖票。

分析:事:卖票

           卖:窗口  【不同的窗口卖相同的票】
           程序中窗口就是线程对象【Thread的对象】,卖票是线程任务【Runnable的对象】

具体代码实现:

package com.tlc.thread;

public class SellTicket implements Runnable {
    //定义初始票数
    int ticket = 100;
    @Override
    public void run() {
        while(ticket > 0) {
            try {
                //线程睡眠就代表正在出票
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口:" + Thread.currentThread().getName() + "买票成功!"
                    + "还剩" + --ticket + "张票");
        }
    }
}



public class SellTicketTest {
    public static void main(String[] args) {
        //创建线程任务
        SellTicket ticket = new SellTicket();

        //创建三个线程对象模拟三个窗口
        Thread thread1 = new Thread(ticket, "窗口一");
        Thread thread2 = new Thread(ticket, "窗口二");
        Thread thread3 = new Thread(ticket, "窗口三");

        //启动线程,三个窗口开始卖票
        thread1.start();
        thread2.start();
        thread3.start();
    }
}


结果:

(二)案例中出现的问题

从上代码执行的结果中我们发现一张票被不同的窗口买了多次,这就是线程安全问题
产生的原因:
    多条线程同时操作同一个资源造成的混乱的执行结果

(三)解决

        目的:就是同步线程

        解决方案:

                1.同步代码块

                2.同步方法

                3.Lock接口

1)同步代码块

       1. 格式:  synchronized(锁对象) { 发生线程安全的代码 }

        2.锁对象:可以是任意的引用数据类型的对象,但是一旦确定就要保证其唯一性

        3.优缺点

                优点:解决了多线程的数据安全问题;

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

代码实例【只是对run方法进行改进,其他代码不变】: 

 @Override
    public void run() {
        //开始卖票
        while (ticket > 0) {
                //这里加锁,只有拿到锁对象的线程才可以进去执行
                synchronized ("a") {
                    //由于买到最后一张票时,三个线程都会进来,这样就会造成票为负数的现象,
                    //因此在这里要进行判断票数
                    if (ticket > 0) {
                    try {
                        //线程睡眠就代表正在出票
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("窗口:" + Thread.currentThread().getName() + "买票成功!"
                            + "还剩" + --ticket + "张票");
                }
            }
        }
    }

         2)同步方法

        1.格式: 

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

         锁对象: 成员同步方法锁对象是,this;而静态同步方法锁对象是,类名. class 。

主要代码:

 @Override
    public void run() {
        //开始卖票
        while (ticket > 0) {
            sellTicket();
        }
    }

    public static synchronized void sellTicket() {
        if (ticket > 0) {
            try {
                //线程睡眠就代表正在出票
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口:" + Thread.currentThread().getName() + "买票成功!"
                    + "还剩" + --ticket + "张票");
        }
    }

  3)Lock锁

                1.为了更清晰的表达如何加锁和释放锁

  • JDK5以后提供了一个新的锁对象Lock,Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化。

                2.方法介绍

                ReentrantLock构造方法:  ReentrantLock( )

                加锁解锁方法: lock( ):获得锁

                                          unlock( ):释放锁

具体实现代码:

//定义初始票数
    /*static*/ int ticket = 100;
    //创建Lock对象
    ReentrantLock lock =  new ReentrantLock();
    @Override
    public void run() {
        //开始卖票
        while (ticket > 0) {
            //获得锁
            lock.lock();
                    if (ticket > 0) {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("窗口:" + Thread.currentThread().getName() + "买票成功!"
                            + "还剩" + --ticket + "张票");
            }//释放锁
            lock.unlock();
        }
    }

以上部分如有错误之处,欢迎各位大佬留言解释,谢谢! 

版权声明:本文为CSDN博主「喵了个@汪」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_44919512/article/details/119487021

如果看完对你的学习有帮助,感谢点赞支持!

              

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值