Java线程编程基础 第一章

 

 

Java线程编程基础 第一章

 

1、本章任务:建立多线程程序

2、本章知识点:

  •   了解Java线程模型
  •   掌握创建线程的方法
  •   设置线程优先级


3、多线程编程概述

  •   Java提供了对多线程编程的内置支持
  •   多线程程序包括能够并发运行的两个或多个代码段

      单线程、多线程对比示例图:

 

       
    从图中可以看出:
    1.单线程中的3段代码只能在不同的时间点执行,
    2.多线程中的3段代码可以在同一时间点执行

4、进程与线程

  • 进程:一个进程是 一个正在执行的程序 ,目前的操作系统大多都支持同时运行两个或多个程序,称为多任务操作系统
  • 线程:单个程序 可以同时执行 两个或更多的任务 ,每个任务称 一个线程 ,进程包含线程
  • 进程是重量级的任务 ,每个进程都有自己独立的地址空间
  • 线程是轻量级的任务 ,他们共享一个地址空间,共享同一个进程
  • 进程间通信代价昂贵而且受限,线程通信很容易
  • 利用多线程可使你的程序最大限度地利用CPU,因为空闲时间被限制在最小


5、
Thread类和Runnable接口

    Java的多线程系统构建在Thread类 、方法以及Runnable接口 之上。
    可以通过继承Thread类或者实现Runnable接口创建一个新线程
   
    Thread类定义了几个管理线程的方法,常用方法如下:
    方法名            功能
    ====================================
    getName        获取线程名
    getPriority      获得线程的优先级
    isAlive            判定线程是否仍在运行
    join                等待一个线程终止
    run                线程入口方法
    sleep             暂停一个线程一段时间
    start              启动线程

6、程序的主线程


    6.1)主线程描述
    Java程序启动时 (从main方法开始),一个线程 立刻开始运行,这个线程称
    为主线程 ,主线程的重要性体现在如下两个方面:
        1)它是产生其他子线程的线程
        2)一般情况下,必须是最后一个结束 执行的线程,
             因为它要执行各种关闭操作


    主线程不但在程序开始时自动创建,也能通过Thread对象来控制,此时需要
    调用:Thread.currentThread()

    6.2)主线程控制示例代码
   

public static void main(String args[ ]){
        Thread t = Thread.currentThread( ); //获取当前线程(主线程)
        System.out.println("当前线程:" + t); //输出:线程名-优先级-线程组名
        //改变主线程名称
        t.setName("My Thread");         System.out.println("改变之后的名称:" + t);

        //打印数字1 ~ 5,每隔1秒打印1次
        try{
            for(int i = 1; i <= 5; i++){
                System.out.println(i);
                Thread.sleep(1000); //休眠1000毫秒
            }
        }catch(InterruptedException e){
             System.out.println( " 主线程中断! " );
        }
    }

 

7、创建线程

    Java定义了两种 创建线程的方法
    1)可以实现Runnable接口 ,这是创建线程最简单的方法,实现Runnable接口
         只需一个简单的run()方法,其声明如下:
        public void run();
         run()方法是线程的进入点,线程在run方法返回时结束
   
    2)可以继承Thread类 ,重写Thread类的run()方法

8、创建线程
方法1:实现Runnable接口

/**
     * 线程类MyThread,实现Runnable接口
     */
    public class MyThread implements Runnable{ //线程类
        //线程入口点
        public void run(){
            try{
                //打印数字1 ~ 5,每隔500毫秒打印一次
                for(int i = 1; i  <= 5; i++){
                    System.out.println("子线程打印:" + i);
                    Thread.sleep(500); //休眠500毫秒
                }
            }catch(InterruptedException e){
                 System.out.println("子线程中断!");
            }
            System.out.println("子线程执行结束!");
        }
    }
   
    /**
     * 线程类MyThread的测试类
     */
    public class TestMyThread{
        public static void main(String args[ ]){
            //创建线程
            Thread t = new Thread(new MyThread(), "Demo Thread");
            System.out.println("子线程被创建:" + t); //显示线程信息
            t.start(); //启动线程
            //主线程中间隔1秒,打印数字1 ~ 5
            try{
                for(int i = 1; i  <= 5; i++){
                    System.out.println("主线程打印:" + i);
                    Thread.sleep(1000); //休眠1000毫秒
                }
            }catch(InterruptedException e){
                    System.out.println("主线程中断!");
            }
            System.out.println("主线程执行结束!");
        }
    }

 

9、创建线程 方法2:继承Thread类

   

