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/

 

posted @ 2017-09-27 11:09  huster-stl  阅读(204)  评论(0编辑  收藏  举报