打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
黑马程序员ava学习笔记
------- android培训java培训、期待与您交流! ----------

多线程

    概念

    进程:是一个正在执行的程序,每一个进程都有一个执行顺序,或者叫一个控制单元。

    线程:就是进程中一个独立的控制到元,线程在操控着进程的的执行,一个进程中至少有一个线程。 

    多线程:一个程序里边有多条执行路径,就是说由程序是由多个线程组成的,多线程虽然降低了程序运行的效率,但一个程序中线程越多,获取到cpu执行权的概率就越大。

    创建线程的方式

    创建线程的方式有两种,一种是继承Thread类,另一种是实现Runnable接口。

    继承Thread类

    步骤:1,定义一个类继承Thread;

          2,复写Thread类中的run( )方法;

          3,调用线程的start( )方法,开启线程,并调用执行run( )方法。

    我们用一个小程序来说明:

  1. class Demo extends Thread  
  2. {  
  3.     public void run()  
  4.     {  
  5.         for(int x=0; x<60; x++)  
  6.             System.out.println("demo run----"+x);  
  7.     }  
  8. }  
  9. class ThreadDemo   
  10. {  
  11.     public static void main(String[] args)   
  12.     {  
  13.         Demo d = new Demo();//创建好一个线程。  
  14.         d.start();//开启线程并执行该线程的run方法。  
  15.         //d.run();仅仅是对象调用方法。而线程创建了,并没有运行。     
  16.         for(int x=0; x<60; x++)  
  17.             System.out.println("Hello World!--"+x);   
  18.     }  
  19. }  

    

    发现运行结果每一次都不同,因为多个线程都在获取cpu的执行权,cpu执行到谁,谁就运行,需要明确的是,在某一时刻,只有一个程序在运行(多核除外),cpu在做着快速的切换,,以达到看上去是同时在运行的结果,我们可以形象的把多线程的运行形容为,多个线程在抢夺cpu的执行权,这就是多线程的一个特性,随机性。谁抢到谁执行,至于执行多长时间,cpu说了算。

    多线程run方法和start方法的区别

    Thread类用于描述线程,该类定义了一个能用于储存线程运行代码的方法,这个方法就是run方法,也就是说Thread类中的run方法用于存储线程要运行的代码,主线程要运行的代码放在main方法中,虚拟机定义的。我们复写run方法的目的就是将自定义的代码放在run方法中,让线程运行。

    d.start():开启线程并执行该线程的run方法;

    d.run():仅仅是对象调用方法,线程创建了却并没有运行。

    实现Runnable接口

    步骤:1,定义一个类实现Rannable接口;

          2,覆盖Rannable接口中的run( )方法;

          3,通过Thread类创建线程对象;

          4,将Rannable接口的子类对象作为实际参数传递给Thread类的构造函数;

          5,调用Thread类的start方法开启线程,调用Runnable接口子类的run方法 。

    为什么要将Runnable接口的子类对象传递给Thread类的构造函数?

    因为自定义的run方法所属的对象是Runnable接口的子类对象,所以要让线程去指定对象的run方法,就必须明确该run方法所属的对象。

    实现方式和继承方式的区别:我们用一个售票的例子来进行说明,用继承Thread类的方法创建线程的话,需要建立四个线程对象,运行四次,等于是卖了400张票,我们可以想到用静态,但是静态的生命周期太长,所以我们可以选择实现Runnable接口的方法来完成,代码如下:

  1. /* 
  2. 需求:简单的卖票程序。 
  3. 多个窗口同时买票。 
  4. */  
  5. class Ticket implements Runnable//extends Thread  
  6. {  
  7.     private  int tick = 100;  
  8.     public void run()  
  9.     {  
  10.         while(true)  
  11.         {  
  12.             if(tick>0)  
  13.             {  
  14.                 System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);  
  15.             }  
  16.         }  
  17.     }  
  18. }  
  19. class  TicketDemo  
  20. {  
  21.     public static void main(String[] args)   
  22.     {  
  23.         Ticket t = new Ticket();//只需要建立一个对象,然后将这个对象传递给Thread类的构造函数;  
  24.         Thread t1 = new Thread(t);//创建了一个线程;  
  25.         Thread t2 = new Thread(t);//创建了一个线程;  
  26.         Thread t3 = new Thread(t);//创建了一个线程;  
  27.         Thread t4 = new Thread(t);//创建了一个线程;  
  28.         t1.start();  
  29.         t2.start();  
  30.         t3.start();  
  31.         t4.start();  
  32.     }  
  33. }  


    运行结果是:

      


    实现方式和继承方式的区别

    实现方式好处:避免了单继承的局限性,在定义线程时,建立使用实现方式。
    两种方式区别:继承Thread:线程代码存放Thread子类run方法中。
    实现Runnable,线程代码存在接口的子类的run方法。

    

    线程运行状态

    

    

    如图所示,线程存在五种状态,分别是创建,运行,临时,冻结和消亡状态。

    获取线程对象及名称

    currentThread:这是一个静态方法,用于获取当前对象;

    getName:获取线程的名称;

    setName:设置线程的名称。

    

    多线程的安全问题

    增加窗口后的运行结果:


        



    通过上边的买票小程序,我们发现当买票的窗口增加后,可能会出现打印0,-1,-2这些错票的情况,为了验证,我们可以让程序在打票之前,用sleep方法让进入if语句的线程停一段时间,我们队if语句中的代码作如下改动,如图:

    

   

    分析一下出现这种问题的原因:在if语句中有可能,四个线程进来后,都停在sleep这条语句这里,我们假设现在tick=1,某一个线程获取执行权后,向下执行输出语句,执行完tick--,tick=0,这是,还在if语句中的其他线程已经判断过条件了,所以即使tick的值已经不满足if的条件表达式,这些线程还是会向下执行输出语句,所以就会出现打印出错票的情况,线程越多,有可能打印出的错票就越多。

    得出一个结论:当多条语句在执行同一个线程共享数据时,一个线程对象对于语句只执行了一部分,没有执行完,另一个线程参与进来执行,导致共享数据的错误。

    解决办法:对于多条操作共享数据的语句,只能让一个线程都执行完后在执行其他线程,执行过程中,其他线程不能参与执行。

    java对多线程安全问题提供了专门的解决方式:同步代码块和同步函数。

    同步代码块

    synchronized(对象)  //这个对象可以是任意对象;

   {

        要被同步的代码;

    }

    那些代码需要被同步,就看那些代码在操作共享数据。

    对于售票这个例子,加上同步代码快后的代码如下,就只有run方法的代码有变化,其他地方都不变,在这里为了说明问题,我只写run方法中的代码:

  1. public void run()  
  2.     {  
  3.         while(true)  
  4.         {  
  5.             synchronized(obj)  
  6.             {  
  7.                 if(tick>0)  
  8.                 {  
  9.                     try{Thread.sleep(10);}catch(Exception e){}//让进来的线程在这里停一会;  
  10.                     System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);  
  11.                 }  
  12.             }  
  13.         }  
  14.     }  

    

    对象如同锁,持有锁的线程可以在同步中执行,没有持有锁的线程,及时获取了cpu的执行权,也进不去,因为没有获取锁。

    同步的前提:1,必须要有两个或者两个以上的线程;

                2,必须是多个线程使用同一个锁;

    必须保证同步中只有一个线程在运行。

    同步的好处与弊端

    好处:解决了多线程的安全问题。

    弊端:多个线程需要判断锁,较为消耗资源。

    同步函数

    同步函数:就是在函数中添加了synchronized修饰符的函数。

    如何找多线程中的安全问题?

     1,明确哪些代码是多线程运行代码;

     2,明确共享数据;

     3,明确多线程运行代码中哪些语句是操作共享数据的。

    同步代码快的锁是任意对象,那么同步函数的锁又是什么呢?

    对于同步函数的锁,我们可以去验证一下,创建两个线程,一个线程执行同步代码快中的代码,另一个线程执行同步函数中的代码,这个可以通过定义标记来实现,验证的原理就是同步的前提:必须是多个线程使用同一个锁;通过运行结果,我们发现,当同步代码快中的对象是Object类的对象时,还是会发生安全问题,这说明两个锁不一样,换成this后,安全问题就解决了,说明同步函数的锁是this。

    如果同步函数被静态修饰后,使用的锁是什么呢?

    我们也可以用上面的方法进行验证,发现不再是this,因为静态方法中也不可以定义this;静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象,类名.class  该对象的类型是Class
    静态的同步方法,使用的锁是该方法所在类的字节码文件对象。 类名.class

    单例设计模式中的懒汉式在多线程,处理安全问题的用的就是用的静态同步函数。

    死锁

    同步中嵌套同步,而锁却不同。

  1. /* 
  2. 死锁 
  3. */  
  4. class Dead implements Runnable  
  5. {  
  6.     private boolean flag;  
  7.     Dead(boolean flag)  
  8.     {  
  9.         this.flag = flag;  
  10.     }  
  11.     public void run()  
  12.     {  
  13.         if(flag)  
  14.         {  
  15.             synchronized(MyLock.locka)//a锁  
  16.             {  
  17.                 System.out.println("if locka");  
  18.                 synchronized(MyLock.lockb)//b锁  
  19.                 {  
  20.                     System.out.println("if lockb");  
  21.                 }  
  22.             }  
  23.         }  
  24.         else  
  25.         {  
  26.             synchronized(MyLock.lockb)//b锁  
  27.             {  
  28.                 System.out.println("if lockb");  
  29.                 synchronized(MyLock.locka)//a锁  
  30.                 {  
  31.                     System.out.println("if locka");  
  32.                 }  
  33.             }  
  34.         }  
  35.     }  
  36. }  
  37. class MyLock  
  38. {  
  39.     static Object locka = new Object();  
  40.     static Object lockb = new Object();  
  41. }  
  42. class DeadLockDemo   
  43. {  
  44.     public static void main(String[] args)  
  45.     {  
  46.         Thread t1 = new Thread(new Dead(true));  
  47.         Thread t2 = new Thread(new Dead(false));  
  48.         t1.start();  
  49.         t2.start();  
  50.     }  
  51. }  


