java多线程小结,及解决应用挂死的问题

这两天为了定位JBOSS总是挂死的问题,学习了一下JAVA多线程方面的知识,在此总结一下 

一、在Java程序中,JVM负责线程的调度。线程调度是指按照特定的机制为多个线程分配CPU的使用权。 
调度的模式有两种:分时调度和抢占式调度。分时调度是全部线程轮流得到CPU使用权,并平均分配每一个线程占用CPU的时间;抢占式调度是根据线程的优先级别来获取CPU的使用权。JVM的线程调度模式采用了抢占式模式。 

二、Thread类实际上也是实现了Runnable接口的类。 
在启动的多线程的时候,须要先经过Thread类的构造方法Thread(Runnable target) 构造出对象,而后调用Thread对象的start()方法来运行多线程代码。 
实际上全部的多线程代码都是经过运行Thread的start()方法来运行的。所以,不论是扩展Thread类仍是实现Runnable接口来实现多线程,最终仍是经过Thread的对象的API来控制线程的,熟悉Thread类的API是进行多线程编程的基础。 

三、JAVA多线程涉及到2个问题,一个是线程的调度,另外一个是线程的同步 

四、线程的状态有:new、runnable、running、waiting、timed_waiting、blocked、dead 

当执行new Thread(Runnable r)后,新建立出来的线程处于new状态,这种线程不可能执行 

当执行thread.start()后,线程处于runnable状态,这种状况下只要获得CPU,就能够开始执行了。runnable状态的线程,会接受JVM的调度,进入running状态,可是具体什么时候会进入这个状态,是随机不可知的 

running状态中的线程最为复杂,可能会进入runnable、waiting、timed_waiting、blocked、dead状态: 
若是CPU调度给了别的线程,或者执行了Thread.yield()方法,则进入runnable状态,可是也有可能马上又进入running状态 
若是执行了Thread.sleep(long),或者thread.join(long),或者在锁对象上调用object.wait(long)方法,则会进入timed_waiting状态 
若是执行了thread.join(),或者在锁对象上调用了object.wait()方法,则会进入waiting状态 
若是进入了同步方法或者同步代码块,没有获取锁对象的话,则会进入blocked状态 

处于waiting状态中的线程,若是是由于thread.join()方法进入等待的话,在目标thread执行完毕以后,会回到runnable状态;若是是由于object.wait()方法进入等待的话,在锁对象执行object.notify()或者object.notifyAll()以后会回到runnable状态 

处于timed_waiting状态中的线程,和waiting状态中的差很少,只不过是设定时间到了,就会回到runnable状态 

处于blocked状态中的线程,只有获取了锁以后,才会脱离阻塞状态 

当线程执行完毕,或者抛出了未捕获的异常以后,会进入dead状态,该线程结束 

五、当线程池中线程都具备相同的优先级,调度程序的JVM实现自由选择它喜欢的线程。这时候调度程序的操做有两种可能:一是选择一个线程运行,直到它阻塞或者运行完成为止。二是时间分片,为池内的每一个线程提供均等的运行机会。 

六、设置线程的优先级:线程默认的优先级是建立它的执行线程的优先级。能够更改线程的优先级。 

JVM从不会改变一个线程的优先级。然而,1-10之间的值是没有保证的。一些JVM可能不能识别10个不一样的值,而将这些优先级进行每两个或多个合并,变成少于10个的优先级,则两个或多个优先级的线程可能被映射为一个优先级。 

七、Thread.yield()方法做用是:暂停当前正在执行的线程对象,并执行其余线程。 
yield()应该作的是让当前运行线程回到可运行状态,以容许具备相同优先级的其余线程得到运行机会。所以,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。可是,实际中没法保证yield()达到让步目的,由于让步的线程还有可能被线程调度程序再次选中。 

结论:yield()从未致使线程转到等待/睡眠/阻塞状态。在大多数状况下,yield()将致使线程从运行状态转到可运行状态,但有可能没有效果。 

八、另外一个问题是线程的同步,这个我感受比调度更加复杂一些 

Java中每一个对象都有一个“内置锁”,也有一个内置的“线程表” 

当程序运行到非静态的synchronized方法上时,会得到与正在执行代码类的当前实例(this实例)有关的锁;当运行到同步代码块时,得到与声明的对象有关的锁 

释放锁是指持锁线程退出了synchronized方法或代码块。 

当程序运行到synchronized同步方法或代码块时对象锁才起做用。 

一个对象只有一个锁。因此,若是一个线程得到该锁,就没有其余线程能够得到锁,直到第一个线程释放(或返回)锁。这也意味着任何其余线程都不能进入该对象上的synchronized方法或代码块,直到该锁被释放。 

九、当提到同步(锁定)时,应该清楚是在哪一个对象上同步(锁定)? 

十、 

obj.wait() 
obj.notify() 
obj.notifyAll() 

关于这3个方法,有一个关键问题是: 

