zoukankan      html  css  js  c++  java
  • 黑马程序员----java基础--多线程

    ------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

    一、进程、线程、多进程的概念

    1、进程和线程

    进程:正在进行中的程序。其实进程就是一个应用程序运行时的内存分配空间。

    线程:其实就是进程中一个程序执行控制单元,一条执行路径。进程负责的是应用程序的空间的标示。线程负责的是应用程序的执行顺序。

    一个进程至少有一个线程在运行,当一个进程中出现多个线程时,就称这个应用程序是多线程应用程序,每个线程在栈区中都有自己的执行空间,自己的方法区、自己的变量。

    2、java虚拟机就是多线程程序

    jvm在启动的时,首先有一个主线程,负责程序的执行,调用的是main函数。主线程执行的代码都在main方法中。

    当产生垃圾时,收垃圾的动作,是不需要主线程来完成,因为这样,会出现主线程中的代码执行会停止,会去运行垃圾回收器代码,效率较低,所以由单独一个线程来负责垃圾回收。

    3、多线程的好坏之处 

    多线程的好处:解决了多部分代码同时运行的问题。
    多线程的弊端:线程太多,会导致效率的降低。
    其实,多个应用程序同时执行都是CPU在做着快速的切换完成的。这个切换是随机的。哪个线程抢到了cpu的执行权,哪个线程就执行。CPU的切换是需要花费时间的,从而导致了效率的降低。

    创建线程的目的就是为了开启一条执行路径,去运行指定的代码和其他代码实现同时运行,而运行的指定代码就是这个执行路径的任务。

    二、创建线程的方式

    1、获取当前线程名称

    Thread.currentThread().getName();

    线程的名称是由:Thread-编号定义的。编号从0开始。

    线程要运行的代码都统一存放在了run方法中。

    2、创建线程的方式

    (1)、第一种方式:继承Thread类,由子类复写run方法。

    1. 定义一个类继承Thread类。
    2. 覆盖Thread类中的run方法。
    3. 直接创建Thread的子类对象创建线程。
    4. 调用start方法开启线程并调用线程的任务run方法执行。

    run方法就是封装自定义线程运行任务的函数,run方法中定义的就是线程要运行的任务代码。记住:把需要开启多线程的运行代码都写到run方法中。

    代码演示:

     1 //继承Thread类,复写run方法
     2 class Demo extends Thread
     3 {
     4     public void run(){//run方法内不能抛异常,只能try catch。因为该方法是复写Thread类中的run方法。Thread类中的run方法没有抛异常
     5         for (int x=0;x<70 ;x++ )
     6         {
     7             System.out.println(Thread.currentThread().getName()+"....."+x);
     8         }
     9     }
    10 }
    11 class ThreadDemo 
    12 {
    13     public static void main(String[] args) //开启了两个线程:Demo和main。两个线程交替运行
    14     {
    15         new Demo().start();//创建子类对象,并开启线程,调用run方法
    16         for (int x=0;x<70 ;x++ )
    17         {
    18             System.out.println(Thread.currentThread().getName()+":::"+x);//mian函数的线程名称就是:main
    19         }
    20     }
    21 }

      自定义线程名称的方法

     1 class Demo extends Thread
     2 {
     3     private String name;
     4     Demo(String name){
     5         super(name);
     6     }
     7     public void run(){
     8         System.out.println(Thread.currentThread().getName());
     9     }
    10 }
    11 class ThreadDemo
    12 {
    13     publici static void main(Stirng[] args){
    14         new Demo("线程一").start();
    15         new Demo("线程二").start();
    16     }
    17 }                   

    (2)、创建线程的第二种方式:实现Runnable接口,复写run方法。

    1.定义类实现Runnable接口

    2.覆盖接口中的run方法(用于封装线程要运行的代码)

    3.通过Thread类创建线程对象

    4.将实现了Runnable接口的子类对象作为实际参数传递给Thread类中的构造函数。

    为什么要传递呢?线程的任务都封装在Runnable接口子类对象的run方法中,所以要在线程对象创建时就必须明确要运行的任务。

    5.调用Thread对象的start方法。开启线程,并运行Runnable接口子类中的run方法。

    为什么要有Runnable接口的出现?

    1.通过继承Thread类的方式,可以完成多线程的建立。但是这种方式有一个局限性,如果一个类已经有了自己的父类,就不可以继承Thread类,因为java单继承的局限性。

    可是该类中的还有部分代码需要被多个线程同时执行。这时怎么办呢?

    只有对该类进行额外的功能扩展,java就提供了一个接口Runnable。这个接口中定义了run方法,其实run方法的定义就是为了存储多线程要运行的代码。

    所以,通常创建线程都用第二种方式。因为实现Runnable接口可以避免单继承的局限性。

    2.其实是将不同类中需要被多线程执行的代码进行抽取。将多线程要运行的代码的位置单独定义到接口中。为其他类进行功能扩展提供了前提。所以Thread类在描述线程时,内部定义的run方法,也来自于Runnable接口。

    实现Runnable接口可以避免单继承的局限性。而且,继承Thread,是可以对Thread类中的方法,进行子类复写的。但是不需要做这个复写动作的话,只为定义线程代码存放位置,实现Runnable接口更方便一些。所以Runnable接口将线程要执行的任务封装成了对象。

    代码演示: 

     1 //实现Runnable,复写run方法
     2 class Demo implements Runnable
     3 {
     4     public void run(){
     5         for (int x=0;x<60 ;x++ )
     6         {            
     7             System.out.println(Thread.currentThread().getName()+".."+x);
     8         }
     9     }
    10 }
    11 class ThreadDemo
    12 {
    13     public static void main(String[] args) 
    14     {
    15         new Thread(new Demo()).start();
    16         for (int x=0;x<60 ; x++)
    17         {
    18             System.out.println(Thread.currentThread().getName()+":::::"+x);
    19         }
    20     }
    21 }

     (3)、线程状态

    被创建:start()

    运行:具备了执行资格,同时也具备执行权

    临时阻塞状态:线程具备cpu的执行资格,但是没有cpu的执行权

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

    消亡:stop()方法,或者run()方法结束

    三、线程安全问题

    1、多线程安全问题的原因

    通过图解:发现一个线程在执行多条语句时,并运算同一个数据时,在执行过程中,其他线程参与进来,并操作了这个数据。导致到了错误数据的产生。

    涉及到两个因素:

    1.多个线程在操作共享数据。

    2.有多条语句对共享数据进行运算。

    原因:这多条语句,在某一个时刻被一个线程执行时,还没有执行完,就被其他线程执行了。

    2、线程安全问题的解决方案

    解决安全问题的原理:

    只要将操作共享数据的语句在某一时段让一个线程执行完,在执行过程中,其他线程不能进来执行就可以解决这个问题。必须要当前线程把这些代码都执行完毕后,其他线程才可以参与运算。

    解决同步的方式:
    (1)、同步代码块的格式:
        synchronized(对象){// 任意对象都可以。这个对象就是锁。
               需要被同步的代码;
        } 
    同步的好处:解决了线程的安全问题。
    同步的弊端:当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
    同步的前提:必须有多个线程并使用同一个锁。

     需求:模拟4个线程同时卖100张票。

     1 //加上同步代码块后
     2 class Ticket implements Runnable
     3 {
     4     private int ticket=100;
     5     private Object obj=new Object();
     6     public void run(){
     7         while (true)
     8         {
     9             synchronized(obj){//加上同步代码块后,obj对象相当于一把锁,一个线程进去后,其他线程无法进去,只有当这个线程执行完,跳出同步后,另一个才能进去
    10                 if (ticket>0)//一个线程进同步有两个隐式操作,刚进同步后:上锁,跳出同步后:释放锁
    11                 {
    12                     try{Thread.sleep(20);}catch(Exception e){}//让线程停止20毫秒,这时候出现了线程等于0、-1、-2的情况
    13                     System.out.println(Thread.currentThread().getName()+"..."+ticket--);//出现问题的原因是:线程停止了20毫秒后,继续运行的时候没有判断if的条件。
    14                 }
    15             }
    16         }
    17         
    18     }    
    19 }
    20 class TicketDemo 
    21 {
    22     public static void main(String[] args) 
    23     {
    24         Ticket t=new Ticket();
    25         new Thread(t).start();
    26         new Thread(t).start();
    27         new Thread(t).start();
    28         new Thread(t).start();
    29     }
    30 }

     需求:储户,两个,每个都到银行存钱,每次存100,共存三次。

     1 class Bank
     2 {
     3     private Object obj=new Object();
     4     private int sum;
     5     public void add(int num){
     6         synchronized(obj){
     7             sum=sum+num;
     8             try{Thread.sleep(10);}catch(Exception e){}
     9             System.out.println("sum="+sum);
    10         }
    11     }
    12 }
    13 class Person implements Runnable
    14 {
    15     private Bank b=new Bank();
    16     public void run(){
    17         for(int x=0;x<3;x++){
    18             b.add(100);
    19         }
    20     }
    21 }
    22 class BankDemo 
    23 {
    24     public static void main(String[] args) 
    25     {
    26         Person p=new Person();
    27         new Thread(p).start();
    28         new Thread(p).start();
    29     }
    30 }

     (2)、同步函数

    同步函数:其实就是将同步关键字定义在函数上,让函数具备了同步性。

    上面的代码可以用同步函数代替同步代码块。

    1 class Bank
    2 {
    3     private int sum;
    4     public synchronized void add(int num){        
    5         sum=sum+num;
    6         try{Thread.sleep(10);}catch(Exception e){}
    7         System.out.println("sum="+sum);        
    8     }
    9 }

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

    同步函数和同步代码块的区别:
    1. 同步函数的锁是固定的this。
    2. 同步代码块的锁是任意的对象。
    建议使用同步代码块。

    关于同步函数用的是this锁,可以通过该程序进行验证。使用两个线程来买票。一个线程在同步代码块中。一个线程在同步函数中。都在执行卖票动作。

     1 class Ticket implements Runnable
     2 {
     3     private int ticket=100;
     4      boolean flag;
     5     public void run(){
     6         if(flag){
     7             while (true)
     8             {
     9                 synchronized(this){
    10                     if(ticket>0){
    11                         try{Thread.sleep(10);}catch(Exception e){}
    12                         System.out.println(Thread.currentThread().getName()+"..obj."+ticket--);
    13                     }
    14                 }                
    15             }            
    16         }
    17         else
    18             while(true)
    19                 show();
    20     }
    21     public synchronized void show(){        
    22         if(ticket>0){
    23             try{Thread.sleep(10);}catch(Exception e){}
    24             System.out.println(Thread.currentThread().getName()+"::this::"+ticket--);
    25         }
    26         
    27     }
    28 }
    29 class ThisLockDemo 
    30 {
    31     public static void main(String[] args) 
    32     {
    33         Ticket t=new Ticket();
    34         new Thread(t).start();
    35         try{Thread.sleep(10);}catch(Exception e){}//让线程一先sleep一会,这时候主线程还获取执行权,继续向下执行
    36         t.flag=true;//标记为true,这时候会运行show方法中的代码
    37         new Thread(t).start();
    38     }
    39 }

     上面的代码显示两个线程交替执行而且把100张票卖完了,而且没有出现0号和负数的票,说明两个线程是安全的,同时也说明了同步函数用的锁是this。

    (3)、静态同步函数

    当同步函数被static修饰时,这时的同步用的是哪个锁呢?

    静态函数在加载时所属于类,这时有可能还没有该类产生的对象,但是该类的字节码文件加载进内存就已经被封装成了对象,这个对象就是该类的字节码文件对象。

    所以静态加载时,只有一个对象存在,那么静态同步函数就使用的这个对象。

    这个对象就是 类名.class。

    还是用卖票的程序可以验证静态同步函数的锁是.class。

     1 class Ticket implements Runnable
     2 {
     3     private static int ticket=100;
     4     boolean flag;
     5     public void run(){
     6         if(flag){
     7             while (true)
     8             {
     9                 synchronized(Ticket.class){
    10                     if(ticket>0){
    11                         try{Thread.sleep(10);}catch(Exception e){}
    12                         System.out.println(Thread.currentThread().getName()+"..obj."+ticket--);
    13                     }
    14                 }                
    15             }            
    16         }
    17         else
    18             while(true)
    19                 show();
    20     }
    21     public static synchronized void show(){        
    22         if(ticket>0){
    23             try{Thread.sleep(10);}catch(Exception e){}
    24             System.out.println(Thread.currentThread().getName()+"::this::"+ticket--);
    25         }
    26         
    27     }
    28 }
    29 class ThisLockDemo 
    30 {
    31     public static void main(String[] args) 
    32     {
    33         Ticket t=new Ticket();
    34         new Thread(t).start();
    35         try{Thread.sleep(10);}catch(Exception e){}//让线程一先sleep一会,这时候主线程还获取执行权,继续向下执行
    36         t.flag=true;//标记为true,这时候会运行show方法中的代码
    37         new Thread(t).start();
    38     }
    39 }

    在一个类中只有一个同步,可以使用同步函数。如果有多同步,必须使用同步代码块,来确定不同的锁。所以同步代码块相对灵活一些。

    四、多线程下的单例设计模式

    饿汉式不存在线程安全问题,因为操作的代码只有一句。

    1 class Single 
    2 {
    3     private Single(){}
    4     private static final Single s=new Single();
    5     public static Single getInstance(){
    6         return s;
    7     }
    8 }
    懒汉式存在安全问题,可以使用同步函数解决。若直接使用同步函数,则效率较低,因为每次都需要判断。可通过双重判断的方法解决效率问题。
     1 class Single
     2 {
     3     private Single(){}
     4     private static Single s=null;
     5     public static Single getInstance(){//如果在函数上加上synchronized就出现了每次都判断锁
     6         if(s==null){//双重判断是否为空可以解决低效问题。
     7             synchronized(Single.class){
     8                 if(s==null){
     9                     s=new Single();            
    10                 }
    11             }            
    12         }        
    13         return s;
    14     }
    15 }

     五、死锁

    所谓的死锁就是同步中嵌套同步,在开发过程中避免发生死锁现象。
    演示:制造一个死锁程序。 
     1 class DeadLock implements Runnable
     2 {
     3     private boolean flag;
     4     DeadLock(boolean flag){
     5         this.flag=flag;
     6     }
     7     public void run(){    
     8         if(flag){
     9             while (true)
    10             {
    11                 synchronized(MyLock.locka){//同步代码块中嵌套同步代码块
    12                     System.out.println("if....locka...");
    13                     synchronized(MyLock.lockb){
    14                         System.out.println("if....lockb...");
    15                     }
    16                 }
    17             }                
    18         }
    19         else{
    20             while (true)
    21             {
    22                 synchronized(MyLock.lockb){
    23                     System.out.println("else-----lockb--");
    24                     synchronized(MyLock.locka){
    25                         System.out.println("else-----locka---");
    26                     }
    27                 }
    28             }                
    29         }
    30     }    
    31 }
    32 class MyLock
    33 {
    34     public static MyLock locka=new MyLock();
    35     public static MyLocks lockb=new MyLock();
    36 }
    37 class DeadLockDemo 
    38 {
    39     public static void main(String[] args) 
    40     {
    41         new Thread(new DeadLock(true)).start();
    42         new Thread(new DeadLock(false)).start();
    43 
    44     }
    45 }

     六、线程间通信问题

    线程间通信:思路:多个线程在操作同一个资源,但是操作的动作却不一样。

    1.将资源封装成对象。

    2.将线程执行的任务(任务其实就是run方法。)也封装成对象。

    等待/唤醒机制涉及的方法:
    1. wait():让线程处于冻结状态,被wait的线程会被存储到线程池中。
    2. notify():唤醒线程池中的一个线程(任何一个都有可能)。
    3. notifyAll():唤醒线程池中的所有线程。

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

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

    wait和sleep区别?
    1.wait可以指定时间也可以不指定。sleep必须指定时间。
    2.在同步中时,对CPU的执行权和锁的处理不同。
    wait:释放执行权,释放锁。
    sleep:释放执行权,不释放锁。

    (1)、一个生产者,一个消费者。即: 一个线程负责生产,一个线程负责消费

    代码演示:

     1 class Resource
     2 {
     3     private String name;
     4     private String sex;
     5     private boolean flag;
     6     public synchronized void set(String name,String sex){
     7         if(flag)//如果为真,执行下面wait方法,线程处于等待状态,释放了执行权并且放弃了锁。
     8             try{this.wait();}catch(Exception e){}
     9         //如果标记不为true,那么往里面存数据。
    10         this.name=name;
    11         this.sex=sex;
    12         flag=true;
    13         this.notify();//唤醒线程池中第一个等待的线程
    14     }
    15     public synchronized void out(){
    16         if(!flag)
    17             try{this.wait();}catch(Exception e){}
    18         System.out.println(name+"...."+sex);
    19         flag=false;
    20         this.notify();
    21     }
    22 }
    23 class Input implements Runnable
    24 {
    25     private Resource r;//为了保证操作的是同一资源,所以把资源对象传参进去
    26     Input(Resource r){
    27         this.r=r;
    28     }
    29     public void run(){
    30         int x=0;
    31         while (true)
    32         {
    33             if(x==0)
    34                 r.set("mike","man");            
    35             else
    36                 r.set("丽丽","女女女女女女");
    37             x=(x+1)%2;//切换输入内容
    38         }
    39     }
    40 }
    41 class Output implements Runnable
    42 {
    43     private Resource r;
    44     Output(Resource r){
    45         this.r=r;
    46     }
    47     public void run(){
    48         while (true)
    49         {
    50             r.out();
    51         }
    52     }    
    53 }
    54 class ResourceDemo
    55 {
    56     public static void main(String[] args) 
    57     {
    58         Resource r=new Resource();//创建资源对象
    59         new Thread(new Input(r)).start();//开启两个线程
    60         new Thread(new Output(r)).start();
    61     }
    62 }

     (2)、多生产--多消费问题

    上面的例子是一个输入一个输出,现在是多个线程执行输入同时多条线程执行输出。即:多个线程操作生产,多个线程操作消费。这是开发中最常见的,希望能够掌握。

     1 class Resource
     2 {
     3     private String name;
     4     private int count=1;
     5     private boolean flag=false;
     6     public synchronized void set(String name){
     7         while(flag)//循环判断标记来防止线程等待被唤醒后往下执行,直接生产覆盖上一个数据。
     8             try{this.wait();}catch(Exception e){}
     9         this.name=name+"....."+count++;
    10         System.out.println(Thread.currentThread().getName()+"..生产者.."+this.name);
    11         flag=true;
    12         this.notifyAll();//唤醒线程池中的全部线程
    13     }
    14     public synchronized void out(){
    15         while(!flag)
    16             try{this.wait();}catch(Exception e){}
    17         System.out.println(Thread.currentThread().getName()+"----消费者-------"+this.name);
    18         flag=false;
    19         this.notifyAll();//唤醒线程池中的全部线程
    20     }
    21 }
    22 class Producer implements Runnable
    23 {
    24     private Resource r;
    25     Producer(Resource r){
    26         this.r=r;
    27     }
    28     public void run(){
    29         while (true)
    30         {
    31             r.set("商品");
    32         }
    33     }
    34 }
    35 class Consumer implements Runnable
    36 {
    37     private Resource r;
    38     Consumer(Resource r){
    39         this.r=r;
    40     }
    41     public void run(){
    42         while (true)
    43         {
    44             r.out();
    45         }
    46     }
    47 }
    48 class  ProducerConsumerDemo
    49 {
    50     public static void main(String[] args) 
    51     {
    52         Resource r=new Resource();
    53         new Thread(new Producer(r)).start();
    54         new Thread(new Producer(r)).start();
    55         new Thread(new Consumer(r)).start();
    56         new Thread(new Consumer(r)).start();
    57 
    58     }
    59 }

    七、线程间通信-生产者消费者JDK1.5升级版

      上面的程序虽然能够解决问题,但是发现每次都唤醒全部线程,这样会导致线程都去抢夺cpu执行权,效率有点低,所以JDK在升级1.5的时候推出了一个接口Lock接口。它的出现就替代了同步代码块或者同步函数,将同步的隐式操作变成显示锁操作。同时更为灵活,可以一个锁上加上多组监视器。。

      在之前的版本中使用Object类中wait、notify、notifyAll的方法来完成的,那是因为同步中的锁是任意对象,所以操作时的等待唤醒的方法是都是定义在Object类中。而现在锁是指定对象Lock,所以查找等待唤醒机制方式需要通过Lock接口来完成.而Lock接口中并没有直接操作等待唤醒的方法,而是将这些方法又单独的封装了一个对象中,这个对象就是Condition,将Object中的三个方法进行了单独的封装,并提供了功能一致的方法await()、signal()、signalAll()。所以Condition接口出现替代了Object中的wait、notify、notifyAll方法。将这些监视器方法单独进行了封装,变成Condition监视器对象,可以任意锁进行组合。

    Lock接口

    1 Lock lock=new ReentrantLock();//创建Lock对象
    2 void    lock();//获取锁。
    3 void    unlock();//释放锁,为了防止异常出现,导致锁无法被关闭,所以锁的关闭动作要放在finally中。
    4 Condition  newCondition();//获取Condition对象

     Condition接口

    1 void    await();//对应于Object中的wait方法
    2 void    signal();//对应于Object中的notify方法
    3 void    signalAll();//对应于Object中的notifyAll方法

     JDK1.5多生产--多消费问题代码

     1 import java.util.concurrent.locks .*;
     2 class Resource
     3 {
     4     private boolean flag;
     5     private Lock lock=new ReentrantLock();//创建Lock对象
     6     private Condition procondition=lock.newCondition();//创建Condition对象
     7     private Condition concondition=lock.newCondition();
     8     private int count=1;
     9     private String name;
    10     public void set(String name){
    11         lock.lock();//显示的加锁
    12         try
    13         {
    14             while(flag)
    15                 procondition.await();//await替换了wait;
    16             this.name=name+"..."+count++;
    17             System.out.println(Thread.currentThread().getName()+"..生产者.."+this.name);
    18             flag=true;
    19             concondition.signal();//signal替换了notify
    20         }
    21         catch (Exception e)
    22         {
    23             System.out.println(e);
    24         }
    25         finally{
    26             lock.unlock();//释放锁,释放资源的动作一定要放在finally块中
    27         }
    28         
    29     }
    30     public void out(){
    31         lock.lock();
    32         try
    33         {
    34             while(!flag)
    35                 concondition.await();
    36             System.out.println(Thread.currentThread().getName()+"------消费者-----"+this.name);
    37             flag=false;
    38             procondition.signal();
    39         }
    40         catch (Exception e)
    41         {
    42             System.out.println(e);
    43         }
    44         finally{
    45             lock.unlock();
    46         }
    47     }
    48 }
    49 class Producer implements Runnable
    50 {
    51     private Resource r;
    52     Producer(Resource r){
    53         this.r=r;
    54     }
    55     public void run(){        
    56         while (true)
    57         {            
    58             r.set("商品");
    59         }
    60     }
    61 }
    62 class Consumer implements Runnable
    63 {
    64     private Resource r;
    65     Consumer(Resource r){
    66         this.r=r;
    67     }
    68     public void run(){
    69         while (true)
    70         {
    71             r.out();
    72         }
    73     }
    74 }
    75 class ProducerConsumerDemo2 
    76 {
    77     public static void main(String[] args) 
    78     {
    79         Resource r=new Resource();
    80         new Thread(new Producer(r)).start();
    81         new Thread(new Producer(r)).start();
    82         new Thread(new Consumer(r)).start();
    83         new Thread(new Consumer(r)).start();
    84     }
    85 }

    八、Thread类中其他方法

    1、停止线程

    查看API发现线程停止方法stop方法已经过时了,所以我们无法在使用。其实让线程停止的原理就是:run方法结束。

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

    所以run方法中通常定义循环,指定控制住循环线程即可结束。

    特殊情况:当线程处于了冻结状态。就不会读取到标记。那么线程就不会结束。当没有指定的方式让冻结的线程恢复到运行状态是,这时需要对冻结进行清除。强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。Thread类提供该方法 interrupt(),interrupt()方法将线程从冻结状态强制恢复到运行状态中来,让线程具备CPU的执行资格。强制动作会发生InterruptedException,一定要记得处理。

     1 class StopThread implements Runnable{
     2        private boolean flag = true;
     3        public void run(){
     4              while(flag ){
     5                   System. out.println(Thread.currentThread().getName() + "...");
     6             }
     7        }
     8        public void setFlag(){
     9              flag = false ;
    10        }
    11 }
    12  
    13 class StopThreadDemo{
    14        public static void main(String[] args){
    15             StopThread st = new StopThread();
    16             new Thread(st).start();
    17             new Thread(st).start();        
    18 
    19              int num = 0;
    20             while(true){
    21                    if(num++ == 50){
    22                         st.setFlag();
    23                          break;
    24                   }
    25                   System. out.println(Thread.currentThread().getName()+"..."+ num);
    26              }
    27              System. out.println("over" );
    28       }
    29 }
     1 class StopThread implements Runnable{
     2        private boolean flag = true;
     3        public synchronized void run(){
     4              while(flag){
     5                    try{
     6                         wait();
     7                   } catch(Exception e){
     8                         System.out.println(e);
     9                         flag = false;
    10                   }
    11                   System.out.println(Thread.currentThread().getName() );
    12             }
    13       }
    14        public void setFlag(){
    15             flag = false;
    16       }
    17 }
    18 
    19 class StopThreadDemo{
    20        public static void main(String[] args){
    21             StopThread st = new StopThread();
    22             Thread t1 = new Thread(st).start();
    23             Thread t2 = new Thread(st).start();    
    24              int num = 0;
    25              while(true){
    26                    if(num++ == 50){
    27                         t1.interrupt();
    28                         t2.interrupt();
    29                          break;
    30                   }
    31                   System.out.println( Thread.currentThread().getName()+"..." + num);
    32             }
    33             System.out.println( "over");
    34       }
    35 }

    2、守护线程

    setDaemon,将该线程标记为守护线程或用户线程。正在运行的线程都是守护线程时,Java 虚拟机退出。该方法必须在启动线程前调用。 setDaemon(boolean):将线程标记为后台线程,后台线程和前台线程一样,开启,一样抢执行权运行,只有在结束时,有区别,当前台线程都运行结束后,后台线程会自动结束。

    3、join方法

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

    4、 setPriority方法 

    用来设置优先级

    MAX_PRIORITY 最高优先级10

    MIN_PRIORITY   最低优先级1

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

    5、yield方法

    暂停当前线程,让其他线程执行,可以让线程释放执行权

    6、toString方法

    返回该线程的字符串表示形式,包括线程名称、优先级和线程组

  • 相关阅读:
    【leetcode】496. Next Greater Element I
    工具网站
    [err]Traceback (most recent call last): File "/usr/local/bin/pip", line 7, in <module> from pip._internal import main ImportError: No module named 'pip._internal'
    []TLD code run
    【动手学深度学习】
    【论文阅读】Wing Loss for Robust Facial Landmark Localisation with Convolutional Neural Networks
    【linux基础】ubuntu系统NVIDIA驱动安装
    【linux基础】linux不能进入系统
    【leetcode】492. Construct the Rectangle
    【leetcode】485. Max Consecutive Ones
  • 原文地址:https://www.cnblogs.com/521Android/p/4731504.html
Copyright © 2011-2022 走看看