博客列表 >Java多线程的正常使用

Java多线程的正常使用

NuoNon
NuoNon原创
2020年11月20日 17:51:26839浏览

一个线程的生命周期

线程是一个动态执行的过程,它也有一个从产生到死亡的过程。

下图显示了一个线程完整的生命周期。

  • 新建状态:

    使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。

  • 就绪状态:

    当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。

  • 运行状态:

    如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。

  • 阻塞状态:

    如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:

    • 等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。

    • 同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。

    • 其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。

  • 死亡状态:

    一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。



线程例子:

1.实现Runnable接口:

  1. public class MyRunnable implements Runnable{
  2. private String msg;
  3. @Override
  4. public void run() {
  5. for(int i=0;i<10;i++){
  6. System.out.println(Thread.currentThread().getName()+"\t"+this.msg);
  7. }
  8. }
  9. public MyRunnable(){
  10. super();
  11. }
  12. public MyRunnable(String msg){
  13. super();
  14. this.msg=msg;
  15. }
  16. }
  1. /**
  2.  * 使用Runnable目的就是有共享内容 几个线程共同完成任务
  3.  * @author WQY
  4.  *匿名内部类在Runnable上的使用 
  5.  *不能加参数 必须无参!如果线程只在一处使用,可以选择这种方式,但是可读性较差
  6.  */
  7. //
  8. public class Test3 {
  9. public static void main(String[] args) {
  10.         for(int i=0;i<10;i++){
  11.         System.out.println(Thread.currentThread().getName()+"但可寻所爱");
  12.         }
  13. MyRunnable m1=new MyRunnable("山海不可平");
  14. //1.使用匿名内部类(很少使用)
  15. Thread t1=new Thread(new Runnable() {
  16. @Override
  17. public void run() {
  18. for(int i=0;i<10;i++){
  19.         System.out.println(Thread.currentThread().getName()+"山海不可平");
  20.        }
  21. }
  22. },"所思隔云端");
  23. //2.没有使用匿名内部类,实现Runnable接口解决了单一继承限制
  24. Thread t2=new Thread(m1,"奈何凡肉身");
  25. t1.start();
  26. t2.start();
  27. }
  28. }


2.继承Thread
  1. public class MyThread extends Thread {
  2. private String msg;
  3. public MyThread() {
  4. super();
  5. }
  6. public MyThread(String name,String msg) {
  7. super(name);
  8. this.msg=msg;
  9. }
  10. @Override
  11. public void run() {
  12. for(int i=0;i<10;i++){
  13. System.out.println(this.getName()+"\t"+this.msg);
  14. }
  15. }
  16. }
  1. //主线程 默认名字main
  2. public class Test2 {
  1.           public static void main(String[] args) {
  2. //MyThread mt1=new MyThread("mt1","一望可相见");
  3. MyThread mt2=new MyThread("mt2","一步如重城");
  4. //使用匿名内部类来启动线程
  5. new Thread("mt1"){
  6. @Override
  7. public void run() {
  8. for(int i=0;i<10;i++){
  9. System.out.println(Thread.currentThread().getName()+"\t"+"一望可相见");
  10. }
  11. }
  12. }.start();
  13. //继承Thread建立线程 
  14. mt2.start();
  15. for(int i=0;i<10;i++){
  16. System.out.println(Thread.currentThread().getName()+"\t"+"所爱隔山海");
  17. }
  18. }
  19. }


3.窗口卖票例子:

  1. class TicketRunnable implements Runnable {
  2. private Integer count;
  3. public TicketRunnable(Integer count) {
  4. super();
  5. this.count = count;
  6. }
  7. public TicketRunnable() {
  8. super();
  9. }
  10. @Override
  11. public void run() {
  12.          while(true){
  13.         //锁池状态
  14.         //同步块  (同步锁)解决脏数据问题。保证一个线程在运行 运行结束别的线程才能进去
  15.         synchronized(this){//this是表示当前对象 共享的count 。synchronized目的就是把共享的数据锁住
  16.         if(count<=0){
  17.         System.out.println(Thread.currentThread().getName()+"exit   窗口:\t"+this.count);
  18.         break;
  19.         }
  20.         try {
  21.         //sleep()控制运行时间
  22. Thread.sleep(100);
  23. } catch (InterruptedException e) {
  24. e.printStackTrace();
  25. }
  26.         int i=count--;
  27.         //count-- 表达式的值后改变 先给表达式赋值在改count的值
  28.         System.out.println(Thread.currentThread().getName()+"窗口:\t"+i);
  29.             }
  30.          }
  31. }
  32. }
  33. public class Ticket {
  34. public static void main(String[] args) {
  35. MyThreadDemo m=new MyThreadDemo();
  36. Thread t1=new Thread(m,"窗口1");
  37. Thread t2=new Thread(m,"窗口2");
  38. Thread t3=new Thread(m,"窗口3");
  39. Thread t4=new Thread(m,"窗口4");
  40. t1.start();
  41. t2.start();
  42. t3.start();
  43. t4.start();
  44. }
  45. }

4.经典的生产者消费者模型:

