Java_多线程

一 Java多线程

1.概念

进程:是一个正在执行的程序,每一个进程执行都会有一个执行顺序,该顺序是一个执行路径,或者叫控制单元,一个进程可以有多个线程。

线程:是进程的一个独立的控制单元,线程在控制着进程的执行。

线程从宏观上来说是串行执行,从微观上来说是并行执行。

2.Java中多线程

jvm在运行时会有一个java.exe进程,该程序中至少有一个线程是负责Java程序的执行。而且这个线程的运行代码存在于main代码块中。该线程称为主线程。其实jvm不止一个线程,还有一个线程,负责垃圾回收机制。

3.多线程特性

随机性;谁抢到谁执行,执行多长时间,cup说的算在没有人为控制的情况下。

二  创建线程

创建线程有两种方式:继承Thread和实现Runnable接口。

1.继承Thread类

创建步骤:

(1)定义类继承thread类。

(2)重写thread类中的run方法。

(3)调用线程的strat方法。该方法有俩个动作:启动线程,调用run方法。

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

实例:有两个卖票窗口,卖不同的票。

[java]  view plain  copy
  1. class ThreadDemo extends Thread  
  2. {  
  3.       ThreadDemo(Stringname){  
  4.            super(name);  
  5.       }  
  6.       //覆盖run方法,  
  7.       publicvoid run(){  
  8.            inttick=100;  
  9.            while(tick>0){  
  10.                  System.out.println(Thread.currentThread().getName()+"..."+tick);  
  11.                  tick--;  
  12.                  }  
  13.       }     
  14. }  
  15. public class Test1  
  16. {  
  17.       publicstatic void main(String[] args){  
  18.            ThreadDemot1=new ThreadDemo("老人票窗口");  
  19.            ThreadDemot2=new ThreadDemo("小孩票窗口");  
  20.            t1.start();  
  21.            t2.start();  
  22.            }  
  23. }  


第二种写法:使用内部匿名类

不创建类继承Thread,而是直接通过匿名内部类的特点直接在内部创建一个线程实例。该方法只能是线程中run方法逻辑比较简单的,否则代码看起来容易臃肿不易查看。

示例代码如下:

[java]  view plain  copy
  1. public class ThreadJCTest {  
  2.      public static void createThreadByNestClass(){  
  3.          Thread th= new Thread(){//匿名内部类  
  4.                public void run(){  
  5.                     for (int x=0;x<10;x++){  
  6.                         System. err.println(Thread.currentThread().getName()+ "..." +x);  
  7.                    }  
  8.               }  
  9.          };  
  10.          th.start();  
  11.     }  
  12.      public static void main(String[] args) {  
  13.           createThreadByNestClass();  
  14.     }  
  15. }  


2.实现runable接口

分析步骤:

(1)定义类实现runable接口。

(2)覆盖runable接口中的run方法。

       目的: 将线程要运行的代码放在run方法中。

(3)通过thread类创建线程对象。

(4)将runable接口的子类对象作为实际参数传递给thread类的构造方法。

        目的: 自定义的run方法的所属的对象是runable接口的子类对象。所以要让线程指定哪个对象的run方法就必须明确该run方法的所属对象。

(5)调用thread类中的start的方法,开启线程并调用runable接口子类的run方法。

//实现runnable接口方式

[java]  view plain  copy
  1. class MyRunnable implements Runnable  
  2. {  
  3.        //覆盖run方法,  
  4.        private  int tick=100;  
  5.        publicvoid run(){  
  6.               while(true){  
  7.                      if(tick>0){  
  8.                      System.out.println(Thread.currentThread().getName()+"..."+tick--);  
  9.                      }  
  10.                      elsebreak;  
  11.               }  
  12.        }       
  13. }  
  14. public class Test1  
  15. {  
  16.        publicstatic void main(String[] args){  
  17.               MyRunnablet=new MyRunnable();  
  18.               Threadt1=new Thread(t,"小孩票窗口");  
  19.               Threadt2=new Thread(t,"老人票窗口");  
  20.               t1.start();  
  21.               t2.start();  
  22.               }  
  23. }  


