Java线程安全状态专题解析

一、观察线程的所有状态

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

 public static void main(String[] args) {
        for (Thread.State state : Thread.State.values()){
            System.out.println(state);
        }
    }

NEW: 安排了工作, 还未开始行动

RUNNABLE: 可工作的. 又可以分成正在工作中和即将开始工作.就绪状态

BLOCKED: 这几个都表示排队等着其他事情

WAITING: 这几个都表示排队等着其他事情

TIMED_WAITING: 这几个都表示排队等着其他事情

TERMINATED: 工作完成了.

二、线程状态和状态转移的意义

NEW:Thread对象有了,但是PCB还没有

RUNNABLE:线程正在CPU上执行或者即将到CPU上执行(PCB在就绪队列中,随时可能被调度到)

WAITING:wait方法导致

TIMED_WAITING:sleep方法导致

BLOCKED:等待锁导致

TERMINATED:对象还在,但PCB已经没了

public static void main(String[] args) {
        Thread t = new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 100_00; i++){

                }
            }
        };
        System.out.println("线程启动前:" + t.getState());

        t.start();
        while (t.isAlive()){
            System.out.println("线程运行中:" + t.getState());
        }
        System.out.println("线程结束后:" + t.getState());
    }

三、多线程带来的风险

线程不安全的原因

①线程是抢占式执行的

线程不安全的万恶之源,线程之间的调度完全由内核负责,用户代码中感知不到,也无法控制。线程之间谁先执行,谁后执行,谁执行到哪里从CPU上下来,这样的过程用户无法控制也无法感知到的。

②自增操作不是原子的

每次++都能拆成三个步骤

        把内存中的数据读取到CPU中

        把CPU中的数据+1

        把计算的数据写回内存中

如果两个线程串行执行,此时计算结果为2。

如果两个线程并行执行,线程1进行++操作到一半的时候,线程也进行了++操作,此时自增两次,但结果为1。

必须保证线程1save结束了,线程2再load,此时计算结果才正确

③多个线程尝试修改同一个变量

如果是一个线程修改一个变量,线程安全

如果多个线程读取同一个变量,线程安全

如果多个线程修改不同的变量。线程安全

④内存可见性导致线程安全问题

⑤指令重排序

Java的编译器在编译代码时,会对指令进行优化,调整指令的先后顺序,保证原有的逻辑不变的情况下,提高程序的运行效率

四,解决线程安全问题

锁-synchronized

未加锁

static class Counter{
        public int count = 0;

         public void increase(){
            count++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        Thread t1 = new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 50000; i++){
                    counter.increase();
                }
            }
        };
        Thread t2 = new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 50000; i++){
                    counter.increase();
                }
            }
        };
        t1.start();
        t2.start();
        t1.join();
        t2.join();

        System.out.println(counter.count);
    }

 已加锁

static class Counter{
        public int count = 0;

        synchronized public void increase(){
            count++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        Thread t1 = new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 50000; i++){
                    counter.increase();
                }
            }
        };
        Thread t2 = new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 50000; i++){
                    counter.increase();
                }
            }
        };
        t1.start();
        t2.start();
        t1.join();
        t2.join();

        System.out.println(counter.count);
    }

此处的synchronized就是针对counter这个对象来加锁,进入increase方法内部,就把加锁状态设为ture,increase方法退出之后,就把加锁状态设为false,如果某个线程已经把加锁状态设为ture,此处的其他的线程尝试去加锁,就会阻塞

synchronized的特性——刷新内存

synchronized 的工作过程:

        1. 获得互斥锁

        2. 从主内存拷贝变量的最新副本到工作的内存

        3. 执行代码

        4. 将更改后的共享变量的值刷新到主内存

        5. 释放互斥锁

