首页 > 代码库 > 黑马程序员_多线程

黑马程序员_多线程

 ------- android培训、java培训、期待与您交流! ----------

                                        多线程
1、进程:
  正在运行的程序所占有的内存空间,叫做进程。
  线程:
  一个应用程序中的子程序,对于CPU,子程序可以有一条独立的执行路径,称为线程。
  线程特点:依靠一个应用程序,对于其他子程序,独立被CPU执行的
2、多线程的好处:
  充分利用CPU的资源,程序的同时运行,提高效率
3、java中线程创建的两种方法:
  第一种:
  定义类继承Thread class extends Thread
  重写Thread中的run方法;public void run()
  创建Thread类的子类对象
  调用子类对象的start方法,开启线程
注:start方法告诉JVM开启一个对CPU的执行路径,调用run方法。
  class A extends Thread {
    //..
    public void run() {
    // . . .
    }
  }
  A t = new A();
  t.start();

第二种(重点):
  定义类实现Runnable接口
  重写run方法
  创建Thread类对象
  调用Thread对象的start方法开启线程
  class A implements Runnable {

    public void run() {
    . . .
    }
  }
  Thread t=new Thread(new A());
  t.start();
  这两种方法的区别:
  第一种是继承,具有局限性,数据也是线程独享的;
  第二种是实现接口,避免了单继承的局限性,同时数据是线程共享的。
4、线程名字的获取和设置
  获取Thread子类的线程名字:
  Thread类的方法 String getName();
  获取非Thread子类的线程名字:
  Thread类的方法 static Thread currentThread();//返回当前正在执行的线程对象
  设置线程名字的方法:
  Thread类的方法 void setName(字符串的名字)
  利用Thread类的构造方法 Thread(字符串名字)
5、线程的状态图(重点)

  
  冻结和阻塞的区别:
  冻结值线程的休眠或者等待,此时线程主动的放弃CPU的执行资格,同时也放弃了CPU的执行权力;
  阻塞时线程不会放弃CPU的执行资格,但没有CPU的执行权力。
6、同步机制,保证多线程在操作共享数据的安全性
  同步代码块:
  格式: synchronized(对象){
    线程操作的共享数据
  }
  对象:专业名词,对象监视器,锁
  同步方法:
  同步的非静态方法的锁是this,表示本类对象的引用
  同步的静态方法的锁是本类.class(因为JVM为每一种数据类型赋予了一个静态的成员变量,变量的返回值就是本类的class文件对象。)
7、多线程并发的单例模式懒汉(重点)
  懒汉,延迟加载,多线程并发实验,单例模式不能保证对象的唯一性。

 1 class single{ 2   private Single(){} 3   private Single s=null; 4  5   public static Single getInstance(){ 6     if(s==null){ 7       synchronized(Single.class){ 8         if(s==null) 9           s=new Single()    10       }    11     }12     return s;13   }14 }    

 