3.两种创建方式区别

上述2种方式都可以创建线程,而且从示例代码上看,线程执行的功能是一样的,那么这三种创建方式有什么不同呢?

  这涉及到Java中多线程的运行模式,对于Java来说,多线程在运行时,有“多对象多线程”和“单对象多线程”的区别:

       多对象多线程,程序在运行过程中创建多个线程对象,每个对象上运行一个线程。

       单对象多线程,程序在运行过程中创建一个线程对象,在其上运行多个线程。

显然,从线程同步和调度的角度来看,多对象多线程要简单一些。上述2种线程创建方式,前一个属于“多对象多线程”,后者既可以使用“多对象多线程”,也可以使用“单对象单线程”。

 

俩种继承方式和实现方式有什么区别;

实现可以继承别的类,如果继承了thread类则无法继承别的类。

继承thread:线程运行代码存放在thread子类的run方法中。

实现runable:线程运行代码存放在实现该接口的run方法中。

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

三 线程状态及切换

线程状态总的可分为五大状态:分别是创建、运行、冻结、等待/阻塞、消亡。用一个图来描述如下:

 

运行状态:经过start方法达到运行状态。

冻结状态:经过sleep(time)方法可以到达冻结状态,经过time时间后,重新回到运行状态。

在运行时,还可以经过wait()方法冻结状态。经过notity();方法回到运行状态。

阻塞状态:具备运行资格,但没有执行权,跟运行状态互通。在冻结状态被唤醒时,不一定马上就会运行,这就是临时状态。也称为阻塞状态。

死亡状态:在运行过程中经过stop();方法可以让线程消亡,run方法执行完后也会消亡。

状态之间的切换我们会用到下面方法:

       *Thread()或者Thread(Runnable):构造线程。

       *Thread.start:启动线程。

       *Thread.sleep:将线程切换至休眠状态。

       *Thread.interrupt:中断线程的执行。

       *Thread.join:等待某线程结束。

       *Thread.yield:剥夺线程在CPU上的执行时间片,等待下一次调度。

       *Object.wait:将Object上所有线程锁定,直到notify方法才继续运行。

       *Object.notify:随机唤醒Object上的1个线程。

       *Object.notifyAll:唤醒Object上的所有线程。

 

四 线程的名称

线程都有自己的默认名称:thread-xxx,从0开始。

setName( ):设定线程名称;

getName( ); 获取线程名称;

thread.currentThread()方法,和this方法作用一样。前面是通用方法。获取当前线程对象的名称;

GroupName:每个线程都会默认在一个线程组里,我们也可以显式的创建线程组,一个线程组中也可以包含子线程组,这样线程和线程组,就构成了一个树状结构。

五 线程安全问题

线程同步,大部分情况下,我们是在针对“单对象多线程”的情况进行讨论,一般会将其分成两部分,一部分是关于“共享变量”,一部分关于“执行步骤”。

Java对于多线程的安全问题提供了专业的解决方式就是同步代码块。

安全问题如何确定:

(1)明确哪些代码是多线程运行代码。

(2)明确哪些是共享数据。

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

同步的前提:

(1)须要有俩个或者两个以上的线程。

(2)必须是多个线程使用同一个锁。

(3)必须保证同步中只能有一个线程运行。

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

1.控制共享变量

当我们在线程对象(Runnable)中定义了全局变量,run方法会修改该变量时,如果有多个线程同时使用该线程对象,那么就会造成全局变量的值被同时修改,造成错误。

实例:计算1到100的相加的值。

