线程的状态与线程安全

  线程也有生命周期,线程从创建到死亡会经历如下的过程:

    创建(new)-->待运行(runnable)-->运行(running)-->死亡(dead)

              |_____阻塞______|(阻塞过程包含waiting、sleeping、yeild、join、blocked)

  下面将一一讲诉具体的每个过程。前面讲到怎样创建一个线程,但创建完一个线程后此线程并不会马上进入到运行状态,而是要等分配到一定的资源(比如内存空

间)后变为runnable,当调用start()方法后,线程便开始运行。如果此时cpu出于空闲,则会去执行此线程,否则就会出于blocked状态,等待cpu的执行权。这里的运行线程是调用start(),而不是run(),调用run()只是当前的线程去执行此方法,而不是开启新的线程。

  waiting、sleeping、yeild、blocked都是让线程出于阻塞状态,那么彼此之间又有什么区别。首先wait()方法是让线程出于等待状态,此时线程会交出cpu的执

行权,如果线程此时持有锁还会释放出锁;sleep()同样是让线程出于睡眠状态,但是线程只会交出cpu的执行权,并不会释放手中持有的锁;yeild()是让当前运行

的线程变成待运行的状态,cpu再从同等优先级的线程中选择某个线程去执行(还是有可能会选择到此线程);join()会暂停当前线程的运行去执行调用此方法的

线程,等到调用的线程执行完毕后再回过来执行当前线程;当cpu执行其他线程时,那些没有得到执行权的线程就处于blocked状态,还有就是线程处于等待锁的时候

也处于blocked状态。

  我觉得这里重点需要注意的就是wait()与notify()。用一道面试题说明这个问题。题目是:顺序输出A、B、C十次。

  1 package OutputABC;
  2
  3 public class ABCTest1 {    //三个标志位,作用是控制A、B、C输出的顺序
  4     Boolean flagA = true;
  5     Boolean flagB = false;
  6     Boolean flagC = false;
  7     Object lock = new Object();
  8     public static void main(String[] args) {
  9         ABCTest1 test = new ABCTest1();
 10         test.new ThreadA().start();
 11         try {
 12             Thread.sleep(1000);
 13         } catch (InterruptedException e) {
 14             // TODO Auto-generated catch block
 15             e.printStackTrace();
 16         }
 17         test.new ThreadB().start();
 18         try {
 19             Thread.sleep(1000);
 20         } catch (InterruptedException e) {
 21             // TODO Auto-generated catch block
 22             e.printStackTrace();
 23         }
 24         test.new ThreadC().start();
 25
 26     }
 27     //线程1,输出A
 28     class ThreadA extends Thread{
 29         @Override
 30         public void run() {
 31             // TODO Auto-generated method stub             //加锁
 32             synchronized(lock){          //循环十次
 33                 for(int i = 0;i<10;i++){              //进行判断是否该输出A了
 34                     if(flagA){                //输出A,同时置标志位B为true
 35                         System.out.println("A");
 36                         flagA = false;
 37                         flagB = true;                //通知其他线程醒来
 38                         lock.notifyAll();
 39                         try {                  //自己等待
 40                             lock.wait();
 41                         } catch (InterruptedException e) {
 42                             // TODO Auto-generated catch block
 43                             e.printStackTrace();
 44                         }
 45                     }              //如果不是就继续等待
 46                     else{
 47                     try {
 48                         lock.wait();                //这个必须要,假设当输出B后,此时A被唤醒,而B会交出锁,A如果这个时候抢占到了执行权就会去执行for循环i++,很明显会使A的输出减少1次                //大家可以试一试,如果去掉了i--,此时只会输出5次
 49                         i--;
 50                     } catch (InterruptedException e) {
 51                         // TODO Auto-generated catch block
 52                         e.printStackTrace();
 53                     }
 54                     }
 55                 }
 56             }
 57         }
 58     }
 59
 60     class ThreadB extends Thread{
 61         @Override
 62         public void run() {
 63             // TODO Auto-generated method stub
 64             synchronized(lock){
 65                 for(int i = 0;i<10;i++){
 66                     if(flagB){
 67                         System.out.println("B");
 68                         flagB = false;
 69                         flagC = true;
 70                         lock.notifyAll();
 71                         try {
 72                             lock.wait();
 73                         } catch (InterruptedException e) {
 74                             // TODO Auto-generated catch block
 75                             e.printStackTrace();
 76                         }
 77                     }
 78                     else{
 79                         try {
 80                             lock.wait();
 81                             i--;
 82                         } catch (InterruptedException e) {
 83                             // TODO Auto-generated catch block
 84                             e.printStackTrace();
 85                         }
 86                         }
 87                 }
 88             }
 89         }
 90     }
 91
 92     class ThreadC extends Thread{
 93             // TODO Auto-generated method stub
 94         @Override
 95         public void run() {
 96             // TODO Auto-generated method stub
 97             synchronized(lock){
 98                 for(int i = 0;i<10;i++){
 99                     if(flagC){
100                         System.out.println("C");
101                         flagC = false;
102                         flagA = true;
103                         lock.notifyAll();
104                         try {
105                             lock.wait();
106                         } catch (InterruptedException e) {
107                             // TODO Auto-generated catch block
108                             e.printStackTrace();
109                         }
110                     }
111                     else{
112                         try {
113                             lock.wait();
114                             i--;
115                         } catch (InterruptedException e) {
116                             // TODO Auto-generated catch block
117                             e.printStackTrace();
118                         }
119                         }
120                 }
121             }
122         }
123         }
124 }

  这道题用wait()与notify()做我只能想到用这个标志位的比较简单,想过用三个锁分别是ABC的,但在释放锁的时候感觉好麻烦。上面的注释已经很清楚了,这里就不再赘述。