/**
     * 线程类MyThread1,继承Thread类
     */
    public class MyThread1 extends Thread{
        public MyThread1(){
            super("Demo Thread"); //线程命名
        }
        //重写线程入口方法
        public void run(){
            try{
                //打印数字1 ~ 5,每隔500毫秒打印一次
                for(int i = 1; i  <= 5; i++){
                    System.out.println("子线程打印:" + i);
                    Thread.sleep(500); //休眠500毫秒
                }
            }catch(InterruptedException e){
                 System.out.println("子线程中断!");
            }
            System.out.println("子线程执行结束!");
        }
    }
   
    /**
     * 线程类MyThread1的测试类
     */
    public class TestMyThread1{
        public static void main(String args[ ]){
            //创建线程
            Thread t = new MyThread1();
            System.out.println("子线程被创建:" + t); //显示线程信息
            t.start(); //启动线程
            //主线程中间隔1秒,打印数字1 ~ 5
            try{
                for(int i = 1; i  <= 5; i++){
                    System.out.println("主线程打印:" + i);
                    Thread.sleep(1000); //休眠1000毫秒
                }
            }catch(InterruptedException e){
                System.out.println("主线程中断!");
            }
            System.out.println("主线程执行结束!");
        }
    }

 


10、如何选择创建线程的方式

    问: Java有两种创建线程的方式,哪种更好?
   
    1. Thread类定义了多个派生类可以重写的方法,run()方法只是其中之一,
    所以只有在需要增强或者修改Thread类时才应该使用继承Thread类方式
   
    2. 如果不想重写Thread类的run()方法外的其他方法,最好还是简单的
    实现Runnable接口(推荐)

11、线程的状态

  • 准备运行状态:ready to run
  • 运行状态:running
  • 暂停状态:suspended 运行中的线程可以暂停,暂停的线程可以重新启动
  • 阻塞状态:blocked 线程在等待其他资源时被阻塞

    (这个状态和视频播放器的操作非常类似,: )
   
    注意:线程在任何时候都能被终止,一旦终止就不能被重新激活
   
12、启动多个线程

   

/**
     * 改进的MyThread线程类(改为自启动线程)
     */
    public class MyThread implements Runnable {
       
        private Thread t;//线程对象
        public Thread getT() {
            return t;
        }
        /**构造方法*/
        public MyThread(String threadName){           
            //创建线程对象
            this.t = new Thread(this, threadName);
            System.out.println("创建子线程:" + threadName);
            //启动线程
            this.t.start();
        }
       
        /**
         * 功能:线程入口方法
         */
        public void run() {
           
            Thread t = Thread.currentThread();
            try {
               
                //打印数字1 ~ 5,间隔1秒
                for(int i = 1; i <= 5; i++){
                    System.out.println(t.getName() + " 打印:" + i);
                    //休眠1秒
                    Thread.sleep(1000);
                }               
            } catch (InterruptedException e) {
                System.out.println("子线程中断!");
            }
           
            System.out.println("子线程结束!");           
        }
    }
   
    /**
     * 多线程测试类
     */
    public class TestMyThread {
        public static void main(String[] args) {
           
            //创建线程对象1
            MyThread t1 = new MyThread("子线程1");
           
            //创建线程对象2
            MyThread t2 = new MyThread("子线程2");
           
            try {               
                //主线程打印数字1 ~ 5,间隔1秒
                for(int i = 1; i <= 5; i++){
                    System.out.println("主线程打印:" + i);
                    //休眠1秒
                    Thread.sleep(1000);
                }
               
            } catch (InterruptedException e) {
                System.out.println("主线程中断!");
            }           
            System.out.println("主线程结束!");
        }
    }

 

13、使用isAlive()join()

    问题: 主线程一般要最后结束,之前的示例中是通过在main方法中加入sleep()
    使主线程休眠足够长的时间以确保主线程最后结束,这个解决方式合理吗?怎样
    才能知道子线程是否终止?或者说怎样才能保证主线程最后结束?

    答案: 有两种方式可以确定一个线程是否结束:
    1)在线程中调用isAlive(),这个方法由Thread定义,如果它调用的线程仍在运行,
         返回true,否则返回false
    2)使用join()方法来等待另一个线程的结束,该方法一直等待直到它调用的线程终止

14、isAlive()和jion()示例

    尝试运行以下代码并观察主线程是否最后结束:

   