[java]  view plain  copy
  1. class MyRunnable implements Runnable {  
  2.      int sum=0;  
  3.      public void run(){  
  4.           for (int x=1;x<=100;x++){  
  5.                sum+=x;  
  6.                try {  
  7.                    Thread. sleep(50);  
  8.                    }  
  9.                     catch (Exception e){  
  10.                          System. err.println("错误" );  
  11.                    }  
  12.                 System. err.println(Thread.currentThread().getName()+ "..."+x);  
  13.                 System. err.println(Thread.currentThread().getName()+ "..."+ sum);  
  14.          }  
  15.           System. err.println( "线程结束" +sum );  
  16.     }  
  17. }  
  18. public class Test1  
  19. {  
  20.         public static void main(String[] args) {  
  21.          MyRunnable m= new MyRunnable();  
  22.          Thread t= new Thread(m,"线程1");  
  23.          Thread t1= new Thread(m,"线程2");  
  24.          t.start();  
  25.          t1.start();  
  26.     }  
  27. }  


正确结果是5050,输出出来确实10100,这时因为俩个线程同时对sum进行操作。

如何控制线程同步

既然线程同步有上述问题,那么我们应该如何去解决呢?针对不同原因造成的同步问题,我们可以采取不同的策略。

 

我们可以采取3种方式来控制共享变量。

(1)将“单对象多线程”修改成“多对象多线程”

   

[java]  view plain  copy
  1.   publicclass Test1  
  2. {  
  3.         public static void main(String[] args) {  
  4.          MyRunnable m= new MyRunnable();  
  5.                 MyRunnable m1= new MyRunnable();  
  6.          Thread t= new Thread(m,"线程1");  
  7.          Thread t1= new Thread(m1,"线程2");  
  8.          t.start();  
  9.          t1.start();  
  10.         }  
  11. }  

输出结果为线程1和线程2都是5050.

(2)将“全局变量”降级为“局部变量”

  既然是共享变量造成的问题,那么我们可以将共享变量改为“不共享”,即将其修改为局部变量。这样也可以解决问题,

(3)使用ThreadLocal机制

  ThreadLocal是JDK引入的一种机制,它用于解决线程间共享变量,使用ThreadLocal声明的变量,即使在线程中属于全局变量,针对每个线程来讲,这个变量也是独立的。

2.控制执行步骤

安全问题原由:当多态语句操作同一线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来,导致共享数据的错误。如多窗口卖票出现负数的票和0票。

 

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

 

Java所提供的解决办法中,synchronized是一个非常重要的关键字。它的原理和数据库中事务锁的原理类似。我们在使用过程中,应该尽量缩减synchronized覆盖的范围,原因有二:

1)被它覆盖的范围是串行的,效率低;

2)容易产生死锁。

所以我们应该把需要同步的内容集中在一起,尽量不包含其他不相关的、消耗大量资源的操作

(1)同步代码块:

格式:synchronized( 对象 ){   需要被同步的代码块 }

 

对象如同锁,持有锁的对象才能在同步代码块中执行,没有持有锁的线程即使获得cpu的执行权,也进不去。

示例:模拟卖票时出现0和负数的票。

[java]  view plain  copy
  1. class MyRunnable implements Runnable  
  2. {  
  3.        //覆盖run方法,  
  4.        private  int tick=100;  
  5.        publicvoid run(){  
  6.               while(true){  
  7.                      if(tick>0){  
  8.                      try{  
  9.                      Thread.sleep(10);  
  10.                      }  
  11.                      catch(Exceptione){  
  12.                             thrownew RuntimeException("error");  
  13.                      }  
  14.                      System.out.println(Thread.currentThread().getName()+"..."+tick--);  
  15.                      elsebreak;  
  16.                      }  
  17.               }  
  18.        }       
  19. }  
  20. public class Test1  
  21. {  
  22.        publicstatic void main(String[] args){  
  23.               MyRunnablet=new MyRunnable();  
  24.               Threadt1=new Thread(t,"小孩票窗口");  
  25.               Threadt2=new Thread(t,"老人票窗口");  
  26.               t1.start();  
  27.               t2.start();  
  28.               }  
  29. }  

输出结果中可能会出现0和负数票,这里也能体现出多线程的随机性。

解决办法:

