• java进阶学习--java多线程


    多线程

    一、多线程

      1、进程与线程     

    进程:一个计算机程序的运行实例,包含了需要执行的指令;有自己的独立地址空间,包含程序内容和数据;不同进程的地址空间是互相隔离的;

      进程拥有各种资源和状态信息,包括打开的文件、子进程和信号处理。

    线程:表示程序的执行流程,是CPU调度执行的基本单位;线程有自己的程序计数器、寄存器、堆栈和帧。同一进程中的线程共用相同的地址空间,

       同时共享进程锁拥有的内存和其他资源。

     

    2、Java标准库提供了进程和线程相关的API

        进程主要包括表示进程的java.lang.Process类和创建进程的java.lang.ProcessBuilder类

        表示线程的是java.lang.Thread类,在虚拟机启动之后,通常只有Java类的main方法这个普通线程运行,运行时可以创建和启动新的线程;

        有一类守护线程(damon thread),守护线程在后台运行,提供程序运行时所需的服务。当虚拟机中运行的所有线程都是守护线程时,虚拟机终止运行。

           3、线程的生命周期

        线程是一个动态执行的过程,它也有一个从产生到死亡的过程。

        

      • 新建状态:

        使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序start() 这个线程。

      • 就绪状态:

        当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。

      • 运行状态:

        如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。

      • 阻塞状态:

        如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:

        • 等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。

        • 同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。

        • 其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。

      • 死亡状态:

        一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。

      4、线程的优先级    

    每一个 Java 线程都有一个优先级,这样有助于操作系统确定线程的调度顺序。

    Java 线程的优先级是一个整数,其取值范围是 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )

    默认情况下,每一个线程都会分配一个优先级 NORM_PRIORITY(5)

    具有较高优先级的线程对程序更重要,并且应该在低优先级的线程之前分配处理器资源。但是,线程优先级不能保证线程执行的顺序,而且非常依赖于平台。

      5、线程的创建      

      Java 提供了三种创建线程的方法:

        • 通过实现 Runnable 接口;
        • 通过继承 Thread 类本身;
        • 通过 Callable 和 Future 创建线程。

        1、通过实现 Runnable 接口来创建线程    

    (1)定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。

    (2)创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。

    (3)调用线程对象的start()方法来启动该线程。

    示例:

     1 package com.thread;  
     2   
     3 public class RunnableThreadTest implements Runnable  
     4 {  
     5   
     6     private int i;  
     7     public void run()  
     8     {  
     9         for(i = 0;i <100;i++)  
    10         {  
    11             System.out.println(Thread.currentThread().getName()+" "+i);  
    12         }  
    13     }  
    14     public static void main(String[] args)  
    15     {  
    16         for(int i = 0;i < 100;i++)  
    17         {  
    18             System.out.println(Thread.currentThread().getName()+" "+i);  
    19             if(i==20)  
    20             {  
    21                 RunnableThreadTest rtt = new RunnableThreadTest();  
    22                 new Thread(rtt,"新线程1").start();  
    23                 new Thread(rtt,"新线程2").start();  
    24             }  
    25         }  
    26   
    27     }  
    28   
    29 }  

      2、继承Thread类创建线程类   

    (1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。

    (2)创建Thread子类的实例,即创建了线程对象。

    (3)调用线程对象的start()方法来启动该线程。

     1 package com.thread;  
     2   
     3 public class FirstThreadTest extends Thread{  
     4     int i = 0;  
     5     //重写run方法,run方法的方法体就是现场执行体  
     6     public void run()  
     7     {  
     8         for(;i<100;i++){  
     9         System.out.println(getName()+"  "+i);  
    10           
    11         }  
    12     }  
    13     public static void main(String[] args)  
    14     {  
    15         for(int i = 0;i< 100;i++)  
    16         {  
    17             System.out.println(Thread.currentThread().getName()+"  : "+i);  
    18             if(i==20)  
    19             {  
    20                 new FirstThreadTest().start();  
    21                 new FirstThreadTest().start();  
    22             }  
    23         }  
    24     }  
    25   
    26 }  

        上述代码中Thread.currentThread()方法返回当前正在执行的线程对象。GetName()方法返回调用该方法的线程的名字。

      3、通过Callable和Future创建线程    

    (1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。

    (2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。

    (3)使用FutureTask对象作为Thread对象的target创建并启动新线程。

    (4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

    示例: 

     1 package com.thread;  
     2   
     3 import java.util.concurrent.Callable;  
     4 import java.util.concurrent.ExecutionException;  
     5 import java.util.concurrent.FutureTask;  
     6   
     7 public class CallableThreadTest implements Callable<Integer>  
     8 {  
     9   
    10     public static void main(String[] args)  
    11     {  
    12         CallableThreadTest ctt = new CallableThreadTest();  
    13         FutureTask<Integer> ft = new FutureTask<>(ctt);  
    14         for(int i = 0;i < 100;i++)  
    15         {  
    16             System.out.println(Thread.currentThread().getName()+" 的循环变量i的值"+i);  
    17             if(i==20)  
    18             {  
    19                 new Thread(ft,"有返回值的线程").start();  
    20             }  
    21         }  
    22         try  
    23         {  
    24             System.out.println("子线程的返回值:"+ft.get());  
    25         } catch (InterruptedException e)  
    26         {  
    27             e.printStackTrace();  
    28         } catch (ExecutionException e)  
    29         {  
    30             e.printStackTrace();  
    31         }  
    32   
    33     }  
    34   
    35     @Override  
    36     public Integer call() throws Exception  
    37     {  
    38         int i = 0;  
    39         for(;i<100;i++)  
    40         {  
    41             System.out.println(Thread.currentThread().getName()+" "+i);  
    42         }  
    43         return i;  
    44     }  
    45   
    46 }  

        4、创建线程的三种方式的对比      

    采用实现Runnable、Callable接口的方式创见多线程时,

    优势是:

    线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。

    在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。

    劣势是:

    编程稍微复杂,如果要访问当前线程,则必须使用Thread.currentThread()方法。

    使用继承Thread类的方式创建多线程时

    优势是:

    编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获得当前线程。

    劣势是:

    线程类已经继承了Thread类,所以不能再继承其他父类。

      5、小结:

          在多线程的使用上:有效利用多线程的关键是理解程序是并发执行而不是串行执行的。例如:程序中有两个子系统需要并发执行,这时候就需要利用多线程编程。

          另外在多线程编程时:

            重点理解:         

          • 线程同步
          • 线程间通信
          • 线程死锁
          • 线程控制:挂起、停止和恢复

        

        参考博客:http://blog.csdn.net/escaflone/article/details/10418651

              http://blog.csdn.net/longshengguoji/article/details/41126119

              http://www.runoob.com/java/java-multithreading.html

              http://www.tiantianbianma.com/java-wait-notify.html/

    千里路我只陪你一程,从此艳阳风雪我不问!
  • 相关阅读:
    关于Android的布局
    一个新的开端
    Flux的基础概念和实战入门
    在Redux中使用插件createAction之后
    学习
    Object.assign() 对象的扩展
    Redux 中的CombineReducer的函数详解
    React组件的防呆机制(propTypes)
    css的新特性 calc () 使用
    shim和polyfill有什么区别
  • 原文地址:https://www.cnblogs.com/huststl/p/7600699.html
Copyright © 2020-2023  润新知