必须从同步环境内调用wait()、notify()、notifyAll()方法。只有拥有该对象的锁的线程,才能调用该对象上的wait()、notify()、notifyAll()方法 

与每一个对象具备锁同样,每一个对象也能够有一个线程列表,他们等待来自该对象的通知。线程经过执行对象上的wait()方法得到这个等待列表。从那时候起,它再也不执行任何其余指令,直到调用对象的notify()方法为止。若是多个线程在同一个对象上等待,则将只选择一个线程(不保证以何种顺序)继续执行。若是没有线程等待,则不采起任何特殊操做。 

十一、下面贴几个代码实例,配合jstack命令说明一下 
java

Java代码  收藏代码数据库

  1. public class ThreadA {  编程

  2.   

  3.     public static void main(String[] args) {  多线程

  4.           

  5.         ThreadB b = new ThreadB();// ThreadB status: new  学习

  6.       

  7.         b.start();// ThreadB status: runnable  this

  8.           

  9.         synchronized (b) {  spa

  10.             try {                 线程

  11.                 System.out.println("等待对象b完成计算。。。");  对象

  12.                 Thread.sleep(60000);  接口

  13.                 b.wait();  

  14.             } catch (InterruptedException e) {  

  15.                 e.printStackTrace();  

  16.             }  

  17.             System.out.println("b对象计算的总和是:" + b.total);  

  18.         }  

  19.     }  

  20.   

  21. }  

  22.   

  23. public class ThreadB extends Thread {  

  24.   

  25.     int total;  

  26.   

  27.     public void run() {  

  28.         synchronized (this) {  

  29.             for (int i = 0; i < 101; i++) {  

  30.                 total += i;  

  31.             }  

  32.             notifyAll();  

  33.         }  

  34.     }  

  35.   

  36. }   


jstack输出的结果是: 

"main" prio=6 tid=0x00846800 nid=0x1638 waiting on condition [0x0092f000] 
   java.lang.Thread.State: TIMED_WAITING (sleeping) 
at java.lang.Thread.sleep(Native Method) 
at net.kyfxbl.lock.ThreadA.main(ThreadA.java:20) 
- locked <0x22a18a90> (a net.kyfxbl.lock.ThreadB) 

"Thread-0" prio=6 tid=0x02bbb800 nid=0x1410 waiting for monitor entry [0x02f0f000] 
   java.lang.Thread.State: BLOCKED (on object monitor) 
at net.kyfxbl.lock.ThreadB.run(ThreadB.java:11) 
- waiting to lock <0x22a18a90> (a net.kyfxbl.lock.ThreadB) 

能够看到,主线程和新线程在同一个对象上锁定,主线程的方法里执行了Thread.sleep(60000),所以进入了TIMED_WAITING状态,而新线程则进入BLOCKED状态 

Java代码  收藏代码

  1. public class ThreadA {  

  2.   

  3.     public static void main(String[] args) {  

  4.   

  5.         ThreadB b = new ThreadB();// ThreadB status: new  

  6.   

  7.         b.start();// ThreadB status: runnable  

  8.   

  9.         synchronized (b) {  

  10.               

  11.             try {  

  12.                 System.out.println("等待对象b完成计算。。。");  

  13.                 b.wait();  

  14.             } catch (InterruptedException e) {  

  15.                 e.printStackTrace();  

  16.             }  

  17.             System.out.println("b对象计算的总和是:" + b.total);  

  18.         }  

  19.     }  

  20.   

  21. }  

  22.   

  23. public class ThreadB extends Thread {  

  24.   

  25.     int total;  

  26.   

  27.     public void run() {  

  28.   

  29.         synchronized (this) {  

  30.   

  31.             try {  

  32.                 Thread.sleep(60000);  

  33.             } catch (InterruptedException e) {  

  34.                 e.printStackTrace();  

  35.             }  

  36.   

  37.             for (int i = 0; i < 101; i++) {  

  38.                 total += i;  

  39.             }  

  40.             notifyAll();  

  41.         }  

  42.     }  

  43.   

  44. }  


jstack输出的结果是: 

"main" prio=6 tid=0x00846800 nid=0x1684 in Object.wait() [0x0092f000] 
   java.lang.Thread.State: WAITING (on object monitor) 
at java.lang.Object.wait(Native Method) 
- waiting on <0x22a18b08> (a net.kyfxbl.lock.ThreadB) 
at java.lang.Object.wait(Object.java:485) 
at net.kyfxbl.lock.ThreadA.main(ThreadA.java:22) 
- locked <0x22a18b08> (a net.kyfxbl.lock.ThreadB) 


"Thread-0" prio=6 tid=0x02bcc800 nid=0x19c waiting on condition [0x02f0f000] 
   java.lang.Thread.State: TIMED_WAITING (sleeping) 
at java.lang.Thread.sleep(Native Method) 
at net.kyfxbl.lock.ThreadB.run(ThreadB.java:12) 
- locked <0x22a18b08> (a net.kyfxbl.lock.ThreadB) 