[java]  view plain  copy
  1. class MyRunnable implements Runnable  
  2. {  
  3.        //覆盖run方法,  
  4.        private  int tick=100;  
  5.        publicvoid run(){  
  6.               while(true){  
  7.                      //同步锁  
  8.                      synchronized(this){  
  9.                      if(tick>0){  
  10.                          try{Thread.sleep(10); }catch(Exceptione){throw new RuntimeException("error");}  
  11.                         System.out.println(Thread.currentThread().getName()+"..."+tick--);  
  12.                      }  
  13.                      elsebreak;  
  14.                      }  
  15.               }  
  16.        }       
  17. }  
  18. public class Test1  
  19. {  
  20.        publicstatic void main(String[] args){  
  21.               MyRunnablet=new MyRunnable();  
  22.               Threadt1=new Thread(t,"小孩票窗口");  
  23.               Threadt2=new Thread(t,"老人票窗口");  
  24.               t1.start();  
  25.               t2.start();  
  26.               }  
  27. }  

(2)同步函数

在线程运行的同步代码中的函数上加上synchronized.

注意,必须是线程操作共享数据的函数上加入同步函数。

[java]  view plain  copy
  1. class MyRunnable implements Runnable  
  2. {  
  3.        //覆盖run方法,  
  4.        private  int tick=100;  
  5.        booleanflag=true;  
  6.        publicvoid run(){  
  7.               while(flag){  
  8.                      show();  
  9.               }  
  10.        }  
  11.        //同步函数  
  12.        publicsynchronized void show(){  
  13.               if(tick>0){  
  14.                          try{Thread.sleep(10); }catch(Exceptione){throw new RuntimeException("error");}  
  15.                         System.out.println(Thread.currentThread().getName()+"..."+tick--);  
  16.                      }  
  17.                      elseflag=false;  
  18.        }  
  19. }  
  20. public class Test1  
  21. {  
  22.        publicstatic void main(String[] args){  
  23.               MyRunnablet=new MyRunnable();  
  24.               Threadt1=new Thread(t,"小孩票窗口");  
  25.               Threadt2=new Thread(t,"老人票窗口");  
  26.               t1.start();  
  27.               t2.start();  
  28.               }  
  29. }  


注意:

在使用synchronized关键字时候,应该尽可能避免在synchronized方法或synchronized块中使用sleep或者yield方法,因为synchronized程序块占有着对象锁,你休息那么其他的线程只能一边等着你醒来执行完了才能执行。不但严重影响效率,也不合逻辑。

同样,在同步程序块内调用yeild方法让出CPU资源也没有意义,因为你占用着锁,其他互斥线程还是无法访问同步程序块。当然与同步程序块无关的线程可以获得更多的执行时间。

同步函数用的哪个锁呢?

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

 

静态同步函数用哪个锁?

因为静态方法中不可以定义this,所有不是this对象。

静态进内存时,内存中还没有对象,但是一定有该类对应的字节码文件对象,类名.class 该对象的类型是Class.

实例:单例模式。

       

[java]  view plain  copy
  1.  class Single{  
  2.     private static Single s =null;  
  3.     private Single(){  
  4.     }  
  5.     public static Single getinstends(){  
  6.           if(s ==null){//双重判断,减少资源消耗。  
  7. //存在多线程安全问题,同步代码块,代码块对象为本类对象,也可以是s.class,防止实例化多个对象。  
  8.           synchronized (s) {4  
  9. //当A程序进来停止住,B程序再进来会实例化多个对象  
  10.                if(s ==null){  
  11.                     s= new Single();  
  12.               }  
  13.          }  
  14.          }  
  15.           return s ;  
  16.     }  
  17.        }  

六 死锁

线程发生死锁可能性很小,即使看似可能发生死锁的代码,在运行时发生死锁的可能性也是小之又小。

同步中嵌套同步,且锁不一样时,容易发生死锁。