出现这种结果:


    
    

    死锁就相当于吃饭时用筷子,筷子只有一双,两个人每人手里有一根,这时,两人一人一根筷子,但是两人都不愿意让别人先吃,不愿意将筷子送给对方,于是两人都吃不了饭,死锁也是一样,两个线程都想获取对方的锁,如果都这么僵持着,就会出现死锁,死锁就是在这样的情况下造成的。
     

    线程间通信

    线程间通信其实就是多个线程在操作同一个资源,但操作的动作不同。

    我们用一个小程序来提现什么是线程间通信:这个小程序我们只创建两个线程,已解决过安全问题,而且得到的结果是存一个打一个。

  1. /* 
  2. 线程间通信 
  3. 这个小程序,创建了两个线程,一个线程负责设置名字,另一个线程负责打印名字。 
  4. */  
  5. class Resource  
  6. {  
  7.     private String name,sex;  
  8.     private boolean flag;//定义一个标记;  
  9.     public synchronized void setName(String name,String sex)  
  10.     {  
  11.         if(flag)//如果这个标记为真  
  12.             try{this.wait();}catch(Exception e){}//让线程释放执行权;  
  13.         this.name = name;  
  14.         this.sex = sex;  
  15.         flag = true;//传完值后,把标记改为真;  
  16.         this.notify();//唤醒后,再回去判断标记久为真,那么,就会wait;  
  17.     }  
  18.     public synchronized void printName()  
  19.     {  
  20.         if(!flag)//如果这个标记为假  
  21.             try{this.wait();}catch(Exception e){}//让线程释放执行权;  
  22.         System.out.println(name+"............"+sex);//如果为假,就打印  
  23.         flag = false;//打印完,把标记改为假;  
  24.         this.notify();//唤醒另一个线程后,执行完再回去判断标记if中的条件表达式为真,就会wait;  
  25.     }  
  26. }  
  27. class Input implements Runnable  
  28. {  
  29.     private Resource res;  
  30.     Input(Resource res)  
  31.     {  
  32.         this.res = res;  
  33.     }  
  34.     public void run()  
  35.     {  
  36.         int x = 0;  
  37.         while(true)  
  38.         {  
  39.             if(x==0)  
  40.                 res.setName("BaiYun","M M");  
  41.             else  
  42.                 res.setName("黑土","G G");  
  43.             x = (x+1)%2;  
  44.         }  
  45.     }  
  46. }  
  47. class Output implements Runnable  
  48. {  
  49.     private Resource res;  
  50.     Output(Resource res)  
  51.     {  
  52.         this.res = res;  
  53.     }  
  54.     public void run()  
  55.     {  
  56.         while(true)  
  57.         {  
  58.             res.printName();  
  59.         }  
  60.     }  
  61. }  
  62. class InputOutputDemo  
  63. {  
  64.     public static void main(String[] args)  
  65.     {  
  66.         Resource res = new Resource();  
  67.         new Thread(new Input(res)).start();//开启并执行线程  
  68.         new Thread(new Output(res)).start();  
  69.     }  
  70. }  


