栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java线程的几种状态及线程安全问题

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java线程的几种状态及线程安全问题

目录

一、线程状态

1.1 所有线程状态

1.2  线程状态的转移 

二、线程安全性问题

2.1 线程安全的概念

2.2  线程不安全的原因

2.2.1  抢占式执行

2.2.2  多个线程修改同一个变量

2.2.3  非原子性操作

2.2.4  内存可见性

2.2.5  指令重排序


一、线程状态

1.1 所有线程状态

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

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

· NEW: 新建状态,安排了工作还未开始行动。

·  RUNNABLE:运行状态,分为①RUNNABLE:得到时间片运行中的状态 ②Ready:未得到时  间片的就绪状态

·  BOLCKED:阻塞状态,当遇到锁时,会出现这种状态。

·  WAITING:无限期的等待状态 。

·  TIMED_WAITING:有明确结束时间的等待状态。

·  TERMINATED:终止状态,当线程任务结束之后会变成此状态。

1.2  线程状态的转移 

NEW->RUNNABLE:

新一个线程,状态为new,在我们启动线程之后就会变成runnable。

public class ThreadState {
    public static void main(String[] args) throws InterruptedException {
        Thread thread1=new Thread(()->{
            System.out.println("当前线程状态2:"+Thread.currentThread().getState());
        });
        System.out.println("当前线程状态:"+thread1.getState());
        thread1.start();
    }
}

RUNNABLE->TIMED_WAITING

在线程运行时我们让线程休眠一段时间,这时获取线程的状态,就是timed_waiting。

import java.util.concurrent.TimeUnit;