public static void main(String[] args) {
        MyThread t1 = new MyThread("子线程1"); // 创建线程1
        MyThread t2 = new MyThread("子线程2"); // 创建线程2
        MyThread t3 = new MyThread("子线程3"); // 创建线程3

            //显示线程是否运行
        System.out.println("子线程1是否运行:" + t1.getT().isAlive());
        System.out.println("子线程2是否运行:" + t2.getT().isAlive());
        System.out.println("子线程3是否运行:" + t3.getT().isAlive());
        // 主线程等待子线程结束
        try {
            System.out.println("等待子线程结束…");
            t1.getT().join(); // 等待t1线程结束
            t2.getT().join(); // 等待t2线程结束
            t3.getT().join(); // 等待t3线程结束
        } catch (InterruptedException e) {
            System.out.println("主线程中断!");
        }
        //显示线程是否运行
        System.out.println("子线程1是否运行:" + t1.getT().isAlive());
        System.out.println("子线程2是否运行:" + t2.getT().isAlive());
        System.out.println("子线程3是否运行:" + t3.getT().isAlive());
        System.out.println("主线程执行结束!");
    }

 


15、线程优先级概述

    Java给每个线程分配一个优先级,以决定哪个线程可以优先分配CPU时间
    优先级是一个整数,用于指定线程的相对优先程度
    优先级可以决定什么时候从一个运行中的线程切换到另一个线程,切换规则如下:

  •   一个线程自愿释放控制(放弃、睡眠或者阻塞),所有其他线
      程被检查,高优先级线程被分配CPU时间
  •   一个线程可以被另一个高优先级线程抢占资源,称为抢占式多任务

    注意:具有同一优先级的线程竞争CPU时间,不同操作系统的处理方式上存在差别

16、线程优先级设置

  • 线程调度器使用线程优先级以决定什么时候允许运行
  • 理论上高优先级的线程将比低优先级的线程得到更多CPU时间
  • 低优先级线程在运行时,高优先级的线程会抢占低优先级线程的执行权
  • 设置线程优先级,使用setPriority()方法:

    final void setPriority(int level);
    参数level为线程的优先级 ,其值在1 ~ 10 之间


    Thread类提供了如下常量方便优先级的设置
    Thread.MIN_PRIORITY == 1
    Thread.MAX_PRIORITY == 10
    Thread.NORM_PRIORITY == 5


17、线程优先级示例

   

/**
     * 功能:计数器线程
     */
    public class Clicker implements Runnable {
       
        private int click = 0;//计数值        
        private Thread t;//线程对象        
        private volatile boolean running = true;//运行开关
       
        public int getClick() {
            return click;
        }       
        public Thread getT() {
            return t;
        }       
        /**构造方法,参数为优先级*/
        public Clicker(int priority){
            this.t = new Thread(this);
            this.t.setPriority(priority);//设置线程优先级
        }
       
        public void run() {           
            while(this.running){               
                this.click++;//计数器累加
            }
        }
       
        public void start(){
            this.t.start();//启动线程
        }       
        public void stop(){
            this.running = false;//结束线程
        }       
    }
   
    /**
     * 计数器线程测试类
     */
    public class TestClicker {
        public static void main(String[] args) {
          
            //创建线程,设置4种档次的优先级
            Clicker hi = new Clicker(Thread.NORM_PRIORITY + 2);
            Clicker hi1 = new Clicker(Thread.NORM_PRIORITY + 4);
            Clicker lo = new Clicker(Thread.NORM_PRIORITY - 2);
            Clicker lo1 = new Clicker(Thread.NORM_PRIORITY - 4);           
            //启动线程
            hi.start();
            hi1.start();
            lo.start();
            lo1.start();
           
            try {               
                Thread.sleep(2000);//休眠2秒                
                //结束线程
                hi.stop();
                hi1.stop();
                lo.stop();
                lo1.stop();
                //等待线程结束
                hi.getT().join();
                hi1.getT().join();
                lo.getT().join();
                lo1.getT().join();               
            } catch (InterruptedException e) {
                System.out.println("主线程中断!");
            }           
            //显示计数器值
            System.out.println("lo1计数器:" + lo1.getClick());
            System.out.println("lo计数器:" + lo.getClick());
            System.out.println("hi计数器:" + hi.getClick());
            System.out.println("hi1计数器:" + hi1.getClick());
            System.out.println("主线程结束!");           
        }
    }

 

18、总结

  • 进程与线程的区别?
  • 如何获取主线程?
  • 创建线程的两种方式?
  • 获知线程是否运行使用哪个方法?
  • 等待线程执行结束使用哪个方法?
  • 线程优先级的范围是什么?

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章