线程不安全的几种表现形式以及解决方式

线程不安全的几种表现形式以及解决方式

案例一:买票问题

实现思想run()方法中不断地在买票直到票被买完为止先假设有三中人同时间买票分别是 黄牛,乘客,小白

首先我们先做一个买票方法,模拟乘客买票

    private int buy() throws InterruptedException {
        if (ticks<=0){
            flag=false;
            return 0;
        }
        Thread.sleep(10);
        //防止cpu执行速度太快还没等其它人买票就将票买完了
        System.out.println(Thread.currentThread().getName()+"==>"+ticks--);
        return 0;
    }

然后将run() 方法进行重写

private  int ticks=10;//定义当前票数
    boolean flag=true;   //循环终止flag
    @Override
    public void run() {
            while (true){
                try {
                    buy();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

最后在开启线程进行测试

public static void main(String[] args) {
        buyTicket buyTicket=new buyTicket();
        //将三个角色丢进同一根线程中
        new Thread(buyTicket,"黄牛").start();
        new Thread(buyTicket,"乘客").start();
        new Thread(buyTicket,"小白").start();
    }

首先我们会以为票卖完的时候线程就会自动终止,但是并非如此
在这里插入图片描述
黄牛可真是猛啊居然买到了-1张票,匪夷所思。好吧然我们画一张图看一下。

都是假象惹的祸

在这里插入图片描述

现在让我们给buy方法加一把锁

  private synchronized int  buy()

运行代码
在这里插入图片描述
现在的买票1正常了

案例二:银行取钱模拟

在这里插入图片描述
我们想的是假如我从银行中取走了50元现在账户中信息只有50元,所以女朋友不会取到钱,或者女朋友直接从银行中取走了100元,而我没有取到钱。所以我根据这个思路写一个方法来模拟。

        public void run() {
            if (account.money-drawingMoney<0){
                System.out.println("钱不够取不了");
                return;
            }
            try {
                Thread.sleep(100);//模拟延时
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            account.money=account.money-drawingMoney;
            nowMoney=nowMoney+drawingMoney;
            System.out.println(account.name+"余额为"+account.money);
            System.out.println(this.getName()+"手里面的钱"+nowMoney);
        }

好了现在我们让我和女朋友同时取钱

public static void main(String[] args) {
        Account account=new Account(100,"家森的私房钱");
        Drawing you=new Drawing(account,50,"你");
        Drawing girlfriend=new Drawing(account,100,"girlfriend");
        you.start();
        girlfriend.start();
    }

有点惊讶,我和女朋友都有钱,但是好像成了贷款。。。这是明显的线程不安全。
在这里插入图片描述

解决办法:给run方法加一把锁 ?
在这里插入图片描述

看来加锁后还是不行那我们得换一种方法。

同步块 :synchronized(obj) { }

  Obj称之为同步监视器:
  1. Obj可以是任何对象,推荐使用共享资源作为同步监视器
  2. 同步方法中无需指定同步监视器,因为同步方法中得监视器就是this,对象本身,或者反射中得class;

同步监视器得执行过程:
3. 第一个线程访问,锁定同步监视器,执行其中代码
4. 第二个线程访问 ,发现同步监视器锁定,无法进行访问
5. 当第一个线程访问结束时,解锁同步监视器
6. 第二个线程再次访问发现没有锁,锁定并访问

我们发现Account是共享资源所以我们将account作为同步监视器
 synchronized (account){
               if (account.money-drawingMoney<0){
                   System.out.println("钱不够取不了");
                   return;
               }
               try {
                   Thread.sleep(100);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               account.money=account.money-drawingMoney;
               nowMoney=nowMoney+drawingMoney;
               System.out.println(account.name+"余额为"+account.money);
               System.out.println(this.getName()+"手里面的钱"+nowMoney);
           }

在这里插入图片描述

案例三:不安全线程

我们都知道ArrayList集合是线程不安全的,但是他是如何表现得不安全的呐?

 List<String> list =new ArrayList<String>();
        for (int i = 0; i <= 99999; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();//每进行一次循环进入一个线程
        }
        Thread.sleep(3000);
        System.out.println(list.size());

理论上当打印出list.size()应该为100000。
在这里插入图片描述

线程少了,不安全事件发生了,但是怎么解决呐?可以给list加一个同步监视器

在这里插入图片描述
问题又解决了。
好了这就是本文章的全部内容了,,,感谢阅读。。。。。。

### 回答1: Java线程和Linux的线程有很大的不同。Linux的线程是基于操作系统内核的线程实现,而Java线程则是基于虚拟机的线程实现。此外,Linux的线程使用的是分时调度,而Java线程则使用的是协作式调度。此外,Java线程拥有更丰富的API和更优秀的线程安全性。 ### 回答2: Java线程和Linux的线程之间有一些重要的区别。 首先,Java线程是由Java虚拟机(JVM)管理的。JVM负责线程的创建、调度和销毁。这意味着Java线程是跨平台的,不受底层操作系统的限制。而Linux的线程是由操作系统内核管理的,因此受限于特定的操作系统。 其次,Java线程是轻量级的。Java线程模型采用了一种称为“协作式”(cooperative)的方式,即线程自己控制自己的调度。这是通过Java线程调度器来实现的,调度器根据线程的优先级和其他因素来确定下一个执行的线程。而Linux的线程是内核级线程,由操作系统内核负责调度和管理,内核会根据一些算法和策略来决定下一个执行的线程。 另外,Java线程是以对象的形式存在的。每个Java线程都由一个Thread对象表示,程序员可以通过Thread类来操纵线程的状态和行为。而Linux的线程则没有这么强的关联性,它们是由操作系统内核在内存中维护的。 最后,Java线程在创建和销毁的过程中会有一些额外的开销。由于Java线程需要JVM的管理和调度,创建、销毁和切换线程的成本通常会比较高。而Linux的线程则没有这样的开销,因为它们由操作系统内核直接管理。 总的来说,Java线程和Linux的线程之间存在着一些关键的区别,包括管理方式、调度机制、实现形式和开销等方面。这些区别决定了它们在不同的环境和使用场景下的运行特性和性能表现。 ### 回答3: Java线程和Linux的线程有以下几点区别。 首先,Java线程是由Java虚拟机(JVM)来管理和调度的,而Linux的线程是由操作系统来管理和调度的。这意味着Java线程是在用户空间中实现的,而Linux的线程是在内核空间中实现的。由于JVM是跨平台的,因此Java线程在不同的操作系统上具有一致性和可移植性,而Linux的线程则依赖于特定的操作系统。 其次,Java线程模型是基于协作式线程调度的,即线程自己控制自己的执行时间,而Linux的线程模型是基于抢占式线程调度的,即操作系统通过时间片轮转等方式控制线程的执行。这使得Java线程更容易实现线程的同步和互斥,但也可能导致线程的执行时间不平均。而Linux的线程能够更有效地利用多核处理器,并且可以更精确地控制线程的执行。 此外,Java线程提供了一套丰富的线程管理和同步机制,如Thread类、Runnable接口、synchronized关键字等,以及诸如锁、条件变量、信号量等高级同步工具。而Linux的线程提供的线程管理和同步机制相对较少,需要依赖于pthread库进行线程的创建、管理和同步。 最后,虽然Java线程和Linux的线程在实现和管理上存在不同,但它们都可以实现多线程编程,提高程序的并发性和性能。开发人员在选择使用Java线程还是Linux线程时,需要考虑到具体的应用场景、性能需求和平台兼容性等因素。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值