[java]  view plain  copy
  1. public class Sisuotest implements Runnable{  
  2.        booleanb=true;  
  3.        Sisuotest(booleanb){  
  4.               this.b=b;  
  5.        }  
  6.        publicvoid run (){  
  7.                      if(b){  
  8.                             synchronized(MyLock.s1) {  
  9.                             System.err.println("if--a--");  
  10.                             synchronized(MyLock.s2) {  
  11.                                           System.err.println("if--a--");  
  12.                                    }  
  13.                             }  
  14.                      }  
  15.                      else{  
  16.                             synchronized(MyLock.s2) {  
  17.                                           System.err.println("else--a--");  
  18.                                           synchronized(MyLock.s1) {  
  19.                                                         System.err.println("else--a--");  
  20.                              
  21.                      }  
  22.        }  
  23.                      }}  
  24.        publicstatic void main(String[] args) {  
  25.               Threadt=new Thread(new Sisuotest (true));  
  26.               Threadt1=new Thread(new Sisuotest (false));  
  27.               t.start();  
  28.               t1.start();  
  29.        }  
  30. }  
  31. class MyLock{  
  32.        staticMyLock s1=new MyLock();  
  33.        staticMyLock s2=new MyLock();  
  34. }  


七 线程间通信

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

示例:生产者与消费者

1.等待唤醒机制

wait(); 当前线程等待

notify(); 唤醒线程

notifyall(); 唤醒全部线程

这三个方法都使用在同步中,因为要对持有监视器(锁)的线程操作。所以要使用在同步中,只有同步才有锁。

 

为什么这些方法要定义到Object类中呢?

因为这些方法在操作同步中的线程时,都必须要标示他们所操作的线程持有的锁,只有同一个锁上的被等待的线程,才可以被同一锁上的notify唤醒。也就是所等待和唤醒必须是同一个锁。

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

等待的线程都放在线程池中,临时存放,当notify唤醒时唤醒的是线程池中的线程,线程池中有N多线程,那么要唤醒谁呢?通常唤醒第一个被等待的。

 

生产者与消费者例子:

当出现多个线程,例如多个生产者和多个消费者时,需要每次判断标记用while不能用if

如果用while也可能会出现全部等待的情况下,所以唤醒时需要全部唤醒用notifyall.

[java]  view plain  copy
  1. class Resource  
  2. {  
  3.        privateString name;  
  4.        privateint age;  
  5.        privateboolean flag=false;  
  6.        publicsynchronized void set(String name,int age){  
  7.               //如果有资源,需要等待,取出资源。  
  8.               if(flag){  
  9.                      try{wait(); }catch(Exception e){ throw new RuntimeException("等待失败"); }  
  10.               }  
  11.               this.name=name;  
  12.               this.age=age;  
  13.               flag=true;  
  14.               notify();  
  15.        }  
  16.        publicsynchronized void out(){  
  17.               //如果没有资源需要等待,存入资源。  
  18.               if(!flag){  
  19.                      try{wait(); }catch(Exception e){ throw new RuntimeException("等待失败"); }  
  20.               }  
  21.               System.out.println("name:"+name+"age"+age);  
  22.               flag=false;  
  23.               notify();  
  24.               }  
  25. }  
  26. //生产者  
  27. class Input implements Runnable  
  28. {  
  29.        privateResource r;  
  30.        Input(Resourcer){  
  31.               this.r=r;  
  32.        }  
  33.        publicvoid run(){  
  34.               intx=0;  
  35.               while(true){  
  36.                      if(x==0){  
  37.                             r.set("张三",30);  
  38.                             }  
  39.                      else  
  40.                             r.set("李四",40);  
  41.                      x=(x+1)%2;  
  42.               }  
  43.        }  
  44. }  
  45. //消费者  
  46. class Output implements Runnable  
  47. {  
  48.        privateResource r;  
  49.        Output(Resourcer){  
  50.               this.r=r;  
  51.        }  
  52.        publicvoid run(){  
  53.               while(true)  
  54.                      r.out();  
  55.        }  
  56. }  
  57. public class Test1  
  58. {  
  59.        publicstatic void main(String [] args){  
  60.               Resourcer=new Resource();  
  61.               Inputi=new Input(r);  
  62.               Outputo=new Output(r);  
  63.               newThread(i).start();  
  64.               newThread(o).start();  
  65.        }  
  66. }  