至于用notify时报错IllegalMonitorStateException这里就不再说了,在java常见错误那篇文章中有详细的说明。

------------------------------------------------------------------------------------------------------------------------------------

  多线程容易产生安全问题,产生安全问题的原因就是当多个线程同时访问一个数据,并对它进行修改,此时就容易造成多次修改的危险。避免此问题的方法就是采用

同步。具体的实现有两种Lock和Synchronized,关键是需要找出需要同步的点。

 1 public class SynchroTest {
 2     //全局变量i,代表临街资源
 3     int i = 0;
 4     public static void main(String[] args) {
 5         SynchroTest test  = new SynchroTest();
 6         MyThraad1 my1 = test.new MyThraad1();
 7         //启动线程1,并人为让线程1进行判断后在休眠,给线程2的执行创造时间
 8         my1.start();
 9         MyThraad2 my2 = test.new MyThraad2();
10         my2.start();
11     }
12
13     //线程1
14     class MyThraad1 extends Thread{
15         @Override
16         public void run() {
17             if(i==0){
18             try {
19                 Thread.currentThread().sleep(5000);
20             } catch (InterruptedException e) {
21                 e.printStackTrace();
22             }
23                 i++;
24             }
25             System.out.println(i);
26         }
27     }
28     //线程2
29     class MyThraad2 extends Thread{
30         @Override
31         public void run() {
32             if(i==0){
33                 i++;
34             }
35             System.out.println(i);
36         }
37     }
38 }

产生问题

  采用同步关键字Synchronized,可以是同步方法,也可以是同步代码块。同步方法比较简单,只需要在需要同步的方法前面加上此关键字。但是需要注意加的必须是同一把锁

看下面这段代码,很容易出现的错误。

 1 public class SynchroTest {
 2     //全局变量i,代表临街资源
 3     int i = 0;
 4     public static void main(String[] args) {
 5         SynchroTest test  = new SynchroTest();
 6         MyThraad1 my1 = test.new MyThraad1();
 7         //启动线程1,并人为让线程1进行判断后在休眠,给线程2的执行创造时间
 8         my1.start();
 9         MyThraad2 my2 = test.new MyThraad2();
10         my2.start();
11     }
12
13     //线程1
14     class MyThraad1 extends Thread{
15         @Override
16         public synchronized void run() {
17             System.out.println(this);
18             if(i==0){
19             try {
20                 Thread.currentThread().sleep(1000);
21             } catch (InterruptedException e) {
22                 e.printStackTrace();
23             }
24                 i++;
25             }
26             System.out.println(i);
27         }
28     }
29     //线程2
30     class MyThraad2 extends Thread{
31         @Override
32         public synchronized void run() {
33             System.out.println(this);
34             if(i==0){
35                 i++;
36             }
37             System.out.println(i);
38         }
39     }
40 }

  如果你没有发现错误在哪里,我们看一下打印的结果就知道了。

        Thread[Thread-0,5,main]
        Thread[Thread-1,5,main]
        1
        2

  出现这个问题的原因就是不是同一把锁。最安全的方式就是自己创建锁这样就不用去判断this是谁了。

 1 public class SynchroTest1 {
 2     //全局变量i,代表数据库中的一个数据
 3     int i = 0;
 4     static SynchroTest1 test  = new SynchroTest1();
 5     public static void main(String[] args) {
 6         MyThraad1 my1 = test.new MyThraad1();
 7         //启动线程1,目的是修改数据库中的i
 8         my1.start();
 9         //代表执行过程中的异常
10         my1.interrupt();
11         //线程1休眠,代表之前执行过程比较慢,用户又重复执行一次
12         MyThraad2 my2 = test.new MyThraad2();
13         my2.start();
14     }
15
16     //线程1
17     class MyThraad1 extends Thread{
18         @Override
19         public void run() {
20             synchronized (test) {
21                 System.out.println(test);
22                 if(i==0){
23                     i++;
24                 }
25                 System.out.println(i);
26             }
27         }
28     }
29     //线程2
30     class MyThraad2 extends Thread{
31         @Override
32         public void run() {
33             synchronized (test) {
34                 System.out.println(test);
35                 if(i==0){
36                     i++;
37                 }
38                 System.out.println(i);
39             }
40         }
41     }
42 }

  对于Lock锁还有它自己的一些特性,如它的子类ReenTrantLock,他是可以中断的,而在Synchronized中是只能等待线程自己释放锁;ReenTrantLock也有类似wait与