public class ThreadState {
    public static void main(String[] args) throws InterruptedException {
        Thread thread1=new Thread(()->{
            System.out.println("当前线程状态2:"+Thread.currentThread().getState());
            try {

                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        System.out.println("当前线程状态:"+thread1.getState());
        thread1.start();
        Thread.sleep(1000);
        System.out.println("当前线程状态3:"+thread1.getState());

    }
}

RUNNABLE ->TERMINATED

线程执行完之后,就会从 RUNNABLE 状态变成 TERMINATED 销毁状态,如下代码所示: 

import java.util.concurrent.TimeUnit;

public class ThreadState {
    public static void main(String[] args) throws InterruptedException {
        Thread thread1=new Thread(()->{
            System.out.println("当前线程状态2:"+Thread.currentThread().getState());
            try {

                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        System.out.println("当前线程状态:"+thread1.getState());
        thread1.start();
        Thread.sleep(1000);
        System.out.println("当前线程状态3:"+thread1.getState());
        //等待线程1执行完
        thread1.join();
        System.out.println("当前线程状态4:"+thread1.getState());

    }
}

二、线程安全性问题

2.1 线程安全的概念

线程不安全是指多线程的执行结果不符合预期。

单线程:

public class ThreadDemo15 {
     static class Counter{
          private int num=0;
          private  int MAX_COUNT=0;
          public Counter(int MAX_COUNT){
               this.MAX_COUNT=MAX_COUNT;
          }
          //++方法
          public void increment(){
               for (int i = 0; i < MAX_COUNT; i++) {
                    num++;
               }
          }
          //--方法
          public void  decrement(){
               int temp=0;
               for (int i = 0; i < MAX_COUNT; i++) {
                    num--;
               }
          }
          public  int getNum(){
               return num;
          }
     }


     public static void main(String[] args) throws InterruptedException {
          Counter counter=new Counter(100000);
          counter.increment();
          counter.decrement();
          System.out.println("最终结果:"+counter.getNum());
     }
}

多线程:

public class ThreadDemo15 {
     static class Counter{
          private int num=0;
          private  int MAX_COUNT=0;
          public Counter(int MAX_COUNT){
               this.MAX_COUNT=MAX_COUNT;
          }
          //++方法
          public void increment(){
               for (int i = 0; i < MAX_COUNT; i++) {
                    num++;
               }
          }
          //--方法
          public void  decrement(){
               int temp=0;
               for (int i = 0; i < MAX_COUNT; i++) {
                    num--;
               }
          }
          public  int getNum(){
               return num;
          }
     }


     public static void main(String[] args) throws InterruptedException {
          Counter counter=new Counter(100000);
          Thread  thread1=new Thread(()->{
               counter.increment();
          });
          Thread thread2=new Thread(()->{
               counter.decrement();
          });
          thread1.start();
          thread2.start();
          thread1.join();
          thread2.join();
          System.out.println("最终结果:"+counter.getNum());
     }
}

 

这个时候我们得到的结果就是不确定的,这是为什么呢?下面我们来看以下问题。

2.2  线程不安全的原因

2.2.1  抢占式执行

多个线程争抢着执行任务,会导致线程不安全,如果我们让一个线程执行完之后,再执行 下一个线程,就不会出现线程不安全的情况了,如下代码所示:

public class ThreadDemo15 {
     static class Counter{
          private int num=0;
          private  int MAX_COUNT=0;
          public Counter(int MAX_COUNT){
               this.MAX_COUNT=MAX_COUNT;
          }
          //++方法
          public void increment(){
               for (int i = 0; i < MAX_COUNT; i++) {
                    num++;
               }

          }
          //--方法
          public void  decrement(){
               int temp=0;
               for (int i = 0; i < MAX_COUNT; i++) {
                    num--;
               }
          }
          public  int getNum(){
               return num;
          }
     }


     public static void main(String[] args) throws InterruptedException {
          Counter counter=new Counter(100000);
          Thread  thread1=new Thread(()->{
               counter.increment();
          });
          Thread thread2=new Thread(()->{
               counter.decrement();
          });
          thread1.start();
          thread1.join();
          thread2.start();
          thread2.join();
          System.out.println("最终结果:"+counter.getNum());
     }
}

 只是将thread1.join()放在了thread2启动之前,就不会出现线程不安全的问题了。

2.2.2  多个线程修改同一个变量

因为两个线程都是修改 num这个变量,线程在并发执行的时候会出现不安全的问题,但如果各自修改各自的变量,就不会出现这个问题,如下代码所示:

public class ThreadDemo15 {
     static class Counter{
//          private int num=0;
          private int num1=0;
          private int num2=0;
          private  int MAX_COUNT=0;
          public Counter(int MAX_COUNT){
               this.MAX_COUNT=MAX_COUNT;
          }
          //++方法
          public int increment(){
               for (int i = 0; i < MAX_COUNT; i++) {
                    num1++;
               }
               return num1;
          }
          //--方法
          public int decrement(){
               int temp=0;
               for (int i = 0; i < MAX_COUNT; i++) {
                    num2--;
               }
               return num2;
          }
          public  int getNum(){
               return num1+num2;
          }
     }


     public static void main(String[] args) throws InterruptedException {
          Counter counter=new Counter(100000);
          Thread  thread1=new Thread(()->{
               counter.increment();
          });
          Thread thread2=new Thread(()->{
               counter.decrement();
          });
          thread1.start();
          thread1.join();
          thread2.start();
          thread2.join();
          System.out.println("最终结果:"+counter.getNum());
     }
}

2.2.3  非原子性操作

什么是原子性?

我们把⼀段代码想象成⼀个房间,每个线程就是要进⼊这个房间的⼈。如果没有任何机制保证,A进⼊房间之后,还没有出来;B 是不是也可以进⼊房间,打断 A 在房间⾥的隐私。这个就是不具备原子性的。
那我们应该如何解决这个问题呢?是不是只要给房间加⼀把锁,A 进去就把⻔锁上,其他⼈是不是就进不来了。这样就保证了这段代码的原子性了。

⼀条 java 语句不⼀定是原⼦的,也不⼀定只是⼀条指令。
⽐如刚才我们看到的 num++,其实是由三步操作组成的:
1. 从内存把数据读到 CPU
2. 进⾏数据更新
3. 把数据写回到 CPU

不保证原⼦性会给多线程带来什么问题?
如果⼀个线程正在对⼀个变量操作,中途其他线程插⼊进来了,如果这个操作被打断了,结果就可能是错误的。这点也和线程的抢占式调度密切相关. 如果线程不是 "抢占" 的, 就算没有原子性, 也问题不大。

线程1的++操作执行了一半的时候,线程2开始进行--操作,因此由于操作不是原子性的就产生了线程不安全的问题。

2.2.4  内存可见性

可见性指一个线程共享变量进行修改时,另外的线程能够及时的看到。

Java内存模型(JMM):java虚拟机规范中定义了java内存模型。

目的是屏蔽各种硬件和操作系统的内存访问差异,以实现让java程序在各种平台下都能达到一致的并发效果。
 

1.当线程要读取一个共享变量时,会先把变量从主内存拷贝到工作内存,再从工作内存内读取数据。

2.当线程要修改一个共享变量时,也会先修改工作内存中的副本,再同步回主内存。

由于每个线程有自己的工作内存,这些工作内存中的内容相当于同一个共享变量的"副本",此时修改线程1中的工作内存中的值,线程2的工作内存不一会及时知道,这就是内存可见性对线程安全 性造成的影响。

2.2.5  指令重排序

JVM会对我们的指令自动进行他认为的优化,在单线程下没有问题,但是多线程中会对我们的线程安全造成影响。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/781485.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号