zoukankan      html  css  js  c++  java
  • Java基础---多线程

    一、多线程概述

            要理解多线程,就必须理解线程。而要理解线程,就必须知道进程。

    1、 进程

            是一个正在执行的程序。

            每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元。

    2、线程

             就是进程中的一个独立的控制单元。线程在控制着进程的执行。只要进程中有一个线程在执行,进程就不会结束。

            一个进程中至少有一个线程。

    3、多线程

            在java虚拟机启动的时候会有一个java.exe的执行程序,也就是一个进程。该进程中至少有一个线程负责java程序的执行。而且这个线程运行的代码存在于main方法中。该线程称之为主线程。JVM启动除了执行一个主线程,还有负责垃圾回收机制的线程。像种在一个进程中有多个线程执行的方式,就叫做多线程。

    4、多线程存在的意义

            多线程的出现能让程序产生同时运行效果。可以提高程序执行效率。

             例如:在java.exe进程执行主线程时,如果程序代码特别多,在堆内存中产生了很多对象,而同时对象调用完后,就成了垃圾。如果垃圾过多就有可能是堆内存出现内存不足的现象,只是如果只有一个线程工作的话,程序的执行将会很低效。而如果有另一个线程帮助处理的话,如垃圾回收机制线程来帮助回收垃圾的话,程序的运行将变得更有效率。

    5、计算机CPU的运行原理

             我们电脑上有很多的程序在同时进行,就好像cpu在同时处理这所以程序一样。但是,在一个时刻,单核的cpu只能运行一个程序。而我们看到的同时运行效果,只是cpu在多个进程间做着快速切换动作。

             而cpu执行哪个程序,是毫无规律性的。这也是多线程的一个特性:随机性。哪个线程被cpu执行,或者说抢到了cpu的执行权,哪个线程就执行。而cpu不会只执行一个,当执行一个一会后,又会去执行另一个,或者说另一个抢走了cpu的执行权。至于究竟是怎么样执行的,只能由cpu决定。

     

    二、创建线程的方式

            创建线程共有两种方式:继承方式和实现方式(简单的说)。

    1、 继承方式

            通过查找java的帮助文档API,我们发现java中已经提供了对线程这类事物的描述的类——Thread类。这第一种方式就是通过继承Thread类,然后复写其run方法的方式来创建线程。

    创建步骤:

            a,定义类继承Thread。

            b,复写Thread中的run方法。

                 目的:将自定义代码存储在run方法中,让线程运行。

            c,创建定义类的实例对象。相当于创建一个线程。

            d,用该对象调用线程的start方法。该方法的作用是:启动线程,调用run方法。

    注:如果对象直接调用run方法,等同于只有一个线程在执行,自定义的线程并没有启动。

    覆盖run方法的原因:

            Thread类用于描述线程。该类就定义了一个功能,用于存储线程要执行的代码。该存储功能就run方法。也就是说,Thread类中的run方法,用于存储线程要运行的代码。

    程序示例:

    [java] view plaincopy
     
    1. /* 
    2. 小练习 
    3. 创建两线程,和主线程交替运行。 
    4. */  
    5.   
    6. //创建线程Test  
    7. class Test extends Thread  
    8. {  
    9. //  private String name;  
    10.     Test(String name)  
    11.     {  
    12.         super(name);  
    13. //      this.name=name;  
    14.     }  
    15.     //复写run方法  
    16.     public void run()  
    17.     {  
    18.         for(int x=0;x<60;x++)  
    19.         System.out.println(Thread.currentThread().getName()+"..run..."+x);  
    20. //      System.out.println(this.getName()+"..run..."+x);  
    21.     }  
    22. }  
    23.   
    24. class  ThreadTest  
    25. {  
    26.     public static void main(String[] args)   
    27.     {  
    28.         new Test("one+++").start();//开启一个线程  
    29.   
    30.         new Test("tow———").start();//开启第二线程  
    31.   
    32.         //主线程执行的代码  
    33.         for(int x=0;x<170;x++)  
    34.         System.out.println("Hello World!");  
    35.     }  
    36. }  

    结果:

          如图,执行是随机、交替执行的,每一次运行的结果都会不同。

           

    2、 实现方式

            使用继承方式有一个弊端,那就是如果该类本来就继承了其他父类,那么就无法通过Thread类来创建线程了。这样就有了第二种创建线程的方式:实现Runnable接口,并复习其中run方法的方式。

    创建步骤:

            a,定义类实现Runnable的接口。

            b,覆盖Runnable接口中的run方法。目的也是为了将线程要运行的代码存放在该run方法中。

            c,通过Thread类创建线程对象。

            d,将Runnable接口的子类对象作为实参传递给Thread类的构造方法。

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

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

            e,调用Thread类中start方法启动线程。start方法会自动调用Runnable接口子类的run方法。

    实现方式好处:避免了单继承的局限性。在定义线程时,建议使用实现方式。 

    程序示例:

    [java] view plaincopy
     
    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.                 //显示线程名及余票数  
    15.                 System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);  
    16.             }  
    17.         }  
    18.     }  
    19. }  
    20.   
    21.   
    22. class  TicketDemo  
    23. {  
    24.     public static void main(String[] args)   
    25.     {  
    26.         //创建Runnable接口子类的实例对象  
    27.         Ticket t = new Ticket();  
    28.   
    29.         //有多个窗口在同时卖票,这里用四个线程表示  
    30.         Thread t1 = new Thread(t);//创建了一个线程  
    31.         Thread t2 = new Thread(t);  
    32.         Thread t3 = new Thread(t);  
    33.         Thread t4 = new Thread(t);  
    34.   
    35.         t1.start();//启动线程  
    36.         t2.start();  
    37.         t3.start();  
    38.         t4.start();  
    39.     }  
    40. }  

     

    三、两种方式的区别和线程的几种状态

    1、两种创建方式的区别

            继承Thread:线程代码存放在Thread子类run方法中伤感的句子

            实现Runnable:线程代码存放在接口子类run方法中。      

    2、几种状态

            被创建:等待启动,调用start启动。

             运行状态:具有执行资格和执行权。

             临时状态(阻塞):有执行资格,淘宝开店教程但是没有执行权。

             冻结状态:遇到sleep(time)方法和wait()方法时,失去执行资格和执行权,sleep方法时间到或者调用notify()方法时,获得执行资格,变为临时状态。

             消忙状态:stop()方法,或者run方法结束。

    注:当已经从创建状态到了运行状态,再次调用start()方法时,就失去意义了,java运行时会提示线程状态异常。

    图解:

       

    四、线程安全问题

    1、导致安全问题的出现的原因:

            当多条语句在操作同一线程共享数据时,一个线程对多条语句只执行了一部分,还没用执行完,另一个线程参与进来执行。导致共享数据的错误。

    简单的说就两点:

            a、多个线程访问出现延迟。

            b、线程随机性    。

    注:线程安全问题在理想状态下,不容易出现,但一旦出现对软件的影响是非常大的。

    2、解决办法——同步

            对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。

            在java中对于多线程的安全问题提供了专业的解决方式——synchronized(同步)

            这里也有两种解决方式,一种是同步代码块,还有就是同步函数。都是利用关键字synchronized来实现。

             a、同步代码块

            用法:

                      synchronized(对象)

                      {需要被同步的代码}

            同步可以解决安全问题的根本原因就在那个对象上。其中对象如同锁。持有锁的线程可以在同步中执行。没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。

    示例:

    [java] view plaincopy
     
    1. /*   
    2. 给卖票程序示例加上同步代码块。 
    3. */  
    4. class Ticket implements Runnable  
    5. {  
    6.     private int tick=100;  
    7.     Object obj = new Object();  
    8.     public void run()  
    9.     {  
    10.         while(true)  
    11.         {  
    12.             //给程序加同步,即锁  
    13.             synchronized(obj)  
    14.             {  
    15.                 if(tick>0)  
    16.                 {  
    17.                     try  
    18.                     {     
    19.                         //使用线程中的sleep方法,模拟线程出现的安全问题  
    20.                         //因为sleep方法有异常声明,所以这里要对其进行处理  
    21.                         Thread.sleep(10);  
    22.                     }  
    23.                     catch (Exception e)  
    24.                     {  
    25.                     }  
    26.                     //显示线程名及余票数  
    27.                     System.out.println(Thread.currentThread().getName()+"..tick="+tick--);  
    28.                 }  
    29.             }     
    30.         }  
    31.     }  
    32. }  

            b,同步函数

            格式:

                    在函数上加上synchronized修饰符即可。

            那么同步函数用的是哪一个锁呢?

            函数需要被对象调用。那么函数都有一个所属对象引用。就是this。所以同步函数使用的锁是this。

    拿同步代码块的示例:

    [java] view plaincopy
     
    1. class Ticket implements Runnable  
    2. {  
    3.     private int tick=100;  
    4.     Object obj = new Object();  
    5.     public void run()  
    6.     {  
    7.         while(true)  
    8.         {  
    9.             show();  
    10.         }  
    11.     }  
    12.   //直接在函数上用synchronized修饰即可实现同步  
    13. public synchronized void show()  
    14. {  
    15.         if(tick>0)  
    16.         {  
    17.         try  
    18.         {     
    19.             //使用线程中的sleep方法,模拟线程出现的安全问题  
    20.             //因为sleep方法有异常声明,所以这里要对其进行处理  
    21.             Thread.sleep(10);  
    22.         }  
    23.         catch (Exception e)  
    24.         {  
    25.         }  
    26.         //显示线程名及余票数  
    27.         System.out.println(Thread.currentThread().getName()+"..tick="+tick--);  
    28.     }  
    29. }     
    30. }  

    3、同步的前提

            a,必须要有两个或者两个以上的线程。

            b,必须是多个线程使用同一个锁。

    4、同步的利弊

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

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

    5、如何寻找多线程中的安全问题

            a,明确哪些代码是多线程运行代码。

            b,明确共享数据。

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

     

    五、静态函数的同步方式

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

            通过验证,发现不在是this。因为静态方法中也不可以定义this。静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。如:

            类名.class 该对象的类型是Class

    这就是静态函数所使用的锁。而静态的同步方法,使用的锁是该方法所在类的字节码文件对象。类名.class

    经典示例:

    [java] view plaincopy
     
    1. /* 
    2. 加同步的单例设计模式————懒汉式 
    3. */  
    4. class Single  
    5. {  
    6.     private static Single s = null;  
    7.     private Single(){}  
    8.     public static void getInstance()  
    9.     {  
    10.         if(s==null)  
    11.         {  
    12.             synchronized(Single.class)  
    13.             {  
    14.                 if(s==null)  
    15.                     s = new Single();  
    16.             }  
    17.         }  
    18.         return s;  
    19.     }  
    20. }  

     

    六、死锁

            当同步中嵌套同步时,就有可能出现死锁现象。

    示例:

    [java] view plaincopy
     
    1. /* 
    2. 写一个死锁程序 
    3. */  
    4.   
    5. //定义一个类来实现Runnable,并复写run方法  
    6. class LockTest implements Runnable  
    7. {  
    8.     private boolean flag;  
    9.     LockTest(boolean flag)  
    10.     {  
    11.         this.flag=flag;  
    12.     }  
    13.     public void run()  
    14.     {  
    15.         if(flag)  
    16.         {  
    17.             while(true)  
    18.             {  
    19.                 synchronized(LockClass.locka)//a锁  
    20.                 {  
    21.                     System.out.println(Thread.currentThread().getName()+"------if_locka");  
    22.   
    23.                     synchronized(LockClass.lockb)//b锁  
    24.                     {  
    25.                     System.out.println(Thread.currentThread().getName()+"------if_lockb");  
    26.                     }  
    27.                 }  
    28.             }  
    29.         }  
    30.         else  
    31.         {  
    32.             while(true)  
    33.             {  
    34.                 synchronized(LockClass.lockb)//b锁  
    35.                 {  
    36.                   System.out.println(Thread.currentThread().getName()+"------else_lockb");  
    37.   
    38.                     synchronized(LockClass.locka)//a锁  
    39.                     {  
    40.                    System.out.println(Thread.currentThread().getName()+"------else_locka");  
    41.                     }  
    42.                 }  
    43.             }  
    44.         }  
    45.     }  
    46. }  
    47.   
    48. //定义两个锁  
    49. class LockClass  
    50. {  
    51.     static Object locka = new Object();  
    52.     static Object lockb = new Object();  
    53. }  
    54.   
    55. class DeadLock  
    56. {  
    57.     public static void main(String[] args)  
    58.     {  
    59.         //创建2个进程,并启动  
    60.         new Thread(new LockTest(true)).start();  
    61.         new Thread(new LockTest(false)).start();  
    62.     }  
    63. }  

    结果:程序卡住,不能继续执行

           

     

    七、线程间通信

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

    1、使用同步操作同一资源的示例:

    [java] view plaincopy
     
    1. /* 
    2.     有一个资源 
    3. 一个线程往里存东西,如果里边没有的话 
    4. 一个线程往里取东西,如果里面有得话 
    5. */  
    6.   
    7. //资源  
    8. class Resource  
    9. {  
    10.     private String name;  
    11.     private String sex;  
    12.     private boolean flag=false;  
    13.       
    14.     public synchronized void setInput(String name,String sex)  
    15.     {  
    16.         if(flag)  
    17.         {  
    18.             try{wait();}catch(Exception e){}//如果有资源时,等待资源取出  
    19.         }  
    20.         this.name=name;  
    21.         this.sex=sex;  
    22.   
    23.         flag=true;//表示有资源  
    24.         notify();//唤醒等待  
    25.     }  
    26.   
    27.     public synchronized void getOutput()  
    28.     {         
    29.         if(!flag)  
    30.         {  
    31.             try{wait();}catch(Exception e){}//如果木有资源,等待存入资源  
    32.         }  
    33.         System.out.println("name="+name+"---sex="+sex);//这里用打印表示取出  
    34.                   
    35.         flag=false;//资源已取出  
    36.         notify();//唤醒等待  
    37.     }  
    38. }  
    39.   
    40.   
    41. //存线程  
    42. class Input implements Runnable  
    43. {  
    44.     private Resource r;  
    45.     Input(Resource r)  
    46.     {  
    47.         this.r=r;  
    48.     }  
    49.     public void run()//复写run方法  
    50.     {  
    51.         int x=0;  
    52.         while(true)  
    53.         {  
    54.             if(x==0)//交替打印张三和王羲之  
    55.             {  
    56.                 r.setInput("张三",".....man");  
    57.             }  
    58.             else  
    59.             {  
    60.                 r.setInput("王羲之","..woman");  
    61.             }  
    62.             x=(x+1)%2;//控制交替打印  
    63.         }  
    64.     }  
    65. }  
    66.   
    67. //取线程  
    68. class Output implements Runnable  
    69. {  
    70.     private Resource r;  
    71.     Output(Resource r)  
    72.     {  
    73.         this.r=r;  
    74.     }  
    75.     public void run()//复写run方法  
    76.     {  
    77.         while(true)  
    78.         {  
    79.             r.getOutput();  
    80.         }  
    81.     }  
    82. }  
    83.   
    84.   
    85.   
    86. class ResourceDemo2   
    87. {  
    88.     public static void main(String[] args)   
    89.     {  
    90.         Resource r = new Resource();//表示操作的是同一个资源  
    91.   
    92.         new Thread(new Input(r)).start();//开启存线程  
    93.   
    94.         new Thread(new Output(r)).start();//开启取线程  
    95.     }  
    96. }  

    结果:部分截图

     几个小问题:

            1)wait(),notify(),notifyAll(),用来操作线程为什么定义在了Object类中?

                    a,这些方法存在与同步中。

                    b,使用这些方法时必须要标识所属的同步的锁。同一个锁上wait的线程,只可以被同一个锁上的notify唤醒。

                    c,锁可以是任意对象,所以任意对象调用的方法一定定义Object类中。

            2)wait(),sleep()有什么区别?

                  wait():释放cpu执行权,释放锁。

                  sleep():释放cpu执行权,不释放锁。

            3)为甚么要定义notifyAll?

            因为在需要唤醒对方线程时。如果只用notify,容易出现只唤醒本方线程的情况。导致程序中的所以线程都等待。

    2、JDK1.5中提供了多线程升级解决方案。

            将同步synchronized替换成显示的Lock操作。将Object中wait,notify,notifyAll,替换成了Condition对象。该Condition对象可以通过Lock锁进行获取,并支持多个相关的Condition对象。

    升级解决方案的示例:

    [java] view plaincopy
     
    1. /* 
    2. 生产者生产商品,供消费者使用 
    3. 有两个或者多个生产者,生产一次就等待消费一次 
    4. 有两个或者多个消费者,等待生产者生产一次就消费掉 
    5.  
    6. */  
    7.   
    8. import java.util.concurrent.locks.*;  
    9.   
    10. class Resource   
    11. {     
    12.     private String name;  
    13.     private int count=1;  
    14.     private boolean flag = false;  
    15.       
    16.     //多态  
    17.     private Lock lock=new ReentrantLock();  
    18.   
    19.     //创建两Condition对象,分别来控制等待或唤醒本方和对方线程  
    20.     Condition condition_pro=lock.newCondition();  
    21.     Condition condition_con=lock.newCondition();  
    22.   
    23.     //p1、p2共享此方法  
    24.     public void setProducer(String name)throws InterruptedException  
    25.     {  
    26.         lock.lock();//锁  
    27.         try  
    28.         {  
    29.             while(flag)//重复判断标识,确认是否生产  
    30.                 condition_pro.await();//本方等待  
    31.   
    32.             this.name=name+"......"+count++;//生产  
    33.             System.out.println(Thread.currentThread().getName()+"...生产..."+this.name);//打印生产  
    34.             flag=true;//控制生产消费标识  
    35.             condition_con.signal();//唤醒对方  
    36.         }  
    37.         finally  
    38.         {  
    39.             lock.unlock();//解锁,这个动作一定执行  
    40.         }  
    41.           
    42.     }  
    43.   
    44.     //c1、c2共享此方法  
    45.     public void getConsumer()throws InterruptedException  
    46.     {  
    47.         lock.lock();  
    48.         try  
    49.         {  
    50.             while(!flag)//重复判断标识,确认是否可以消费  
    51.                 condition_con.await();  
    52.   
    53.             System.out.println(Thread.currentThread().getName()+".消费."+this.name);//打印消费  
    54.             flag=false;//控制生产消费标识  
    55.             condition_pro.signal();  
    56.         }  
    57.         finally  
    58.         {  
    59.             lock.unlock();  
    60.         }  
    61.   
    62.     }  
    63. }  
    64.   
    65. //生产者线程  
    66. class Producer implements Runnable   
    67. {  
    68.     private Resource res;  
    69.     Producer(Resource res)  
    70.     {  
    71.         this.res=res;  
    72.     }  
    73.     //复写run方法  
    74.     public void run()  
    75.     {  
    76.         while(true)  
    77.         {  
    78.             try  
    79.             {  
    80.                 res.setProducer("商品");  
    81.             }  
    82.             catch (InterruptedException e)  
    83.             {  
    84.             }  
    85.         }  
    86.     }  
    87. }  
    88.   
    89. //消费者线程  
    90. class Consumer implements Runnable  
    91. {  
    92.     private Resource res;  
    93.     Consumer(Resource res)  
    94.     {  
    95.         this.res=res;  
    96.     }  
    97.     //复写run  
    98.     public void run()  
    99.     {  
    100.         while(true)  
    101.         {  
    102.             try  
    103.             {  
    104.                 res.getConsumer();  
    105.             }  
    106.             catch (InterruptedException e)  
    107.             {  
    108.             }  
    109.         }  
    110.     }  
    111.   
    112. }  
    113.   
    114. class  ProducerConsumer  
    115. {  
    116.     public static void main(String[] args)   
    117.     {  
    118.         Resource res=new Resource();  
    119.   
    120.         new Thread(new Producer(res)).start();//第一个生产线程 p1  
    121.         new Thread(new Consumer(res)).start();//第一个消费线程 c1  
    122.   
    123.         new Thread(new Producer(res)).start();//第二个生产线程 p2  
    124.         new Thread(new Consumer(res)).start();//第二个消费线程 c2  
    125.     }  
    126. }  

    运行结果:部分截图

           

     

    八、停止线程

            在JDK 1.5版本之前,有stop停止线程的方法,但升级之后,此方法已经过时。

    那么现在我们该如果停止线程呢?

            只有一种办法,那就是让run方法结束。

    1、开启多线程运行,运行代码通常是循环结构。只要控制住循环,就可以让run方法结束,也就是线程结束。

          如:run方法中有如下代码,设置一个flag标记。  

    [java] view plaincopy
     
    1. public  void run()  
    2. {  
    3.     while(flag)  
    4.     {     
    5.         System.out.println(Thread.currentThread().getName()+"....run");  
    6.     }  
    7. }  

            那么只要在主函数或者其他线程中,在该线程执行一段时间后,将标记flag赋值false,该run方法就会结束,线程也就停止了。

    2、上面的1方法可以解决一般情况,但是有一种特殊情况:就是当线程处于冻结状态。就不会读取到标记。那么线程就不会结束。

            当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除。强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。Thread类提供该方法interrupt();

    如:

    [java] view plaincopy
     
    1. class StopThread implements Runnable  
    2. {  
    3.     private boolean flag =true;  
    4.     public  void run()  
    5.     {  
    6.         while(flag)  
    7.         {  
    8.             System.out.println(Thread.currentThread().getName()+"....run");  
    9.         }  
    10.     }  
    11.     public void changeFlag()  
    12.     {  
    13.         flag = false;  
    14.     }  
    15. }  
    16.   
    17. class  StopThreadDemo  
    18. {  
    19.     public static void main(String[] args)   
    20.     {  
    21.         StopThread st = new StopThread();  
    22.         Thread t1 = new Thread(st);  
    23.         Thread t2 = new Thread(st);   
    24.         t1.start();  
    25.         t2.start();   
    26.   
    27.         int num = 0;  
    28.         while(true)  
    29.         {  
    30.             if(num++ == 60)  
    31.             {  
    32.                 t1.interrupt();//清除冻结状态  
    33.                 t2.interrupt();  
    34.                 st.changeFlag();//改变循环标记  
    35.                 break;  
    36.             }  
    37.             System.out.println(Thread.currentThread().getName()+"......."+num);  
    38.         }  
    39.         System.out.println("over");  
    40.     }  
    41. }  

    结果:

           

     

    九、什么时候写多线程?

            当某些代码需要同时被执行时,就用单独的线程进行封装。

    示例:

    [java] view plaincopy
     
    1. class  ThreadTest  
    2. {  
    3.     public static void main(String[] args)   
    4.     {  
    5.         //一条线程  
    6.         new Thread()  
    7.         {  
    8.             public void run()  
    9.             {  
    10.                 for (int x=0;x<100 ;x++ )  
    11.                 {  
    12.                     System.out.println(Thread.currentThread().toString()+"....."+x);  
    13.                 }  
    14.             }  
    15.         }.start();  
    16.       
    17.         //又是一条线程  
    18.         Runnable r= new Runnable()  
    19.         {  
    20.             public void run()  
    21.             {  
    22.                 for (int x=0;x<100 ;x++ )  
    23.                 {  
    24.                     System.out.println(Thread.currentThread().getName()+"....."+x);  
    25.                 }  
    26.             }  
    27.         };  
    28.         new Thread(r).start();  
    29.           
    30.         //可以看作主线程  
    31.         for (int x=0;x<1000 ;x++ )  
    32.         {  
    33.             System.out.println("Hello World!");  
    34.         }  
    35.           
    36.     }  
    37. }  

     

    扩展小知识:

    1、join方法

            当A线程执行到了b线程的.join()方法时,A线程就会等待,等B线程都执行完,A线程才会执行。(此时B和其他线程交替运行。)join可以用来临时加入线程执行。

    2、setPriority()方法用来设置优先级

            MAX_PRIORITY 最高优先级10

            MIN_PRIORITY   最低优先级1

            NORM_PRIORITY 分配给线程的默认优先级

    3、yield()方法可以暂停当前线程,让其他线程执行。

  • 相关阅读:
    tracteroute路由追踪
    搭建Weblogic服务器
    Logview_pro破解版
    Spring Boot 如何在类中应用配置文件
    使用Mybatis-Generator自动生成Dao、Model、Mapping相关文件(转)
    springboot 项目中控制台打印日志以及每天生成日志文件
    springboot输出日志到指定目录,简单粗暴,springboot输出mybatis日志
    spring boot 发布成包所需插件
    spring注解
    Multicast注册中心
  • 原文地址:https://www.cnblogs.com/tiankong101/p/4227077.html
Copyright © 2011-2022 走看看