synchronized的特性——互斥

 public static void main(String[] args) {
        Object locker = new Object();

        Thread t1 = new Thread(){
            @Override
            public void run() {
                Scanner scanner = new Scanner(System.in);
                synchronized (locker) {
                    System.out.println("输入一个整数");
                    int num = scanner.nextInt();
                    System.out.println("num= " + num);
                }
            }
        };
        t1.start();

        Thread t2 = new Thread(){
            @Override
            public void run() {
                while (true){
                    synchronized (locker){
                        System.out.println("线程2获取到锁");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        };
        t2.start();
    }

一旦线程一获取到锁,并且没有释放的话,线程2就会一直在锁这里阻塞等待

 public static void main(String[] args) {
        Object locker1 = new Object();
        Object locker2 = new Object();

        Thread t1 = new Thread(){
            @Override
            public void run() {
                Scanner scanner = new Scanner(System.in);
                synchronized (locker1) {
                    System.out.println("输入一个整数");
                    int num = scanner.nextInt();
                    System.out.println("num= " + num);
                }
            }
        };
        t1.start();

        Thread t2 = new Thread(){
            @Override
            public void run() {
                while (true){
                    synchronized (locker2){
                        System.out.println("线程2获取到锁");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        };
        t2.start();
    }

 不是同一把锁,就不回出现竞争,就没有互斥了。

public static void main(String[] args) {
        Object locker1 = new Object();
        Object locker2 = new Object();

        Thread t1 = new Thread(){
            @Override
            public void run() {
                Scanner scanner = new Scanner(System.in);
                synchronized (locker1.getClass()) {
                    System.out.println("输入一个整数");
                    int num = scanner.nextInt();
                    System.out.println("num= " + num);
                }
            }
        };
        t1.start();

        Thread t2 = new Thread(){
            @Override
            public void run() {
                while (true){
                    synchronized (locker2.getClass()){
                        System.out.println("线程2获取到锁");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        };
        t2.start();
    }

这个代码中,两个线程都在针对locker1和locker2的类对象进行竞争,此处的locker1和locker2的类型都是Object,对应的对象都是相同的对象。 

到此这篇关于Java线程安全状态专题解析的文章就介绍到这了,更多相关Java 线程安全内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java单例模式的线程安全,饿汉和懒汉模式详解

    单例模式 创建唯一的一个变量(对象),在类中将构造函数设为protected或者private(析构函数设为相对应的访问权限),故外部不能实例化对象,再提供访问它的一个全局访问点,即定义一个static函数,返回类中唯一构造的一个实例对象.任何条件下,保证只有一个实例对象,这就是单例. 1.线程安全:在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况. 2..懒汉模式:在系统运行中,实例并不存在,只有当需要的时候才

  • 关于java中线程安全问题详解

    目录 一.什么时候数据在多线程并发的环境下会存在安全问题? 二.怎么解决线程安全问题? 三.银行 取钱/存钱 案例 为什么会出现线程安全问题 四.总结 一.什么时候数据在多线程并发的环境下会存在安全问题? 三个条件: 条件1:多线程并发. 条件2:有共享数据. 条件3:共享数据有修改的行为. 满足以上3个条件之后,就会存在线程安全问题. 二.怎么解决线程安全问题?         线程排队执行.(不能并发).用排队执行解决线程安全问题.这种机制被称为:线程同步机制. 三.银行 取钱/存钱 案例

  • Java线程安全解决方案(synchronized,ReentrantLock,Atomic)

    线程安全解决方案 synchronized,ReentrantLock,Atomic 使用场景描述 在实际开发过程中如果服务量,请求频繁,就会经常碰见并发,这时候不做处理就会出现很多非法数据.这时候就需要解决线程安全的问题,这时候就可以使用java当中的锁机制.常用有java关键synchronized.可重入锁ReentrantLock,还有并发包下的Atomic 或者Concurrent的安全类型. synchronized使用场景: 在资源竞争不是很激烈的情况下,偶尔出现并发,需要同步的情

  • Java中关于线程安全的三种解决方式

    三个窗口卖票的例子解决线程安全问题 问题:买票过程中,出现了重票.错票-->出现了线程的安全问题 问题出现的原因:当某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来,也操作车票 如何解决:当一个线程a在操作ticket的时候,其他线程不能参与进来,知道线程a操作完ticket时,其他线程才可以开始操作ticket,这种情况即使线程a出现了阻塞,也不能被改变 在Java中,我们通过同步机制,来解决线程的安全问题.(线程安全问题的前提:有共享数据) 方式一:同步代码块 synchroniz

  • Java中线程安全问题

    目录 一.线程不安全 二.那些情况导致了线程不安全? 三.Java中解决线程不安全的方案 1.volatile"轻量级"解决线程不安全 2.synchronized自动加锁 四.公平锁与非公平锁机制 五.volatile和synchronized的区别 六.synchronized和Lock的区别 一.线程不安全 多线程的执行环境中,程序的执行结果和预期的结果不符合,这就称为发生了线程不安全现象 二.那些情况导致了线程不安全? 大致分为以下5种情况: (1)CPU抢占执行 (无法解决)

  • JAVA多线程线程安全性基础

    目录 线程安全性 什么是线程安全的代码 什么是线程安全性 总结 线程安全性 一个对象是否需要是线程安全的,取决于它是否被多个线程访问,而不取决于对象要实现的功能 什么是线程安全的代码 核心:对 共享的 和 可变的 状态的访问进行管理.防止对数据发生不受控的并发访问. 何为对象的状态? 状态是指存储在对象的状态变量(例如实例或静态域)中的数据.还可能包括 其他依赖对象 的域. eg:某个HashMap的状态不仅存储在HashMap对象本身,还存储在许多Map.Entry对象中. 总而言之,在对象的

  • 聊聊java多线程创建方式及线程安全问题

    什么是线程 线程被称为轻量级进程,是程序执行的最小单位,它是指在程序执行过程中,能够执行代码的一个执行单位.每个程序程序都至少有一个线程,也即是程序本身. 线程的状态 新建(New):创建后尚未启动的线程处于这种状态 运行(Runable):Runable包括了操作系统线程状态的Running和Ready,也就是处于此状态的线程有可能正在执行,也有可能正在等待着CPU为它分配执行时间. 等待(Wating):处于这种状态的线程不会被分配CPU执行时间.等待状态又分为无限期等待和有限期等待,处于无

  • java多线程创建及线程安全详解

    什么是线程 线程被称为轻量级进程,是程序执行的最小单位,它是指在程序执行过程中,能够执行代码的一个执行单位.每个程序程序都至少有一个线程,也即是程序本身. 线程的状态 新建(New):创建后尚未启动的线程处于这种状态 运行(Runable):Runable包括了操作系统线程状态的Running和Ready,也就是处于此状态的线程有可能正在执行,也有可能正在等待着CPU为它分配执行时间. 等待(Wating):处于这种状态的线程不会被分配CPU执行时间.等待状态又分为无限期等待和有限期等待,处于无

  • Java多线程及线程安全实现方法解析

    一.java多线程实现的两种方式 1.继承Thread /** * * @version: 1.1.0 * @Description: 多线程 * @author: wsq * @date: 2020年6月8日下午2:25:33 */ public class MyThread extends Thread{ @Override public void run() { System.out.println("This is the first thread!"); } public s

  • Java线程安全状态专题解析

    一.观察线程的所有状态 线程的状态是一个枚举类型 Thread.State public static void main(String[] args) { for (Thread.State state : Thread.State.values()){ System.out.println(state); } } NEW: 安排了工作, 还未开始行动 RUNNABLE: 可工作的. 又可以分成正在工作中和即将开始工作.就绪状态 BLOCKED: 这几个都表示排队等着其他事情 WAITING:

  • java线程的基础实例解析

    java中建立线程可以有两种方式,分别是继承Thread类和实现Runnable接口. 继承Thread public class MyThread extends Thread{ public MyThread(String name){ super(name); } int i; public void run(){ for(i=0;i<5;i++){ System.out.println(getName()+"--"+i); } } public static void m

  • Java线程安全基础概念解析

    Java线程安全初步了解.JAVA线程安全从总体上来说,是指Java对象在多线程运行环境下的一种特性,表现为常规(区别于特殊调用情况)情况下每次调用都能得到正确的逻辑结果.从本质上来说,将对象的方法行为加上了同步控制逻辑,而调用者无须做其他额外的同步控制就可以安全放心的使用对象. 1.线程安全的定义 当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象就是线程安

  • Java线程池ForkJoinPool实例解析

    这篇文章主要介绍了Java线程池ForkJoinPool实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 背景:ForkJoinPool的优势在于,可以充分利用多cpu,多核cpu的优势,把一个任务拆分成多个"小任务",把多个"小任务"放到多个处理器核心上并行执行:当多个"小任务"执行完成之后,再将这些执行结果合并起来即可.这种思想值得学习. import java.io.IOExcept

  • 深入探究Java线程的状态与生命周期

    目录 一.线程的状态 新建(初始) 就绪(可运行) 运行 阻塞 死亡 二.线程的状态转移 三.线程的生命周期 一.线程的状态 NEW: 安排了工作, 还未开始行动RUNNABLE: 可工作的. 又可以分成正在工作中和即将开始工作.BLOCKED: 这几个都表示排队等着其他事情WAITING: 这几个都表示排队等着其他事情TIMED_WAITING: 这几个都表示排队等着其他事情TERMINATED: 工作完成了 新建(初始) 当继承Thread类和实现了Runnable接口,就可以创建线程,新建

  • java 线程池状态及状态转换

    目录 线程池状态转移 terminated方法 总结 前言: 在 Java 中,线程池的状态和线程的状态是完全不同的, 线程有 6 种状态: NEW:初始化状态. RUNNABLE:可运行/运行状态. BLOCKED:阻塞状态. WAITING:无时限等待状态 TIMED_WAITING:有时限等待状态和 TERMINATED:终止状态. 而线程池的状态有以下 5 种: RUNNING:运行状态,线程池创建好之后就会进入此状态,如果不手动调用关闭方法,那么线程池在整个程序运行期间都是此状态. S

  • 浅谈Java线程Thread.join方法解析

    join字面上是加入的意思,我们先看看join方法的解释和实现. /** * Waits for this thread to die. * 调用方线程(调用join方法的线程)执行等待操作,直到被调用的线程(join方法所属的线程)结束,再被唤醒 * <p> An invocation of this method behaves in exactly the same * way as the invocation * * * @throws InterruptedException *

  • Java线程状态变换过程代码解析

    线程状态 NEW:刚创建未启动的线程 RUNNABLE:正在执行状态 BLOCKED:处于阻塞状态的线程 WAITING:正在等待另一个线程执行特定动作的线程 TIMED_WAITING:等待另一个线程执行时间到达指定时间 TERMINATED:线程退出执行 public class TestState { public static void main(String[] args) { Thread thread = new Thread(()->{ for (int i = 0; i <

  • Java线程的相关方法详细解析

    start()  启动线程方法 run()  调用start()方法时,真正执行的就是该方法的方法体 sleep()  让当前线程睡眠,睡眠到期自动苏醒,并进入可运行状态,而不是运行状态 yield() 暂停当前正在执行的线程对象,JVM线程调度程序基于优先级的抢先机制调用其他优先级高的线程,优先级的取值范围1 (Thread.MIN_PRIORITY) -- 10( Thread.MAX_PRIORITY),创建线程默认是5 (NORM_PRIORITY) setPriority(int ne

  • Java线程安全与非线程安全解析

    ArrayList和Vector有什么区别?HashMap和HashTable有什么区别?StringBuilder和StringBuffer有什么区别?这些都是Java面试中常见的基础问题.面对这样的问题,回答是:ArrayList是非线程安全的,Vector是线程安全的:HashMap是非线程安全的,HashTable是线程安全的:StringBuilder是非线程安全的,StringBuffer是线程安全的.因为这是昨晚刚背的<Java面试题大全>上面写的.此时如果继续问:什么是线程安全

随机推荐