zoukankan      html  css  js  c++  java
  • Java学习笔记-8.多线程编程

    一、引入线程

    1.多线程和多进程的区别

      (1)两者粒度不同,进程是由操作系统来管理,而线程则是在一个进程内

      (2)每个进程是操作系统分配资源和处理器调度的基本单位,拥有独立的代码、内部数据和状态

         而一个进程内的多线程只是处理器调度的基本单位,共享该进程的资源,线程间有可能相互影响

      (3)线程本身的数据通常只有寄存器数据,以及一个程序执行时使用的堆栈,所以线程的切换比进程切换的负担小

    2.Thread类:Java的线程是通过java.lang.Thread类来实现,一个Thread对象代表一个线程

    3.Runnable接口:只有一个方法run(),所有实现Runable接口的用户类都必须具体实现run()

       当线程被调度并转入运行状态时,它所执行的就是run()方法中定义的操作,所以,一个实现Runnable接口的类实际上是定义一个新线程的操作

       Thread类实现了Runnable接口

    二、线程的实现

    1.实现多线程:继承Thread类构造线程、实现Runnable接口构造线程

    2.继承Thread类:必须覆写Thread类中的run()方法,run()方法中定义了线程要执行的代码

       定义语法:

    class 类名称 extends Thread {
        属性:
        方法:
        //覆写Thread类中的run方法
        public void run() {
            线程主题;
        }
    }

      继承Thread类创建和执行多线程步骤:

    (1)定义一个类扩展Thread

    (2)覆盖run() 方法,这个方法中实现线程中要执行的操作

    (3)创建一个这个线程类的对象

    (4)调用start() 方法启动线程对象

    例:

    class CountingThread extends Thread {
        private String name;
        public CountingThread(String name) {
            this.name = name;
        }
        public void run() {
            System.out.println("Thread start:" + this.name);
            for (int i = 0; i < 9; i++) {
                System.out.println(name + " run:" + (i + 1) + "	");
            }
            System.out.println("Thread finish:" + this.name);
        }
    }
    class ThreadDemo {
        public static void main(String[] args) {
            CountingThread thread1 = new CountingThread("Thread A");
            CountingThread thread2 = new CountingThread("Thread B");
            thread1.start();
            thread2.start();
        }
    }

       不能直接调用run() 方法的原因:当调用start() 方法时,系统启动线程,并分配虚拟CPU开始执行这个线程的run() 方法后,立即返回,而不是等到run()方法执行后返回

    3.实现Runnable接口

      (1)定义一个类实现Runnable接口:implements Runnable

      (2)覆写其中的run() 方法

      (3)创建Runnable 接口实现类的对象

      (4)创建Thread类的对象(以Runnable子类对象为构造方法参数)

      (5)用start() 方法启动线程

    例:

    class CountingThread implements Runnable {
        private String name;
        public CountingThread(String name) {
            this.name = name;
        }
        public void run() {
            System.out.println("Thread start:" + this.name);
            for(int i = 0; i < 10; i++) {
                System.out.println(name + " run: i = " + i);
            }
            System.out.println("Thread end:" + this.name);
        }
    }
    public class RunnableDemo {
        public static void main(String[] args) {
            CountingThread ct1 = new CountingThread("Thread A");
            CountingThread ct2 = new CountingThread("Thread B");
            Thread thread1 = new Thread(ct1);
            Thread thread2 = new Thread(ct2);
            thread1.start();
            thread2.start();
        }
    }

    4.两种实现方式的对比

      (1)使用 Runnable 接口,可以避免由于Java的单继承带来的局限

      (2)实现Runnable接口适合多个相同程序代码的线程去处理同一资源的情况

      所以在开发中建议使用Runnable接口实现多线程

    三、线程的调度

    1.线程的生命周期

      (1)新建状态:Thread myThread = new MyThreadClass()

                 当一个线程处于新建状态时,它仅仅是一个空的线程对象,系统不为它分配资源

      (2)就绪状态:也成为可运行状态(Runnable),方法:start()

                 处于新建状态的线程被启动后,将进入线程队列排队等待CPU时间片

                 此时它已经具备了运行的条件,一旦轮到它来享用CPU资源,就可以脱离创建它的主线程独立开始自己的生命周期

      (3)运行状态:当就绪状态的线程被调度并获得处理器资源时,便进入运行状态

                             当线程对象被调度执行时,它将自动调用本对象的 run() 方法,并顺序执行

      (4)阻塞状态:一个正在执行的线程如果在某些特殊情况下,不能执行线程的状态,将让出CPU

      (5)死亡状态:一个正常运行的线程完成了全部工作,即执行完了run()方法的最后一个语句

                             或线程被提前强制性执行,如通过执行 stop() 方法终止线程

    2.线程的优先级:Java将线程的优先级分为10个等级,分别用1~10表示,数字越大表明级别越高

       Thread类中静态常量:

    定义 描述 表示的常量
    public static final int MIN_PRIORITY 最低优先级 1
    public static final int NORM_PRIORITY 普通优先级,默认优先级 5
    public static final int MAX_PRIORITY 最高优先级 10

       方法:setPriority(int p) :改变线程的优先级

                getPriority():获得线程的优先级

       优先级高的线程会获得较多的运行机会

    四、线程的基本控制

    1.线程睡眠:public static void sleep(long millis) throws InterruptedException

       当前线程将睡眠millis毫秒,可以使优先级低的线程得到执行的机会

    2.线程状态测试:

       线程由start()方法启动后,直到其被终止之间的任何时刻,都处于活动状态

       可以通过Thread中的 isAlive() 方法来获取线程是否处于活动状态

       定义:public final boolean isAlive()

    3.线程加入:public final void join() throws InterruptedException

       有一个A线程正在运行,希望插入一个B线程,并要求B线程先执行完毕,然后再继续A的执行

    4.线程礼让:public static void yield()

       不能指定暂停多长时间,会将CPU的占有权交给具有相同优先级的线程,否则继续运行原线程

       注意:只能让同优先级的线程有执行机会

    5.守护线程:不是应用程序的核心部分,当一个应用程序的所有非守护线程终止运行时,即使仍然有守护线程在运行,应用程序也将终止

       守护线程一般被用于在后台为其他线程提供服务

       方法:public final boolean isDaemon()    判断一个线程是否是守护线程

                public final void setDaemon()    将一个线程设为守护线程

    五、多线程的同步与死锁

    1.对象互斥锁:阻止多个线程同时访问一个条件变量,使用synchronized来声明一个操作共享数据的一段代码块或一个方法

      (1)同步代码块:任何时刻只能有一个线程能获得此代码块的访问权

              synchronized(<同步对象名>) {

                     <需要同步的代码>

              }

      (2)同步方法:任何时刻该方法只能被一个线程执行

              synchronized <方法返回值类型> <方法名>(<参数列表>) {

                     <方法体>

              }

    2.线程间交互同步

    等待通知机制:(1)在生产者没有生产之前,通知消费者等待,生产者生产后,马上通知消费者消费

                         (2)在消费者消费后,通知生产者已经消费完,需要生产

    方法:

    方法名称 描述
    public final void wait() throws InterruptedException 释放已持有的锁,进入等待队列
    public final void wait(long timeout) throws InterruptedException 指定最长的等待时间,单位为毫秒
    public final void notify() 唤醒第一个等待的线程并把它移入锁申请队列
    public final void notifyAll() 唤醒全部等待的线程并将它们移入锁申请队列

    注意:

    wait() 和 notify()/notifyAl()必须在已经持有锁的情况下执行,它们只能出现在synchronized作用的范围内

  • 相关阅读:
    Java 异常Exception e中e的getMessage()和toString()以及 e.printStackTrace();方法的区别
    js几秒以后倒计时跳转示例
    Java读取property配置文件
    js 设置下拉框的默认值
    JS的可枚举性
    Object的原型拷贝-create、assign、getPrototypeOf 方法的结合
    JS 事件循环机制
    vue nextTick深入理解-vue性能优化、DOM更新时机、事件循环机制
    vue 实战问题-watch 数组或者对象
    vue2.0读书笔记2-进阶
  • 原文地址:https://www.cnblogs.com/trj14/p/4311872.html
Copyright © 2011-2022 走看看