notify的方法,不过他是用Condition来完成的,个人觉得这个比Synchronized中的wait用起来方便一点,我们可以将锁的操作和等待操作分开。在java常见异常那篇文章中有

终端的解释,这里就来看看Condition是怎样用的。还是用上面的那个面试题,上面不是说用三个锁分别控制麻烦吗,但是在这里用Condition就可以很清晰的解决这个问题。

 1 import java.util.concurrent.locks.Condition;
 2 import java.util.concurrent.locks.ReentrantLock;
 3
 4 //试想三个线程轮流输出123共十次
 5 public class ABCTest {
 6     ReentrantLock lock1 = new ReentrantLock();    //A、B、C分别对应一个锁
 7     Condition conA = lock1.newCondition();
 8     Condition conB = lock1.newCondition();
 9     Condition conC = lock1.newCondition();
10     public static void main(String[] args) throws Exception {
11         ABCTest test = new ABCTest();
12         test.new ThreadA().start();
13         Thread.sleep(1000);
14         test.new ThreadB().start();
15         Thread.sleep(1000);
16         test.new ThreadC().start();
17     }
18
19     class ThreadA extends Thread{
20         @Override
21         public void run() {
22             // TODO Auto-generated method stub
23             for(int i=0;i<10;i++){
24                 try{              //上锁
25                     lock1.lock();
26                     System.out.println("A");
27                     try {                //这里是唤醒B,并让A等待,是不是好方便,压根就不用考虑锁的问题
28                         conB.signal();
29                         conA.await();
30                     } catch (InterruptedException e) {
31                         e.printStackTrace();
32                     }
33
34                 }finally{
35                     lock1.unlock();
36                 }
37             }
38         }
39     }
40
41     class ThreadB extends Thread{
42         @Override
43         public void run() {
44             // TODO Auto-generated method stub
45             for(int i=0;i<10;i++){
46                 try{
47                     lock1.lock();
48                     System.out.println("B");
49                     try {
50                         conC.signal();
51                         conB.await();
52                     } catch (InterruptedException e) {
53                         e.printStackTrace();
54                     }
55
56                 }finally{
57                     lock1.unlock();
58                 }
59             }
60         }
61     }
62
63     class ThreadC extends Thread{
64         @Override
65         public void run() {
66             // TODO Auto-generated method stub
67             for(int i = 0;i<10;i++){
68                 try{
69                     lock1.lock();
70                     System.out.println("C");
71                     try {
72                         conA.signal();
73                         conC.await();
74                     } catch (InterruptedException e) {
75                         e.printStackTrace();
76                     }
77
78                 }finally{
79                     lock1.unlock();
80                 }
81             }
82         }
83     }
84 }

  这篇文章主要讲了线程的生命周期中出现的几种状态,以及wait常见的问题;还讲了多线程出现的安全问题,以及解决办法;在最后又以一个例子讲了ReentrantLock中的Condition。

时间: 2024-10-21 22:58:41

线程的状态与线程安全的相关文章

Java多线程之线程的状态以及线程间协作通信导致的状态变换