要保证 仓库是一个实例

  1. public class Warehouse {
  2. private List<String> goods=new ArrayList<String>();
  3. public Warehouse() {
  4. super();
  5. }
  6. public Warehouse(List<String> goods) {
  7. super();
  8. this.goods = goods;
  9. }
  10. public List<String> getGoods() {
  11. return goods;
  12. }
  13. public void setGoods(List<String> goods) {
  14. this.goods = goods;
  15. }
  16. //同步方法   使用wait() 和notifyAll() 必须在同步里
  17. public synchronized void customer(){
  18. if(this.goods.isEmpty()){
  19. System.out.println("仓库空了");
  20. try {
  21. this.wait();//进入等待状态 退出CPU 方法结束,不向下运行
  22. } catch (InterruptedException e) {
  23. e.printStackTrace();
  24. }
  25. }
  26. /*try {
  27. Thread.sleep(100);
  28. } catch (InterruptedException e) {
  29. e.printStackTrace();
  30. }*/
  31. System.out.println("消费者消费了"+this.goods.remove(0));
  32. this.notifyAll();
  33. }
  34. public synchronized void produce(String name){
  35. if(!this.goods.isEmpty()){
  36. System.out.println("仓库满了");
  37. try {
  38. this.wait();
  39. } catch (InterruptedException e) {
  40. e.printStackTrace();
  41. }
  42. }
  43. /*try {
  44. Thread.sleep(100);
  45. } catch (InterruptedException e) {
  46. e.printStackTrace();
  47. }*/
  48. this.goods.add(name);
  49. System.out.println("生产者生产了" + name);
  50. this.notifyAll();
  51. }
  52. }
  53. //生产者
  54. public class Pro implements Runnable {
  55. private Warehouse w;
  56. public Pro() {
  57. super();
  58. }
  59. public Pro(Warehouse w) {
  60. super();
  61. this.w = w;
  62. }
  63. @Override
  64. public void run() {
  65. for(int i=0;i<1000;i++){
  66. this.w.produce("产品"+(i+1));
  67. }
  68. }
  69. }
  70. //消费者
  71. public class Cus implements Runnable {
  72. private Warehouse w;
  73. public Cus() {
  74. super();
  75. }
  76. public Cus(Warehouse w) {
  77. super();
  78. this.w = w;
  79. }
  80. @Override
  81. public void run() {
  82. for(int i=0;i<1000;i++){
  83. this.w.customer();
  84. }
  85. }
  86. }
  87. //测试
  88. public class TestCP {
  89. public static void main(String[] args) {
  90. Warehouse w=new Warehouse();
  91. Pro p=new Pro(w);
  92. Thread tp=new Thread(p);
  93. Cus c=new Cus(w);
  94. Thread tc=new Thread(c);
  95. tp.start();
  96. tc.start();
  97. }
  98. }
线程的一些问题:
线程的sleep()方法和yield()方法有什么区别? 
答: 
① sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会; 
② 线程执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后转入就绪(ready)状态; 
③ sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常; 
④ sleep()方法比yield()方法(跟操作系统CPU调度相关)具有更好的可移植性。

当一个线程进入一个对象的synchronized方法A之后,其它线程是否可进入此对象的synchronized方法B? 
答:不能。其它线程只能访问该对象的非同步方法,同步方法则不能进入。因为非静态方法上的synchronized修饰符要求执行方法时要获得对象的锁,如果已经进入A方法说明对象锁已经被取走,那么试图进入B方法的线程就只能在等锁池(注意不是等待池哦)中等待对象的锁。

请说出与线程同步以及线程调度相关的方法。 
答: 
- wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁; 
- sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理InterruptedException异常; 
- notify():唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且与优先级无关; 
- notityAll():唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它们竞争,只有获得锁的线程才能进入就绪状态;
synchronized关键字的用法? 
答:synchronized关键字可以将对象或者方法标记为同步,以实现对对象和方法的互斥访问,可以用synchronized(对象) { … }定义同步代码块,或者在声明方法时将synchronized作为方法的修饰符。在第60题的例子中已经展示了synchronized关键字的用法

线程的优先级

每一个 Java 线程都有一个优先级,这样有助于操作系统确定线程的调度顺序。

Java 线程的优先级是一个整数,其取值范围是 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )。

默认情况下,每一个线程都会分配一个优先级 NORM_PRIORITY(5)。

具有较高优先级的线程对程序更重要,并且应该在低优先级的线程之前分配处理器资源。但是,线程优先级不能保证线程执行的顺序,而且非常依赖于平台。


创建一个线程

Java 提供了三种创建线程的方法:

  • 通过实现 Runnable 接口;
  • 通过继承 Thread 类本身;
  • 通过 Callable 和 Future 创建线程。

通过实现 Runnable 接口来创建线程

创建一个线程,最简单的方法是创建一个实现 Runnable 接口的类。

为了实现 Runnable,一个类只需要执行一个方法调用 run(),声明如下:

public     void     run   (   )  

你可以重写该方法,重要的是理解的 run() 可以调用其他方法,使用其他类,并声明变量,就像主线程一样。

在创建一个实现 Runnable 接口的类之后,你可以在类中实例化一个线程对象。

Thread 定义了几个构造方法,下面的这个是我们经常使用的:

Thread   (   Runnable     threadOb   ,   String     threadName   )   ;  

这里,threadOb 是一个实现 Runnable 接口的类的实例,并且 threadName 指定新线程的名字。

新线程创建之后,你调用它的 start() 方法它才会运行。

void     start   (   )   ;  

 

 
停止线程的几种方法区别  
sleep()方法(休眠)是线程类(Thread)的静态方法,调用此方法会让当前线程暂停执行指定的时间,将执行机会(CPU)让给其他线程,但是对象的锁依然保持,因此休眠时间结束后会自动恢复(线程回到就绪状态,请参考第66题中的线程状态转换图)。wait()是Object类的方法,调用对象的wait()方法导致当前线程放弃对象的锁(线程暂停执行),进入对象的等待池(wait pool),只有调用对象的notify()方法(或notifyAll()方法)时才能唤醒等待池中的线程进入等锁池(lock pool),如果线程重新获得对象的锁就可以进入就绪状态。
 
声明:本文内容转载自脚本之家,由网友自发贡献,版权归原作者所有,如您发现涉嫌抄袭侵权,请联系admin@php.cn 核实处理。
全部评论
文明上网理性发言,请遵守新闻评论服务协议