Jdk1.5中提供了多线程的解决方案。本方只唤醒对方的线程,不在全部唤醒。

将同步synchronized替换成显示的lock操作。

将object中的wait(),notify(),notifyall(),替换成condition对象。

改对象可以lock锁,进行获取。

//生产者与消费者。

[java]  view plain  copy
  1. import java.util.concurrent.locks.*;  
  2. class Resource  
  3. {  
  4.        privateString name;  
  5.        privateint count;  
  6.        privateboolean flag=false;  
  7.        privateLock lock=new ReentrantLock();  
  8.        Conditioncondition_pro=lock.newCondition();  
  9.        Conditioncondition_con=lock.newCondition();  
  10.        publicvoid setProducer(String name)throws Exception{  
  11.               lock.lock();//获取锁。  
  12.               try{  
  13.               while(flag)//判断标记  
  14.               condition_pro.await();  
  15.               this.name="..."+count++;  
  16.               System.out.println(Thread.currentThread().getName()+"...生产..."+this.name);  
  17.               flag=true;  
  18.               condition_con.signal();  
  19.               }  
  20.               finally{  
  21.                      lock.unlock();  
  22.               }  
  23.        }  
  24.        publicvoid getConsumer()throws Exception{  
  25.               try{  
  26.               lock.lock();//获取锁;  
  27.               while(!flag)//判断锁标记  
  28.               condition_con.await();//消费者等待;  
  29.                System.out.println(Thread.currentThread().getName()+".消费."+this.name);  
  30.               flag=false;  
  31.               condition_pro.signal();  
  32.               }  
  33.               finally{  
  34.                      lock.unlock();  
  35.               }  
  36.        }  
  37. }  
  38. //生产者  
  39. class Producer implements Runnable  
  40. {  
  41.        privateResource r;  
  42.        Producer(Resourcer){  
  43.               this.r=r;  
  44.        }  
  45.        publicvoid run(){  
  46.               while(true){  
  47.                      try{  
  48.                      r.setProducer("商品");  
  49.                      }  
  50.                      catch(Exceptione){  
  51.                             thrownew RuntimeException("存入错误");  
  52.                      }  
  53.               }  
  54.        }  
  55. }  
  56. //消费者  
  57. class Consumer implements Runnable  
  58. {  
  59.        privateResource r;  
  60.        Consumer(Resourcer){  
  61.               this.r=r;  
  62.        }  
  63.        publicvoid run(){  
  64.               while(true){  
  65.                      try{  
  66.                      r.getConsumer();  
  67.                      }  
  68.                      catch(Exceptione){  
  69.                             thrownew RuntimeException("存入错误");  
  70.                      }  
  71.               }  
  72.        }  
  73. }  
  74. public class Test1  
  75. {  
  76.        publicstatic void main(String[] args){  
  77.               Resourcer=new Resource();  
  78.               newThread(new Producer(r)).start();  
  79.               newThread(new Consumer(r)).start();  
  80.        }  
  81. }  

八 停止线程

stop方法已过时。

如何停止线程?

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

特殊情况:

当线程处于冻结状态,就不会读取到标记,那么线程就不会结束。

当没有指定的方式让线程从冻结状态恢复到运行状态时,这时需要对冻结状态进行清除,强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束。

Thread类提供了该方法interrupt();