2个线程仍是在同一个对象上同步,但此次主线程马上执行了b.wait()方法,所以释放了对象b上的锁,本身进入了WAITING状态。接下来新线程获得了对象b上的锁,因此没有进入阻塞状态,紧接着执行Thread.sleep(60000)方法,进入了TIMED_WAITING状态 

Java代码  收藏代码

  1. public class ThreadA {  

  2.   

  3.     public static void main(String[] args) {  

  4.   

  5.         ThreadB b = new ThreadB();// ThreadB status: new  

  6.   

  7.         b.start();// ThreadB status: runnable  

  8.   

  9.         synchronized (b) {  

  10.   

  11.             try {  

  12.                 System.out.println("等待对象b完成计算。。。");  

  13.                 b.wait();// ThreadB status: running  

  14.             } catch (InterruptedException e) {  

  15.                 e.printStackTrace();  

  16.             }  

  17.             System.out.println("b对象计算的总和是:" + b.total);  

  18.         }  

  19.     }  

  20.   

  21. }  

  22.   

  23.   

  24. public class ThreadB extends Thread {  

  25.   

  26.     int total;  

  27.   

  28.     public void run() {  

  29.   

  30.         synchronized (this) {  

  31.   

  32.             for (int i = 0; i < 101; i++) {  

  33.                 total += i;  

  34.             }  

  35.   

  36.             notifyAll();  

  37.   

  38.             try {  

  39.                 System.out.println("我要睡了");  

  40.                 Thread.sleep(60000);  

  41.             } catch (InterruptedException e) {  

  42.                 e.printStackTrace();  

  43.             }  

  44.   

  45.         }  

  46.     }  

  47.   

  48. }  


jstack输出的结果是: 

"main" prio=6 tid=0x00846800 nid=0x3ec in Object.wait() [0x0092f000] 
   java.lang.Thread.State: BLOCKED (on object monitor) 
at java.lang.Object.wait(Native Method) 
- waiting on <0x22a18ba0> (a net.kyfxbl.lock.ThreadB) 
at java.lang.Object.wait(Object.java:485) 
at net.kyfxbl.lock.ThreadA.main(ThreadA.java:20) 
- locked <0x22a18ba0> (a net.kyfxbl.lock.ThreadB) 

"Thread-0" prio=6 tid=0x02bbb800 nid=0x14b4 waiting on condition [0x02f0f000] 
   java.lang.Thread.State: TIMED_WAITING (sleeping) 
at java.lang.Thread.sleep(Native Method) 
at net.kyfxbl.lock.ThreadB.run(ThreadB.java:19) 
- locked <0x22a18ba0> (a net.kyfxbl.lock.ThreadB) 

当主线程执行b.wait()以后,就进入了WAITING状态,可是新线程执行notifyAll()以后,有一个瞬间主线程回到了RUNNABLE状态,可是好景不长,因为这个时候新线程尚未释放锁,因此主线程马上进入了BLOCKED状态 

十二、当在对象上调用wait()方法时,执行该代码的线程当即放弃它在对象上的锁。然而调用notify()时,并不意味着这时线程会放弃其锁。若是线程仍然在完成同步代码,则线程在移出以前不会放弃锁。所以,只要调用notify()并不意味着这时该锁被释放 

1三、与线程休眠相似,线程的优先级仍然没法保障线程的执行次序。只不过,优先级高的线程获取CPU资源的几率较大,优先级低的并不是没机会执行。 

1四、在一个线程中开启另一个新线程,则新开线程称为该线程的子线程,子线程初始优先级与父线程相同。 

1五、JRE判断程序是否执行结束的标准是全部的前台执线程行完毕了,而无论后台线程的状态,所以,在使用后台线程时候必定要注意这个问题。 

1六、下面说说咱们此次JBOSS挂死问题的处理方法 

现象:系统运行一段时间以后,发现有几个子系统没法访问了,可是另外几个能够。CPU占用达到100% 

观察了一下,发现没法访问的应用都部署在同一个JBOSS里,因而把该JBOSS的堆栈用jstack命令输出 

发现里面有大量的线程处于BLOCKED状态,均是在执行到c3p0的一个方法里的某一行时,BLOCKED住了 

因而下载c3p0的源码,跟进去看了一下,这是一个同步方法,内部会去获取数据库链接,若是获取到链接,就进行下一步操做,若是获取不到,就执行sleep(long timeout)方法。 

反推一下,我猜想多是这样的: 

因为某段代码没有释放数据库链接-->链接池中的链接耗尽-->部分线程无限TIMED_WAITING-->其他线程都BLOCKED-->开启新线程-->频繁引起GC-->占用大量CPU-->应用挂起 

后来对全部涉及到数据库链接的代码进行排查,发现确实有几个地方作完数据库操做之后,没有释放链接。把这部分代码改掉,从新启动JBOSS,没有再出现JBOSS挂起的现象 

相关文章
相关标签/搜索