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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值