[java]  view plain  copy
  1. public class StopThread implements Runnable{  
  2.      boolean flag= true ;  
  3.      public void changflag(){  
  4.           flag= false ;  
  5.     }  
  6.      public synchronized   void run() {  
  7.           // TODO Auto-generated method stub  
  8.           while (flag ){  
  9.                try {  
  10.                    wait();  
  11.               } catch (InterruptedException e){  
  12.                     // TODO Auto-generated catch block  
  13.             System. err.println(Thread.currentThread().getName()+"Exption...");  
  14.                     flag= false ;  
  15.               }  
  16.                 System. err.println(Thread.currentThread().getName()+ "run...");  
  17.           }  
  18.     }  
  19.      public static void main(String[] args) {  
  20.           // TODO Auto-generated method stub  
  21.          StopThread s= new StopThread();  
  22.          Thread t= new Thread(s);  
  23.          Thread t1= new Thread(s);  
  24.          t.start();  
  25.          t1.start();  
  26.           int num=0;  
  27.           while (true ){  
  28.           if (num++ ==60){  
  29. //            s.changflag();  
  30.               t.interrupt(); //中断线程t冻结状态。  
  31.               t1.interrupt(); //中断线程t1冻结状态。  
  32.                break ;  
  33.          }  
  34.           System. err.println(Thread. currentThread().getName()+num);  
  35.          }  
  36.           System. err.println( "over" );  
  37.     }  
  38. }  

九 守护线程

守护线程与普通线程写法上基本么啥区别,调用线程对象的方法setDaemon(true),则可以将其设置为守护线程。

[java]  view plain  copy
  1. class MyCommon extends Thread {  
  2.        public void run() {  
  3.                 for (int i = 0; i < 5; i++){  
  4.                         System.out.println("前台线程第" + i + "次执行!");  
  5.                         try {  
  6.                                Thread.sleep(7);  
  7.                         } catch (Exception e) {  
  8.                                e.printStackTrace();  
  9.                         }  
  10.                 }  
  11.        }  
  12. }  
  13.    
  14. class MyDaemon implements Runnable {  
  15.        public void run() {  
  16.                 for (int i = 0; i < 100;i++) {  
  17.                        System.out.println("后台线程第" + i+ "次执行!");  
  18.                         try {  
  19.                                Thread.sleep(7);  
  20.                         } catch (Exception e) {  
  21.                                e.printStackTrace();  
  22.                         }  
  23.                 }  
  24.        }  
  25. }  
  26.    
  27. public class Test1 {  
  28.        public static void main(String[] args) {  
  29.                 Thread t1 = new MyCommon();  
  30.                 Thread t2 = new Thread(newMyDaemon());  
  31.                 t2.setDaemon(true);        //设置为守护线程  
  32.    
  33.                 t2.start();  
  34.                 t1.start();  
  35.        }  
  36. }  

前台线程是保证执行完毕的,后台线程还没有执行完毕就退出了。

注意:JRE判断程序是否执行结束的标准是所有的前台执线程行完毕了,而不管后台线程的状态。

十 其他方法

1.join方法;

抢夺cpu资源或者临时加入线程执行。

当A线程执行到B线程时,遇到join方法,A线程就会等待,等B线程执行完毕后A才会执行。

另外,join()方法还有带超时限制的重载版本。例如t.join(5000);则让线程等待5000毫秒,如果超过这个时间,则停止等待,变为可运行状态。

2.线程优先级

setPriority(int newPriority)更改线程的优先级。

getPriority()获取当前线程的优先级。

每个线程都会有自己的优先级,JVM对优先级的处理方式是“抢占式”的。当JVM发现优先级高的线程时,可能会优先运行该线程;对于多个优先级相等的线程,JVM对其进行轮询处理。

Java的线程优先级从1到10,默认是5,Thread类定义了2个常量:MIN_PRIORITY NORM_PRIORITY和MAX_PRIORITY来表示最低、默认和最高优先级。

注意:当设计多线程应用程序的时候,一定不要依赖于线程的优先级。因为线程调度优先级操作是没有保障的,只能把线程优先级作用作为一种提高程序效率的方法,但是要保证程序不依赖这种操作。

3.yield()方法

Thread.yield()方法作用是:暂停当前正在执行的线程对象,并执行其他线程。

yield()应该做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。

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

4.线程睡眠

A:线程睡眠是帮助所有线程获得运行机会的最好方法。

B:线程睡眠到期自动苏醒,并返回到可运行状态,不是运行状态。sleep()中指定的时间是线程不会运行的最短时间。因此,sleep()方法不能保证该线程睡眠到期后就开始执行。

C:sleep()是静态方法,只能控制当前正在运行的线程。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值