结果是:


    

  

    在操作同一种资源时,对于不同的处理方式,可以用多线程来完成,为了让两个线程交替执行,可以定义一个标记,在一个线程执行完后,让该线程进入等待状态,同时,唤醒其他在等待中的线程,这就是我理解的等待唤醒机制。

    等待中的线程临时存放在线程池中,当我们notify的时候,唤醒的是线程池中的线程,如果有多个线程在线程池中,通常先唤醒第一个在等待的,如果要唤醒很多,可以用notifyAll()方法。

    wait和notify方法,是从Object中继承的方法,全都用在同步中,这时必须要指出wait所操作的那个线程所属的锁,wait方法会抛出异常。

    为什么这些操作线程的方法都定义在Object类中?

    因为这些方法在操作同步中线程时,都必须要标识他们所操作线程持有的那个锁,只有同一个锁上的被等待县城可以被一个锁上的notify唤醒,也就是说,等待和唤醒必须是同一个锁,而锁可以是任意对象,所以可以被任意对象所调用的方法定义在Object类中。

    生产者消费者

    对于多个生产者,消费者,在多线程运行时,出现的问题,我们通过下面的代码进行说明,网页上的代码写太多注释,有时候会出问题,所以,我把分析写在后边。

  1. class Resource  
  2. {  
  3.     private String name;  
  4.     private int count = 1;  
  5.     private boolean flag;  
  6.     public synchronized void set(String name)  
  7.     {  
  8.         if(flag)//这里用的是if语句来判断标记;  
  9.             try{wait();}catch(Exception e){}//t1,t2  
  10.         this.name = name+"-----"+count++;  
  11.         System.out.println(Thread.currentThread().getName()+"....生产者......"+this.name);  
  12.         flag = true;  
  13.         this.notify();//这里只唤醒了一个线程;  
  14.     }  
  15.     public synchronized void out()  
  16.     {  
  17.         if(!flag)  
  18.             try{wait();}catch(Exception e){}//t3,t4  
  19.         System.out.println(Thread.currentThread().getName()+"..........消费者.........."+this.name);  
  20.         flag = false;  
  21.         this.notify();  
  22.     }  
  23. }  
  24. class Pro implements Runnable  
  25. {  
  26.     private Resource res;  
  27.     Pro(Resource res)  
  28.     {  
  29.         this.res = res;  
  30.     }  
  31.     public void run()  
  32.     {  
  33.         while(true)  
  34.         {  
  35.             res.set("+商品+");  
  36.         }  
  37.     }  
  38. }  
  39. class Consumer implements Runnable  
  40. {  
  41.     private Resource res;  
  42.     Consumer(Resource res)  
  43.     {  
  44.         this.res = res;  
  45.     }  
  46.     public void run()  
  47.     {  
  48.         while(true)  
  49.         {  
  50.             res.out();  
  51.         }  
  52.     }  
  53. }  
  54. class ProCon  
  55. {  
  56.     public static void main(String[] args)  
  57.     {  
  58.         Resource res = new Resource();  
  59.         Pro pro = new Pro(res);  
  60.         Consumer con = new Consumer(res);  
  61.         Thread t1 = new Thread(pro);//我们创建四个线程来说明问题;  
  62.         Thread t2 = new Thread(pro);  
  63.         Thread t3 = new Thread(con);  
  64.         Thread t4 = new Thread(con);  
  65.         t1.start();  
  66.         t2.start();  
  67.         t3.start();  
  68.         t4.start();  
  69.     }  
  70. }  