注:面试的时候,写同步代码块,不要写同步方法,进行两次判断提高程序的效率
8、死锁:(重点)
  多线程在争抢同一把锁的时候,造成的程序的假死现象。实际的开发中不可能出现

 1 class Dead implements Runnable{ 2   private boolean flag; 3   Dead(){} 4   Dead (boolean flag){this.flag=flag;} 5   public void run(){ 6   while (true){ 7   //如果flag=true,就先进入A房间,再进入B房间 8     if(flag){ 9       synchronized (A .lockA){10         System.out.println("先进入房间A");11           synchronized(B. lockB){12             System.out.println("再进入房间B");13           }14         }15     }else{16        synchronized (B.lockB){17          System.out.println("先进入房间B");18           synchronized(A.lockA){19             System.out.println("再进入房间B");20           }21         }22      }23    }24   }25 26 }27 28 //建立房间A的锁29 class A{30   public static final A lockA=new A();31 }32 //建立B房间的锁33 class B{34   public static final B lockB=new B();35 }36 public class SiSuo {37   public static void main(String[] args) {38     Thread t1=new Thread(new Dead(true));39     Thread t2=new Thread(new Dead(false));40     t1.start();41     t2.start();42 43   }44 }

 

9、线程通信:

  案例一:输入一个名字,打印一个名字。

    案例中碰到的问题:

      A、数据错乱问题:第二次赋值还没完成,就输出了,导致了性别错乱。

        解决方法:  同步共享数据。

      B、数据的安全问题还没解决:

        只有2个原因:共享数据是否都同步了
               同步中的锁是同一个吗

        解决方法:  使用Resource的对象作为锁。
      C、没有交替的输出:

        原因:线程发生了阻塞,cpu资源被其他的线程抢走了

        解决方法:定义一个标记,使用等待和唤醒控制线程

      D、出现了监视器异常:

        原因:wait()和notify()如果不标明谁调用的话,就会是Object类调用的。而作为锁的对象是Resource类的对象。

        解决方法:用Resource调用wait()和notify()方法。
 

 1 public class XianCheng { 2   public static void main(String[] args) { 3     Resource r=new Resource(); 4  5     Thread tin=new Thread(new Input(r)); 6     Thread tout=new Thread(new Output(r)); 7     tin.start(); 8     tout.start(); 9   }10 }11 class Resource{12   String name;13   String sex;14   boolean flag=false;//flag为真代表赋值完成,等待,唤醒输出;为假,代表输出完成,等待,唤醒输入;15 16   Resource (){}17 }18 class Input implements Runnable{19   Resource r;20 21   Input (){}22   Input(Resource r){23   this.r=r;24 }25 26   public void run(){27     int x=0;28     while(true){29       synchronized(r){30         if(r.flag==true){31           try{32             r.wait();33           }catch(Exception e){    }34         }35         if(x%2==0){36           r.name="妖姬";37           r.sex="女";38         }else{39           r.name="亚索";40           r.sex="男";41         }42         x++;43         r.flag=true;44         r.notify();45       }46     }47    }48 }49 class Output implements Runnable{50   Resource r;51   Output (){}52   Output(Resource r){53     this.r=r;54   }55   public void run(){56     while(true){57       synchronized (r){58         if(r.flag==false){59           try{60             r.wait();61           }catch(Exception e){62           }63        }64        System.out.println(r.name+"..."+r.sex);65        r.flag=false;66        r.notify();67       }68     }69    }70 }

 10、案例二:有一个产品,生产一个消费一个。生产四个,消费四个。

   遇到的问题:

    A、生产了多个,消费了多个

      原因:notify()会唤醒先等待的线程,不管标记此时是什么值。

      解决方法:全部唤醒后,再进行标记判断。

    B、资源浪费

      原因:唤醒了全部进程

  

 1 /* * 实现多生产和多消费 2  * 定义产品对象 -- 生产 -- 消费 3  * 生产者线程,控制生产 4  * 消费者线程,控制消费 5  */ 6 //定义产品类 7 class Product { 8     private String name;//品名 9     private int count = 0 ;//计数器10     private boolean b = false;11     //生产方法12     public synchronized void set(String name){13         while(b){14             try{this.wait();}catch(Exception e){}15         }16         this.name = name +".."+count++;//this.name成员变量,品名+计数器17         System.out.println(Thread.currentThread().getName()+" 生产第.."+this.name);18         b = true;19         this.notifyAll();20     }21     //消费方法22     public synchronized void get(){23         while(!b){24             try{this.wait();}catch(Exception e){}25         }26         System.out.println(Thread.currentThread().getName()+" 消费第......"+this.name);27         b = false;28         this.notifyAll();29     }30 }31 //定义生产者线程32 class Pro implements Runnable{33     private Product p ;34     Pro(Product p){this.p = p;}35     public void run(){36         while(true)37             p.set("黄金");38     }39 }40 //定义消费线程41 class Cus implements Runnable{42     private Product p ;43     Cus(Product p ){this.p = p;}44     public void run(){45         while(true)46             p.get();47     }48 }49 public class ThreadDemo1 {50     public static void main(String[] args) {51         Product p = new Product();52         Pro pro = new Pro(p);53         Cus cus = new Cus(p);54         Thread t0 = new Thread(pro);55         Thread t1 = new Thread(pro);56         Thread t2 = new Thread(pro);57         Thread t3 = new Thread(pro);58         59         Thread t4 = new Thread(cus);60         Thread t5 = new Thread(cus);61         Thread t6 = new Thread(cus);62         Thread t7 = new Thread(cus);63         64         t0.start();65         t1.start();66         t2.start();67         t3.start();68         t4.start();69         t5.start();70         t6.start();71         t7.start();72     }73 }

 

 

 

                                          1.5后新的锁

1、java.util.concurrent.locks包中Lock接口

  替换了以前的同步机制,获得了更广泛和灵活的锁定操作

2、Lock接口中的方法:

  Lock() 获得锁

  unlock()释放锁

  synchronized(this){等同于获取锁

    }等同于释放锁

Lock接口的实现类ReentrantLock

                                            

                                          Condition接口

 

 

1、java.util.concurrent.locks包中Condition接口

 解决了同步没有后,锁也没有了,不能使用notify和wait方法的问题。

2、Condition中的方法:

  await()==wait();

  signal()==notifty();

  signalAll()==notify();

3、实现多线程通信中只唤醒一个线程的步骤

  A、导包:java.util.concurrent.locks.*;

  B、获取锁:创建Lock接口的实现类对象

   Lock lock new ReentrantLock();

  C、获取Condition对象,对线程分组管理

    lock.new condition()

  D、利用Condition对象中的方法await和signal实现等待和唤醒

 1 /* 2  * 生产者与消费案例,改造成JDK1.5的新特性,Lock锁方式 3  * 实现节约资源,目的唤醒对方的一个 4  */ 5 import java.util.concurrent.locks.*; 6 class Product { 7     private String name;//品名 8     private int count = 0 ;//计数器 9     private boolean b = false;10     11     //获取Lock接口的实现类对象,获取锁12     private Lock lock = new ReentrantLock();13     private Condition pro = lock.newCondition();14     private Condition cus = lock.newCondition();15     16     //生产方法17     public  void set(String name){18         lock.lock();19         while(b){20             try{pro.await();}catch(Exception e){}21         }22         this.name = name +".."+count++;//this.name成员变量,品名+计数器23         System.out.println(Thread.currentThread().getName()+" 生产第.."+this.name);24         b = true;25         cus.signal();26         lock.unlock();27     }28     //消费方法29     public  void get(){30         lock.lock();31         while(!b){32             try{cus.await();}catch(Exception e){}33         }34         System.out.println(Thread.currentThread().getName()+" 消费第......"+this.name);35         b = false;36         pro.signal();37         lock.unlock();38     }39 }40 //定义生产者线程41 class Pro implements Runnable{42     private Product p ;43     Pro(Product p){this.p = p;}44     public void run(){45         while(true)46             p.set("黄金");47     }48 }49 //定义消费线程50 class Cus implements Runnable{51     private Product p ;52     Cus(Product p ){this.p = p;}53     public void run(){54         while(true)55             p.get();56     }57 }58 public class ThreadLock {59     public static void main(String[] args) {60         Product p = new Product();61         Pro pro = new Pro(p);62         Cus cus = new Cus(p);63         Thread t0 = new Thread(pro);64         Thread t1 = new Thread(pro);65         Thread t2 = new Thread(pro);66         Thread t3 = new Thread(pro);67         68         Thread t4 = new Thread(cus);69         Thread t5 = new Thread(cus);70         Thread t6 = new Thread(cus);71         Thread t7 = new Thread(cus);72         73         t0.start();74         t1.start();75         t2.start();76         t3.start();77         t4.start();78         t5.start();79         t6.start();80         t7.start();81     }82 }

 

 

                                        线程的停止方法

Thread类中的stop()过时了,所以通过终止run方法来停止线程

  方法一:改变循环变量(等待中的线程停不下来)

  方法二:利用wait()方法的异常,在catch语句中改变循环变量

  Thread中中断线程的方法:void interrupt();

 

                                        线程的守护

Thread中void setDaemon(boolean on)如果传递布尔值为true,就将调用该方法的线程标记为守护线程,守卫别的线程。

 

                                          定时任务

java.util.Timer

Timer类的构造方法 Timer(boolean isDaemon)

Timer类中的方法:Schedule(执行的代码TimerTask task,开始时间,持续时间)

          执行的代码:task要实现TimerTask接口需重写run方法

 

                                Thread中的toString()方法

toString()获取线程的名字,优先级和组  

优先级 :  默认5  最低1   最高 10    

设置优先级的方法:setPriority(int newPriority)   

                                    Thread中的join方法

作用:等待该线程终止    

                                    Thread中yield方法  

线程让步