06_Java多线程、线程间通讯

1. 线程的概念编程

     1.1多进程与多线程安全

          进程:一个正在执行的程序.每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或叫一个控制单元. 一个进程至少有一个线程.多线程

    线程:就是进程中的一个独立的控制单元. 线程控制这进程的执行.ide

          多进程的缺点:进程切换开销大;进程间的通讯很不方便。函数

          多线程: 指的是在单个程序中能够同时运行多个不一样的线程,执行不一样的任务,线程切换的开销小 。this

     1.2线程的状态spa

          Java语言使用Thread类及其子类的对象来表示线程,新建的线程在它的一个完整的生命周期一般要经历5种状态.线程

wKioL1P8Z7agpBPpAACtapy1mAs907.jpg

  

冻结状态:在sleep和wait时, 既没有运行资格,有没有执行权3d

阻塞状态: 具有运行资格, 没有执行权code

     1.3线程调度与优先级(多线程的特性:随机性)

     Java采用抢占式调度策略,下面几种状况下,当前线程会放弃CPU: 
          (1)当前时间片用完; 
          (2)线程在执行时调用了yield() 或sleep() 方法主动放弃;  
          (3)进行I/O 访问,等待用户输入,致使线程阻塞;或者为等候一个条件变量,线程调用wait()方法;    
          (4)有高优先级的线程参与调度。 
     线程的优先级用数字来表示,范围从1~10。主线程的默认优先级为5
          Thread.MIN_PRIORITY=1
          Thread.NORM_PRIORITY=5 

    Thread.MAX_PRIORITY=10


2. 多线程编程方法

     2.1 Thread类简介

          Thread类综合了Java程序中一个线程须要拥有的属性和方法,其构造方法以下:

               public Thread (ThreadGroup group,Runnable target,String name);      

               public Thread();

               public Thread(Runnable target);

               public Thread(Runnable target,String name);

               public Thread(String name);

               public Thread(ThreadGroup group,Runnable target);

               public Thread(ThreadGroup group,String name);  

          Thread类的主要方法以及功能如表

wKioL1P8ZtWyleGJAANdD7K_9YU775.jpg

     2.2 继承Thread类实现多线程     

    须要重写run方法实现线程的任务.须要注意的是,程序中不要直接调用此方法,而是调用线程对象的start()方法启动线程,让其进入可调度状态,线程得到调度自动执行run()方法.    

     2.3 实现Runnable接口编写多线程

    经过 Thread 类的构造函数public Thread(Runnable target)能够将一个Runnable 接口对象传递给线程,线程在调度时将自动调用Runnable 接口对象的run方法。

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

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


3. 线程资源的同步处理

     3.1 临界资源问题

wKioL1P8Z4ayeJJcAACC3rgeySg856.jpg

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

  synchronized(对象) {

    须要被同步的代码

  }

          对象如同锁,持有锁的线程能够在同步中执行.

          没有持有锁的线程及时获取cpu的执行权,也进不去,由于没有获取锁.

          同步的前提:

               1.必需要有两个或者以上的线程

               2.必需要多个线程使用同一个锁     

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

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

     同步函数使用的是哪一个锁:

           函数须要被对象调用,那么函数都有一个所属对象的引用.就是this.

           因此同步函数使用的锁是this锁.静态函数使用的是该方法所在类的字节码文件对象.

   懒汉式加强  

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

     3.2 wait()和notify()

     3.3 避免死锁

          多个线程相互等待对方释放持有的锁,而且在获得对方锁以前不释放本身的锁.(本身能写一个死锁代码)    

 1 /*
 2  * 死锁: 同步中嵌套同步容易发生  3  */
 4 
 5 public class LockDemo {  6 
 7     public static void main(String[] args) {  8         Test t = new Test(false);  9         Test t2 = new Test(true); 10         new Thread(t).start(); 11         new Thread(t2).start(); 12 
13  } 14 
15 } 16 
17 class Test implements Runnable{ 18     private boolean flag; 19     Test(boolean flag){ 20         this.flag = flag; 21  } 22     public void run(){ 23         if(flag){ 24             try{Thread.sleep(30);}catch(Exception e){} 25             synchronized(KK.oa){ 26                 System.out.println("if a"); 27                 synchronized(KK.ob){ 28                     System.out.println("if b"); 29  } 30  } 31  } 32         else{ 33             synchronized(KK.ob){ 34                 System.out.println("else b"); 35                 synchronized(KK.oa){ 36                     System.out.println("else a"); 37  } 38  } 39  } 40  } 41 } 42 
43 //自定义两个锁
44 class KK{ 45     static Object oa = new Object(); 46     static Object ob = new Object(); 47 }