运行后出现了这样的问题:


    


    

    出现了生产一个商品却被消费了两次的情况,当然,也有可能出现生产两次,消费一次的情况,这是为什么呢?

    分析:t1、t2是生产者线程,t3、t4是消费者线程,我们假设t1先获取到执行权,这时标记为false,t1向下执行,生产一个商品,然后标记被设置为true,回来再读标记,t1进入等待状态,释放了执行权;现在t2、t3、t4都有可能获取到执行权,假设t2获取到了执行权,标记为true,它也进入等待状态,释放了执行权;接下来t3、t4中有一个获取到执行权,执行后消费了一次,标记被设置为假,t3进入等待状态,释放执行权;然后t3唤醒了t1,t1生产一个商品后,并没有去唤醒消费者线程,把t2唤醒了,这样,虽然标记已经被t1设置为true,t2它已经判断过标记,被唤醒后直接向下执行,所以又会生产一个商品,于是就出现了生产两次,消费一次的情况,原因就是t1在执行完后,唤醒了本方的线程。

    对于这种问题我们应该怎么处理呢?

    发生这个现象就是因为没有循环判断标记,所以,可以用while循环去循环判断标记,但是,新的问题出现了,while循环判断标记可能会造成所有线程都陷入等待状态,如图:

    

 

    那么,我们就需要在线程释放执行权之前,将其他线程都唤醒,可以使用notifyAll()方法,这样问题就解决了。

    然而,在开发中这么做是比较麻烦的,所以我们就想:能不能每次唤醒只唤醒对方的线程呢?答案是可以,java在JDK1.5之后给我们提供了 一些新特性,在java.util.concurrent.locks包中,有Condition和Lock接口,还有ReenTrantLock类给我们提供了方法,Lock替代了synchronized方法和语句的使用,Condition替代了 Object监视器方法的使用。接下来,我们就来使用这些新特性,将生产者消费者的代码重新体现。

  1. import java.util.concurrent.locks.*;//包就不一个一个导了  
  2. class Res  
  3. {  
  4.     private String name;  
  5.     private int count;  
  6.     boolean flag;  
  7.     private Lock lock = new ReentrantLock();//ReentrantLock是非抽象的,要建立它的对象,调用这些方法。  
  8.     private Condition condition_pro = lock.newCondition();//获取condition对象,一个锁上可以有多个相关的condition;  
  9.     private Condition condition_con = lock.newCondition();//获取condition对象  
  10.     public void set(String name)throws InterruptedException  
  11.     {  
  12.         lock.lock();//synchronized同步换成了两个方法,拿锁,释放锁;  
  13.         try  
  14.         {  
  15.             while(flag)  
  16.                 condition_pro.await();//wait换成了await方法  
  17.             this.name = name+"--"+count++;  
  18.             System.out.println(Thread.currentThread().getName()+"......生产者......"+this.name);  
  19.             flag = true;  
  20.             condition_con.signal();//notify换成了signal方法;  
  21.         }  
  22.         finally  
  23.         {  
  24.             lock.unlock();//释放锁的动作一定要做;这里没有catch处理,下边是一样的;  
  25.         }  
  26.           
  27.     }  
  28.     public void out()throws InterruptedException  
  29.     {  
  30.         lock.lock();  
  31.         try  
  32.         {  
  33.             while(!flag)  
  34.                 condition_con.await();  
  35.             System.out.println(Thread.currentThread().getName()+"............消费者..........."+this.name);  
  36.             flag = false;  
  37.             condition_pro.signal();   
  38.         }  
  39.         finally  
  40.         {  
  41.             lock.unlock();  
  42.         }     
  43.     }  
  44. }  
  45. class Producer implements Runnable  
  46. {  
  47.     private Res r;  
  48.     Producer(Res r)  
  49.     {  
  50.         this.r = r;  
  51.     }  
  52.     public void run()  
  53.     {  
  54.         while(true)  
  55.             try  
  56.             {  
  57.                 r.set("+商品+");//run方法里只能try  
  58.             }  
  59.             catch (InterruptedException e)  
  60.             {  
  61.                 e.printStackTrace();  
  62.             }  
  63.     }  
  64. }  
  65. class Consumer implements Runnable  
  66. {  
  67.     private Res r;  
  68.     Consumer(Res r)  
  69.     {  
  70.         this.r = r;  
  71.     }  
  72.     public void run()  
  73.     {  
  74.         while(true)  
  75.             try  
  76.             {  
  77.                 r.out();  
  78.             }  
  79.             catch (InterruptedException e)  
  80.             {  
  81.                 e.printStackTrace();  
  82.             }  
  83.               
  84.     }  
  85. }  
  86. class ProConDemo  
  87. {  
  88.     public static void main(String[] args)  
  89.     {  
  90.         Res r = new Res();  
  91.         Producer pro = new Producer(r);  
  92.         Consumer con = new Consumer(r);  
  93.         Thread t1 = new Thread(pro);  
  94.         Thread t2 = new Thread(pro);  
  95.         Thread t3 = new Thread(con);  
  96.         Thread t4 = new Thread(con);  
  97.         t1.start();  
  98.         t2.start();  
  99.         t3.start();  
  100.         t4.start();  
  101.     }  
  102. }  


