基础复习——Day07线程安全

线程安全

在启动一个线程时会给线程开一个Java虚拟机栈多个线程也就是多个JavaJava虚拟机栈互不影响,但是在创建线程是它们都在一个堆区里所以它们共享堆区。

线程安全:
可见性问题: 一个线程类有一个静态修饰的变量,这个变量是所有这个线程对象共享的,在创建一个线程时并执行对这个变量进行修改时,其他线程却不能及时察觉到这个共享数据的变化

//一个线程类
public class MyThread extends Thread {
    public static int a = 0;//线程共享的数据
    @Override
    public void run() {
        System.out.println("线程启动,休息2秒...");
        try {
            Thread.sleep(1000 * 2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("将a的值改为1");
        a = 1;
        System.out.println("线程结束...");
    }
}

下面是一个测试类

public class Demo {
    public static void main(String[] args) {
        //1.创建出这个线程对象并启动线程
        MyThread t = new MyThread();
        t.start();

        //2.main方法主线程主线程继续
        while (true) {
            if (MyThread.a == 1) {
            //主线程读到数据发生变化时就会停止循环
                System.out.println("主线程读到了a = 1");
            }
        }
    }
}

在测试后发现并不能让主线程停止循环
原因:
静态修饰的变量它会存在于方法区里的静态区这个区也是共享的所以理应能读到变化但是为什么读不到呢——在执行主线程时会把静态区里的值保存一份到主线程中然后线程取就是在主线程中去取子线程也是会在子线程中取,一旦子线程把值改了它会迅速的把数据更新到静态区,然后主线程回去静态区里取最新的值,但是由于代码过于简单导致它执行速度太快了根本没时间取更新就不断的去本线程取数据,加一个打印语句它就会慢下来然后正常的读到并停止

有序性问题: 有时候编译器在编译时会对代码进行一个重排的机制,就是它可能不会按照你写代码的顺序进行执行而是按照它的来比如:

a=10
b=20
int c = a+b
它可能会先定义b的值在定义a的值最后在相加

在单线成情况下不会有什么问题,到了多线程情况下执行顺序不同可能就会直接影响结果举例:

CPU它会给时间片段线程会去抢或谁抢到了谁去执行,然后编译器又会有个重排的机制 这就有问题了。

原子性: 多个线程对共享资源的修改 可能会导致数据最终不符合正常逻辑

volatile: 只要在变量前加上这个关键字修饰就可以让编译器不重排这个变量就解决了有序性这个问题还有就是他会每次都能获取到最新的数据对于多线程之间对共享资源进行修改而可见性的问题进行了解决,但无法解决原子性问题

原子性解决方案: 使用Atomic类型的数据来解决变量 的原子性问题,它也解决了其他的两个问题。例如AtomicInteger这个类型
底层原理主要是有一个CAS乐观锁这么一个概念,它会去主内存中拉取数据跟线程中的数据进行比较如果不一致就更新线程中的数据然后再去拉主内存的数据比较进行多次比较无误之后才会对数据进行操作再更新到主内存中

synchronized关键字来解决代码块的一个原子性问题



public class TicketRun1 implements Runnable{
    // 票数
    private int ticket = 500;

    // 这个成员变量 用作同步代码块的时候 表示锁 无实际意义
    Object obj = new Object();


    @Override
    public void run() {

        while (true){
            /*同步代码块
            在线程切换时都会判断是否上锁
            如果上锁线程就会阻塞直到cpu给的时间片段执行完切换到原先线程把代码执行完毕把锁解除,其他线程再抢到时间时才能执行这一块代码然后上锁重复之前的操作,它之所以会比原先慢就是因为他是同步阻塞的会浪掉CPU的时间
            */
            synchronized (obj){
                // 卖票
                if(ticket > 0){
                    // 有票
                    //try {
                    //    Thread.sleep(50);
                    //} catch (InterruptedException e) {
                    //    e.printStackTrace();
                    //}
                    // 获取当前卖票的 线程名
                    String name = Thread.currentThread().getName();
                    System.out.println(name+"窗口 卖出票:"+ticket--);

                }
            }


        }
    }
}

还有就是synchronized修饰的方法

在这里插入图片描述

Lock锁方法:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TicketRun3 implements Runnable{
    // 票数
    private int ticket = 500;

 
    //创建一个Lock锁
    Lock lock = new ReentrantLock();


    @Override
    public void run() {

        while (true){
            // 同步代码块
            try{
            //上锁
                lock.lock();
                // 卖票
                if(ticket > 0){
                    // 有票
                    //try {
                    //    Thread.sleep(50);
                    //} catch (InterruptedException e) {
                    //    e.printStackTrace();
                    //}
                    // 获取当前卖票的 线程名
                    String name = Thread.currentThread().getName();
                    System.out.println(name+"窗口 卖出票:"+ticket--);
                }

            }catch (Exception e){

            }finally {
            //解锁 之所以放这里是防止万一出现死锁情况 放这里它最后还是会解锁
                lock.unlock();
            }
        }
    }
}

Java集合的并发包
CopyOnWriteArrayList相比较于ArrayList是线程安全的,它底层是使用了Lock锁的方式是实现了一个线程安全问题
CopyOnWriteArraySet相比较于HashSet是线程安全的,它底层是CopyOnWriteArrayList。
Hashtable 相比较于hashmap是线程安全的底层是使用的synchronized关键字对方法做了一个同步方法比如说put()方法它是直接在这个方法上加了synchronized关键字相当于是对整个方法做了一个同步锁而ConcurrentHashMap也是线程安全的它是在方法中对部分操作进行了一个synchronized同步锁 所以效率要比Hashtable要高

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值