4. 线程间通讯

     思考1: wait(),notify(),nofifyAll()用来操做线程为何定义在Object类中?

          1.这些方法存在同步中,用来操做同步中的线程,必需要标志它们所操做线程的只有锁.

          2.使用这些方法必须标识出所属同步的锁,只有同一个锁上被等待线程,能够被同一个锁上notify唤醒.不能够对不一样锁中的线程进行唤醒.

          3.锁能够是任意对象,因此任意对象调用的方法必定定义Object类中.

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

          wait():释放资源,释放锁

          sleep():释放资源,不释放锁 

 

 1 //线程通讯: 取mike, 取丽丽
 2 public class Demo2 {
 3 
 4     public static void main(String[] args) {
 5         Person p = new Person();
 6         new Thread(new Input(p)).start();
 7         new Thread(new Output(p)).start();
 8     }
 9 
10 }
11 
12 class Person{
13     private String name ;
14     private String sex ;
15     private boolean flag = false;
16     
17     public synchronized void set(String name, String sex){
18         //flag为true,表示已存在
19         if(flag){
20             try {wait();}catch (InterruptedException e) {e.printStackTrace();}
21         }
22         //flag为false,表示没人可加
23         this.name = name;
24         this.sex = sex;
25         flag = true;
26         this.notify();
27     }
28     
29     public synchronized void out(){
30         //flag为false,表示无人无法取
31         if(!flag){
32             try {wait();}catch (InterruptedException e) {e.printStackTrace();}
33         }
34         //flag为true,表示有人能够取出
35         System.out.println(toString());    
36         flag = false;
37         this.notify();        
38     }
39     
40     public String toString(){
41         return "姓名:"+name+"性别:"+sex;
42     }
43 }
44 
45 class Input implements Runnable{
46     private Person p;
47     
48     Input(Person p){
49         this.p = p;
50     }
51     
52     public void run(){
53         boolean flag = false;
54         while(true){    
55                 if(flag){
56                     p.set("likai","男");
57                     flag = false;
58                 }
59                 else{
60                     p.set("tangll","女");    
61                     flag = true;
62                 }    
63         }
64     }
65 }
66 
67 class Output implements Runnable{
68     private Person p;
69     
70     Output(Person p){
71         this.p = p;
72     }
73     
74     public void run(){
75         while(true){
76             p.out();
77         }
78     }
79 }
线程通讯Demo1    
 1 public class Demo3 {
 2 
 3     public static void main(String[] args) {
 4         Bridge b = new Bridge();
 5         for(int i=1;i<6;i++){
 6             new Thread(new Person(b,"由北向南第"+i+"人")).start();
 7         }
 8         
 9         for(int i=1;i<7;i++){
10             new Thread(new Person(b,"由南向北第"+i+"人")).start();
11         }
12     }
13 }
14 
15 class Bridge{
16     private String name;
17     private boolean flag;
18     public synchronized void UpBridge(){
19         if(flag){
20             try {
21                 wait();
22             } catch (InterruptedException e) {            
23                 e.printStackTrace();
24             }
25         }
26         flag = true;
27         
28     }
29     public synchronized void DownBridge(){
30         flag = false;
31         notifyAll();
32     }
33 }
34 
35 class Person implements Runnable{
36     private Bridge bridge;
37     private String pName;
38     
39     Person(Bridge bridge, String pName){
40         this.bridge = bridge;
41         this.pName = pName;
42     }
43 
44     public void run() {        
45         bridge.UpBridge();
46         try{
47             Thread.sleep(80);
48         }catch(InterruptedException e){}
49         bridge.DownBridge();
50         System.out.println(pName);
51     }
52     
53 }
过桥问题

生产消费者问题

     生产消费者问题--JDK1.5提供的多线程升级解决方法

          将同步synchronized替换成Lock操做.

          将Object中的wait,notify,notifyAll方法替换成Condition对象.

          该对象能够Lock锁进行获取.

    该实例中,实现了本方只唤醒对方的操做.

     中止线程

          1.定义循环结束标记

               由于线程运行代码通常都是循环,只是控制了循环便可.

          2.使用interrupt(中断)方法: 该方法是强制结束线程的冻结状态,使线程回到运行状态中来.这样就能够操做标记结束.

          (注)stop方法和suspend方法已过期不在再使用.

          setDeamon()方法

               标记为后台线程, 当全部前台前程结束后会Java虚拟机退出. 该方法必须在启动线程前调用.   

          join()方法

               等待该线程终止, 实际上是抢夺CPU执行权.

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

          toString()

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

          yield()

               暂停当前执行的线程对象,并执行其余线程

相关文章
相关标签/搜索