运行后的结果:


    


    在jdk1.5版本后提供了显示的锁机制,以及显示的锁对象上的等待唤醒操作机制。
   

    Lock:替代了Synchronized。
    lock():上锁;

    unlock():释放锁;

    newCondition():获取Condition对象,一个锁可以对应多个Condition,而以前一个wait只能对应一个notify;
   

    Condition:替代了Object中的wait、notify、notifyAll。
    await():相当于wait;

    signal():相当于notify;

    signalAll():相当于notifyAll。

    

    停止线程

    因为stop方法已过时,那么我们想要停止线程要通过什么方法呢?

    只有一种,那就是等run方法结束,开启线程运行,运行代码通常都是循环结构,只要控制住循环,就可以让run方法结束,也就是线程结束。

    步骤:1,定义循环标记;

          2,使用interrupt中断方法,该方法能结束现成的冻结状态,使线程回到运行状态。

    特殊情况:当线程处于冻结状态,就不会读取到标记,那么线程就不会结束,这是,当没有指定的方式让冻结的线程恢复到运行状态是,需要对冻结状态进行清除,强制让线程恢复到运行状态中来,这样就可以让线程结束,Thread类提供了该方法:interrupt方法,中断线程。

  1. class StopThread implements Runnable  
  2. {  
  3.     private boolean flag =true;  
  4.     public  synchronized void run()  
  5.     {  
  6.         while(flag)  
  7.         {  
  8.             try  
  9.             {  
  10.                 wait();  
  11.             }  
  12.             catch (InterruptedException e)  
  13.             {  
  14.                 System.out.println(Thread.currentThread().getName()+"..........Exception");  
  15.                 flag = false;//只要捕捉到异常,说明就有人在强制清除冻结状态;  
  16.             }  
  17.               
  18.             System.out.println(Thread.currentThread().getName()+"....run");  
  19.         }  
  20.     }  
  21.     public void changeFlag()//这个方法就不再需要了;  
  22.     {  
  23.         flag = false;  
  24.     }  
  25. }  
  26. class  StopThreadDemo  
  27. {  
  28.     public static void main(String[] args)   
  29.     {  
  30.         StopThread st = new StopThread();     
  31.         Thread t1 = new Thread(st);  
  32.         Thread t2 = new Thread(st);  
  33.         t1.start();  
  34.         t2.start();  
  35.         int num = 0;  
  36.         while(true)  
  37.         {  
  38.             if(num++ == 60)  
  39.             {  
  40.                 //st.changeFlag();  
  41.                 t1.interrupt();  
  42.                 t2.interrupt();  
  43.                 break;  
  44.             }  
  45.             System.out.println(Thread.currentThread().getName()+"......."+num);  
  46.         }  
  47.         System.out.println("over");  
  48.     }  
  49. }  


