线程中的虚假唤醒

理论基础:线程间通信:

1、生产者+消费者

2、通知等待唤醒机制 wait 和notify

为什么一个关于线程的操作,方法却放在Object包下?

因为多线程的线程安全,必定依赖于锁,而任何对象都可以当锁对象,所以将公共的方法放入到Object类中。

多线程编程模板B: 判断 干活 通知

基于以上理论,我们写出第一版的代码(synchronized实现):

class ShareDataOne//资源类
{
  private int number = 0;//初始值为零的一个变量
 
  public synchronized void increment() throws InterruptedException 
  {
     //1判断
     if(number !=0 ) {
       this.wait();
     }
     //2干活
     ++number;
     System.out.println(Thread.currentThread().getName()+"\t"+number);
     //3通知
     this.notifyAll();
  }
  
  public synchronized void decrement() throws InterruptedException 
  {
     // 1判断
     if (number == 0) {
       this.wait();
     }
     // 2干活
     --number;
     System.out.println(Thread.currentThread().getName() + "\t" + number);
     // 3通知
     this.notifyAll();
  }
}
 
public class NotifyWaitDemoOne
{
  public static void main(String[] args)
  {
     ShareDataOne sd = new ShareDataOne();
     new Thread(() -> {
       for (int i = 1; i < 10; i++) {
          try {
            sd.increment();
          } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
          }
       }
     }, "A").start();
     new Thread(() -> {
       for (int i = 1; i < 10; i++) {
          try {
            sd.decrement();
          } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
          }
       }
     }, "B").start();
  }
}

结果异常的完美,但是如果启动的是四个线程呢?

导致出现线程混乱的原因是:虚假唤醒

出现虚假唤醒的原因:

If只判断一次,while可以判断多次,wait()被唤醒后直接在原地执行,所以会出现以上这种情况。

解决方案: if ==> while

中断和虚假唤醒是可能产生的,所以要用loop循环,if只判断一次,while是只要唤醒就要拉回来再判断一次。if换成while

将if判断更改为while判断即可解决虚假唤醒

解决原理

因为if只进行一次判断,假设当+1抢到了资源, 此时的num=1,其他三个线程进行资源抢夺,即使此时+1*抢到了资源,但是判断后,无法进行下面的代码,就沉睡下去,再当+1抢到资源时,就不会再从开始进行if判断了,从而会直接执行下面的++代码,使得num的值变为了2,导致线程不安全的情况,造成了虚假唤醒

添加一点关于线程安全的内容

我们常见的ArrayListHashSet, HashMap 的默认长度是多少,每次扩容多少?

ArrayList

  • 默认长度:10

  • ArrayList 默认长度是10 ,每次扩容50% ,底层使用的是Object[] 。

HashSet

  • 默认长度:16

  • 扩容方式:HashSet底层是基于HashMap实现的,因此其扩容机制与HashMap类似。当HashSet中的元素数量达到容量与加载因子(默认为0.75)的乘积时,HashSet会进行扩容。扩容时,容量也会增加为原来的1.5倍。

HashMap

  • 默认长度:16

  • 扩容方式:当HashMap中的元素数量达到容量与加载因子(默认为0.75)的乘积时,HashMap会进行扩容。扩容时,容量同样会增加为原来的1.5倍,并且所有键值对会重新哈希到新的位置上。

常见的集合ArrayList,HashSet, HashMap 他们是线程安全的吗?如果不安全,如何解决

       都是线程不安全的,可以使用

  1. 使用Vectory

  2. 使用List<String> list = Collections.synchronizedList(new ArrayList<>());

Collections提供了方法synchronizedList保证list是同步线程安全的。

Collections.synchronizedList()、Collections.synchronizedMap()、Collections.synchronizedSet()同步集合类完善线程安全

使用的是Collections工具类提供的内容。可以解决线程安全问题,但是效率不是特别高。

  1. 最终方案:

做法: list --> List<String> list = new CopyOnWriteArrayList();

Set -- > Set<String> set = new CopyOnWriteArraySet<>();

Map --> Map<String,String> map = new ConcurrentHashMap();

原理:使用了写时复制技术,速度要比同步锁快,类似于表锁和行锁的区别

CopyOnWriteArrayList是arraylist的一种线程安全变体,

多线程创建的四种方式:

  1. 实现runable接口,这是创建线程最传统的方式之一

  2. 继承thread类: 这是另一种创建线程的方式,通过继承Thread类并重写run()方法。

  3. 使用线程池

  4. 使用Future和Callable

class MyThread implements Runnable{
 @Override
 public void run() {
 
 }
}
新类MyThread2实现callable接口
class MyThread2 implements Callable<Integer>{
 @Override
 public Integer call() throws Exception {
  
  return 200;
 } 
}·

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值