一:线程的状态以及变化图 Java中线程中状态可分为五种:New(新建状态),Runnable(就绪状态),Running(运行状态),Blocked(阻塞状态),Dead(死亡状态). New:新建状态,当线程创建完成时为新建状态,即new Thread(...),还没有调用start方法时,线程处于新建状态. Runnable:就绪状态,当调用线程的的start方法后,线程进入就绪状态,等待CPU资源.处于就绪状态的线程由Java运行时系统的线程调度程序(thread scheduler)来

5. 线程的状态、线程的阻塞的方式

Java中线程的状态分为6种 初始(NEW)状态:新创建了一个线程对象,但还没有调用start()方法. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统称为"运行".线程对象创建后其他线程(比如main线程)调用了该对象的start()方法.该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu的使用权.此时处于就绪状态(ready).就绪状态的线程在获得cpu时间片后变为运行中状态(running) 阻塞(BLOCKED):

线程的状态和常用方法

线程的状态一个线程从创建,启动到终止的整个过程称为线程的生命周期,在其间的任何时刻,线程总是处于某个特定状态.这些状态如下:新建状态:线程对象已经创建,对应new语句.就绪状态:就绪状态也可叫做可执行状态,对应start()方法.运行状态:当处于就绪的线程被调度并获得了cpu等执行必须的资源时,便进入到该状态,即运行了run()方法.等待/阻碍/睡眠状态:三个状态组合为一种,其共同点是线程仍然是活的,但是当前没有条件运行.换句话说,它是可运行的,如果某个事件出现,它可能返回到可运行状态,对应以下

Java多线程和并发(五),线程的状态

目录 1.线程的六个状态 2.sleep和wait的区别 3.锁池(EntryList)和等待池(WaitSet) 4.notify和notifyall的区别 五.线程的状态 1.线程的六个状态 2.sleep和wait的区别 3.锁池(EntryList)与等待池(WaitSet) 4.notify和notifyall的区别 原文地址:https://www.cnblogs.com/xzmxddx/p/10362813.html

更加强健的线程模型,解决线程卡死,退出异常情况

线程模型 1 package net.sz; 2 3 import java.util.Random; 4 import java.util.concurrent.ConcurrentLinkedQueue; 5 import org.apache.log4j.Logger; 6 7 /** 8 * 9 * <br> 10 * author 失足程序员<br> 11 * mail [email protected]<br> 12 * phone 13882122019&

线程的状态

线程的状态 线程被创建时,属于新建状态,调用start后进入就绪状态 就绪状态被cpu调用时,进入运行状态 运行状态在调用sleep.wait.join后会进入阻塞状态 运行状态执行完成后会进入死亡状态,这里包括正常执行完毕和异常执行完毕 运行状态在os切换时,线程会进入就绪状态 线程wait进入阻塞状态,被调用notify之后,会进入就绪状态 线程sleep进入阻塞状态,时间过后会进入就绪状态 当前线程的子线程join之后,当前线程进入阻塞状态,在子线程死亡之后,当前线程进入就绪状态 阻塞状态

iOS开发多线程篇—线程的状态

iOS开发多线程篇—线程的状态 一.简单介绍 线程的创建: self.thread=[[NSThread alloc]initWithTarget:self selector:@selector(test) object:nil]; 说明:创建线程有多种方式,这里不做过多的介绍. 线程的开启: [self.thread start]; 线程的运行和阻塞: (1)设置线程阻塞1,阻塞2秒 [NSThread sleepForTimeInterval:2.0]; (2)第二种设置线程阻塞2,以当前时

【学习总结】【多线程】 安全隐患 &amp; 通讯 &amp; 线程的状态

一.多线程的安全隐患 资源共享 1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源 比如多个线程访问同一个对象.同一个变量.同一个文件 当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题 所以很自然的,当某个线程进入某个事件,处理某个事件,访问某个对象的时候,先加 “锁” 互斥锁的优缺点 优点:能有效防止因多线程抢夺资源造成的数据安全问题 缺点:需要消耗大量的CPU资源 互斥锁的使用前提:多条线程抢夺同一块资源 相关专业术语:线程同步,多条线程按顺序地执行任务 互斥锁,就

Java 线程的状态

Java Thread的运行周期中, 有几种状态, 在 java.lang.Thread.State 中有详细定义和说明: NEW 状态是指线程刚创建, 尚未启动 RUNNABLE 状态是线程正在正常运行中, 当然可能会有某种耗时计算/IO等待的操作/CPU时间片切换等, 这个状态下发生的等待一般是其他系统资源, 而不是锁, Sleep等 BLOCKED  这个状态下, 是在多个线程有同步操作的场景, 比如正在等待另一个线程的synchronized 块的执行释放, 或者可重入的 synchro