得到的结果是:


    


    注意:interrupt方法只是将冻结状态线程唤醒,如果没有冻结状态的线程,这个方法是没作用的。    

    

    守护线程 

    特点:1,当正在运行的线程都是守护线程时,java虚拟机退出;

          2,该方法必须在县城启动前调用 。

    setDemon(boolean):如果参数是true,则将该线程设置为守护线程。

    当所有的前台线程都结束,后台线程会自动结束,后台依赖于前台,开启运行都和前台线程没区别,结束时有区别。

    应用:比如有一个县城依赖于另一个线程,另外一个线程的数据如果不在运算了,这个线程存在是没意义的,输入线程不输入了,输出线程就不用输出了。

    

    join方法

    等待该线程终止。

    特点:当A线程执行到了B线程的join方法时,那么A线程就会等待,等B线程都执行完,A线程才会执行,join可以用来临时加入线程执行。


    优先级代表抢资源的频率,所有线程的默认优先级是5,包括主线程。

    setPriority(int num) :设置优先级。


    yeild方法:暂停当前正在执行的线程对象,并执行其他线程,就是临时释放一下执行权,稍微减缓一下线程执行的频率。

    










本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
一道关于java线程的面试题
Java并发入门原理教程(一)
Java 多线程总结
java并发(四)如何创建并运行java线程
多线程实现方式
jdk源码剖析三:锁Synchronized
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服