• 06_Java多线程、线程间通信


    1. 线程的概念

         1.1多进程与多线程

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

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

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

              多线程: 指的是在单个程序中可以同时运行多个不同的线程,执行不同的任务,线程切换的开销小 。

         1.2线程的状态

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

    wKioL1P8Z7agpBPpAACtapy1mAs907.jpg

      

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

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

         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()

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

    ---------thinking、architecture、code、treasure---------
  • 相关阅读:
    DHCP Option 60 的理解
    程序中的魔鬼数字
    开源GUI-Microwindows之程序入口分析
    http报错之return error code:401 unauthorized
    内存泄漏以及常见的解决方法
    怎样对ListView的项进行排序
    getline函数
    JavaFx初探
    ListBox控件的操作与实现
    SQLite的SQL语法
  • 原文地址:https://www.cnblogs.com/elaa/p/3989620.html
Copyright © 2020-2023  润新知