线程安全问题以及如何解决??


前言

在前面的博客中介绍了进程和线程,以及线程中的一些常用方法,sleep() 休眠当前线程/ Thread.currentThread.getName()获取当前线程名 /join()等待线程等,下面来说说线程状态及线程安全问题


一、什么是线程状态

线程状态是一个枚举类型Thread.State

线程的六种状态:

  1. NEW :Tread对象刚创建,还没在系统中创建线程,实例创建之后,调用start()之前
  2. RUNNABLE : 就绪状态,随时可能调度到CPU上执行,或在CPU上正在执行
  3. BLOCKED :阻塞状态
  4. WAITING: 阻塞状态
  5. TIMED_WAITING: 阻塞状态
  6. TERMINATED:系统中线程结束了(入口方法执行完毕)Thread对象销毁之前

状态及状态转移

状态变化可以说是因为方法的执行,调用了一些sleep()/notify()等方法会导致线程状态发生改变

  • 放两张比较容易理解的图:
    在这里插入图片描述

在这里插入图片描述

-isAlive()方法,是否存活,可以认为除了NEW 和TERMINATED的状态都是存活着的
-yield() 上图中从运行到就绪,他表示调用yield()主动让出CPU资源,但是状态还是RUNNABLE

二、线程安全问题

什么是线程安全问题?

  • 当某线程操作过程中,尚未操作完成时,其他线程也参与进来,使结果偏离预期
    • 比如抢车票,如果一个人进入发现还有最后一张票,立马订购,在还没订购完成,又有一个人进来看到也乘一张票,两人都定了票,但是票只有一张,这就属于线程安全问题

为什么会有线程安全问题?

  1. 线程的抢占式执行,是操作系统内核觉得,属于不可控因素
  2. 多个线程修改同一个变量
  3. 修改操作不是“原子的” (保证操作的原子性是保存线程安全问题的主要手段)
  4. 内存的可见性:由于编译器的优化使当前修改的数据没有涉及写入到内存中,另一个线程就读不到最新数据,就读取的是未修改的数据
  5. 代码顺序性 (指令重排序):由于编译器优化,自动对执行顺序调整,就类似于把多个相同的重复操作比如i++ 放在一起在缓存或者寄存器中加,然后在最后写入内存,这样很容易发生线程安全问题

如何解决线程安全问题?

  • 利用synchronized关键字

    • 使用在同步代码块中
      • synchronized(同步监视器){
        //需要被同步的代码
        }
        其中被同步的代码是共享数据 例如多个线程共同操作的变量(ticket)
        同步监视器(锁)任何一个类的对象都可以充当锁,多个线程必须要共用一把锁,否则线程安全依然不能保证
    • 同步方法中
      • 如果操作共享数据的代码完整的声明在一个方法中,可以将此方法声明为同步的
      • 仍要使用同步监视器:
        • 1.非静态的同步方法:同步监视器为:this
        • 2.静态的同步方法:同步监视器为当前类本身 (类.class)
  • 也可以使用Lock锁 JDK5.0新增
    - 1.实例化Lock ReentrantLock lock= new ReentrantLock();
    - 2.调用lock.lock();
    - 3.调用解锁方法,lock.unlock();
    -volatile 关键字 比synchronized更高效 不涉及竞争,不涉及线程调度

    • volatile能够禁止指令重排序,保证内存可见性,但不保证原子性
    • 所以volatile适用于一个线程读一个线程写的情况,不适应两个线程都写的情况

    关于可重入锁

    多个线程连续加锁两次,synchronized会做特殊处理,不会出现死锁
    synchronized内部记录了当前这把锁是哪个线程持有的,如果当前加锁的线程和持有锁的线程相同,此时不是真正的进行加锁,而是把计数器++,如果调用到解锁操作,不是立刻释放锁,而是计数器–,直到减到0,才会释放。

synchronized和lock的异同?

 1. 同:二者都可以解决线程安全问题
 2. 不同:synchronized机制在执行完相应的同步代码块以后,自动释放同步监视器(锁)
                lock需要手动的启动同步(lock())同时结束同步也需要手动释放(unlock());

总结

线程安全问题无论是应用到开发环境中还是面试中,都是很高频的问题,需要我们重视,可以写一些简单的线程不安全代码体会一下,在进行加锁试试,体会线程安全后的结果与之前有哪些不同,上述方法解决了线程安全问题,但操作代码时,只能一个线程参与,其余线程等待,相当于单线程过程,效率低

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值