Javase | 多线程

目录:

    • 1.程序
    • 2.进程
    • 3.线程
    • 4.多线程
    • 5.自定义线程类 :
      • 5.1 继承Thread类
      • 5.2 实现Runnable接口
    • 6.多线程的生命周期及状态转换
      • 新建状态 (New)
      • 就绪状态 (Runnable)
      • 运行状态 (Running)
      • 阻塞状态 (Blocked)
      • 死亡状态 (Terminated)
    • 7.多线程的调度
      • 线程的优先级
      • 线程的休眠
      • 线程的让步
      • yield() 和 sleep() 的区别
    • 8.多线程同步和通信
      • 8.1 多线程的同步
      • 8.2 多线程通信

1.程序

程序:是对数据描述操作的。 代码的集合。是应用程序执行脚本静态

2.进程

  • 在一个操作系统中,每个 独立执行程序 都可以称为一个 进程,也就是 正在运行的程序 ,强调了 运行 的状态。
  • 目前大部分计算机安装的都是多任务操作系统,即能同时执行多个应用程序
  • 表明看进程是并发执行的,实际上这些 进程并不是同时运行的,操作系统会为每一个进程分配一段有限的CPU时间,CPU在这段时间执行某个进程,在下段时间切换到另一个进程

3.线程

  • 在一个 进程 ( 正在运行的程序 )中可以有 多个 “执行单元” 同时运行它们可以看做 程序执行的一条条路径,被称为 线程
  • 操作系统每一个进程至少存在一个线程
  • 在一个Java程序启动时会产生一个进程,该进程默认创建。一个线程,在该线程上运行main()方法中的代码

4.多线程

  • 如果希望 程序中实现“多段程序代码” 交替运行的效果,则 需要创建多个线程,即 多线程 程序。
  • 单线程执行时只有一个路径CPU就一直按按照这个路径执行多线程多个路径CPU在多个路径上切换执行

5.自定义线程类 :

  • 继承Thread类重写run( )方法,在run方法中实现线程中的代码。
  • 实现Runnable接口,同样要 重写run( )方法实现在线程上的代码。

5.1 继承Thread类

  • 继承Thread类创建线程步骤 为:

    定义类,声明继承Thread类

    重写Thread的run( )方法,run()方法方法体线程对应的子任务

    创建自定义类的对象,调用start( )方法。start( )方法的作用启动线程调用run( )方法

  • 由于线程的运行顺序随机调度的,因此会出现不同的结果

  • public class Demo {
        public static void main(String[] args) {
            //此代码为多线程"的程序
            System.out.println("Demo类的主线程正在运行。");
            //创建两个线程
            MyThread myThread1 = new MyThread();
            MyThread myThread2 = new MyThread();
    
            //启动线程
            myThread1.start();
            myThread2.start();
    
            System.out.println("Demo的主线程运行结束。");
        }
    }
    
    class MyThread extends Thread{
    
        @Override
        public void run() {
            System.out.println("线程"+Thread.currentThread().getName()+"的run()方法正在运行。");
        }
    }
    

5.2 实现Runnable接口

  • 通过继承Thread类有一定局限性,因为Java只支持单继承一旦继承了某个父类就不能再继承Thread类,这时可通过实现Runnable接口来避免这个问题

  • Thead类提供了另一个 构造方法 Thread(Runable target)Runnable接口中只有一个run( )方法,因此只需传递一个实现Runnable接口的实例对象就能创建线程

    创建的线程会调用实现Runnable接口中的run( )方法,不用调用Thread类的 run( ) 方法。

  • 实现Runnable接口创建线程步骤 为:

    定义类,实现Runnable接口

    重写Runnable接口的run( )方法,run()方法方法体线程对应的子任务

    创建自定义类的对象,创建Thread类,Thread类的构造方法的参数为“自定义的对象”

    调用 start( ) 方法,开始多线程

    调用start( )方法。start( )方法的作用启动线程调用run( )方法

    public class Demo2 {
        public static void main(String[] args) {
            //此代码为多线程的程序
            System.out.println("Demo2类的主线程正在运行。");
    
            //创建自定义的实现了Runnable接口的类
            MyThread2 obj = new MyThread2();
            //创建两个线程,第二个参数为线程指定的名称
            Thread t1 = new Thread(obj, "Thread 1 ");
            Thread t2 = new Thread(obj, "Thread 2 ");
    
            //启动线程
            t1.start();
            t2.start();
            System.out.println("Demo2类的主线程运行结束。");
        }
    }
    
    class MyThread2 implements Runnable {
    
        @Override
        public void run() {
            System.out.println("线程"+Thread.currentThread().getName()+"的run()方法正在运行。");
        }
    }
    

6.多线程的生命周期及状态转换

  • Thread对象创建完成时,线程的生命周期就开始了。当run()方法中代码正常执行完毕抛出一个未捕获的异常或错误时,线程的生命周期将结束

  • 线程生命周期分为五个阶段

    新建状态 (New)

    就绪状态 (Runnable)

    运行状态 (Running)

    阻塞状态 (Blocked)

    死亡状态 (Terminated)

新建状态 (New)

  • 创建一个线程对象后该线程对象就处于新建状态。此时不能运行,和其他Java对象一样,仅为其分配了内存,没有任何线程的动态特征。

就绪状态 (Runnable)

  • 线程对象调用了start( )方法后,该线程就进入“就绪状态,此时具备了运行的条件,能获得CPU的使用权,还需要等待系统的调度。

运行状态 (Running)

  • 就绪状态线程获得了CPU的使用权开始执行run()方法,则线程处于运行状态。当一个线程启动后,它不可能一直处于运行状态

阻塞状态 (Blocked)

  • 一个正在执行的线程某些特殊情况下,如执行耗时的输入/输出操作时会放弃CPU的使用权,进入 阻塞状态。只有当引起 阻塞的原因被消除后线程才可以转入就绪状态

  • 当线程试图获取某个对象的 同步锁 时,如果该锁其他线程所持有时,线程会由运行状态转为阻塞状态

  • 当调用一个阻塞的IO方法,要等待这个阻塞的IO方法返回,此时 线程会由运行状态转为阻塞状态

  • 当线程调用了某个对象的wait()方法,此时 线程会由运行状态转为阻塞状态 。需要使用notify()方法唤醒该线程

  • 当线程调用了Thread的sleep(long millis)方法时,此时 线程会由运行状态转为阻塞状态等到休眠时间到了才能进行“就绪状态

  • 线程调用了另一个线程join()方法时,此时 线程会由运行状态转为阻塞状态,需要等新加入的线程运行结束后才能结束阻塞

死亡状态 (Terminated)

  • 当线程的run()方法中代码正常执行完毕或者抛出一个未捕获的异常或者错误时,线程进入死亡状态。线程将不在拥有运行的资格也不能再转化到其他状态

7.多线程的调度

  • Java虚拟机会按照特定的机制为程序中的 每个线程分配CPU的使用时间,这种机制被称为 线程的调度

  • 线程调度有两种模型 :①分时调度模型抢占式调度模型

    分时调度模型:所有线程轮流平均分配CPU时间

    抢占式调度模型:可运行池中优先级高的线程相同优先级的随机选择一个线程使其占用CPU,它失去了使用权之后,再随机选择其他线程

线程的优先级

  • 线程的优先级1-10之间的整数来表示,数字越大优先级越高。可以用Thread类中的 setPriority(int newPriority) 方法进行设置。优先级高的先运行,优先级低的后运行

  • public class Demo3 {
        public static void main(String[] args) {
            MaxP max = new MaxP();
            MinP min = new MinP();
    
            Thread maxT = new Thread(max, "优先级高的程序"); //设置了优先级,其先执行
            Thread minT = new Thread(min, "优先级低的程序"); //其后执行
    
            /**
             * 正常而言,优先级高的“线程”先执行,优先级低的“线程”后执行。
             */
            maxT.setPriority(10); //设置优先级
            minT.setPriority(1);
    
            maxT.start();
            minT.start();
    
            System.out.println("主线程结束。");
        }
    }
    
    
    class MaxP implements Runnable {
        public static void main(String[] args) {
            System.out.println("主线程正在运行。");
    
        }
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName()+": "+i);
            }
        }
    }
    
    class MinP implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName()+": "+i);
            }
        }
    }
    

线程的休眠

  • 如果希望人为控制线程,使 线程暂停将CPU让给其他线程,可以使用sleep(long millis)

  • public class Demo4 {
        public static void main(String[] args) throws InterruptedException {
            System.out.println("主线程正在运行。");
            //创建自定义线程对象
            MyThread mt = new MyThread();
            //创建线程
            Thread thread = new Thread(mt);
            //启动线程
            thread.start();
            for (int i = 0; i < 10; i++) {
                if (i == 5) {
                    //线程休眠,后进入阻塞状态,休眠结束后,重新进入“就绪状态”
                    Thread.sleep(9000); //当i==5时Thead.sleep(),该for循环休眠了9秒
                }
                System.out.println("主线程输出: "+i);
            }
            System.out.println("主线程运行结束。");
        }
    }
    
    class MyThread implements Runnable {
    
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println("MyThread的输出: "+i);
            }
        }
    }
    

线程的让步

  • 线程让步 可以通过yield()方法来实现,该方法和sleep()方法有点相似都可以让当前 “正在运行” 的线程暂停
  • 区别在于yield()方法不会阻塞该线程,它只是将线程转换为就绪状态,重新进入“线程排队”状态。
    sleep() 会让线程进入阻塞状态睡眠结束后,该线程才重新进入就绪状态,进行“线程排队”。

yield() 和 sleep() 的区别

  • 两个方法功能有点相似 : 都可以让当前“正在运行”的线程暂停最后使线程进入就绪状态,后进行“线程排队
  • yield() 方法不会阻塞该线程,它只是将线程转换为就绪状态,重新进入“线程排队”状态。
    sleep() 会让线程进入阻塞状态睡眠结束后,该线程才重新进入就绪状态,进行“线程排队”。
  • yield() 告诉操作系统当前线程愿意放弃CPU执行时间片,重新进入“就绪状态”,进行“线程排队”。但是否会马上执行该“就绪状态”的线程得看操作系统的分配了。
  • sleep() 使得该线程进入休眠状态,休眠中为“阻塞状态”。当休眠结束时,线程重新进入“就绪状态”,进行“线程排队”。

8.多线程同步和通信

8.1 多线程的同步

  • 多线程的并发执行虽然可以提高效率,但是当多个线程去访问同一个资源时,也会引发一些安全问题
    如:线程A读取资源A时,线程B正在修改资源A。那么线程A会读取到/该读取到修改之前的资源A,还是修改后的资源A呢?
    (可用多线程的同步避免此类问题

  • 多线程同步两种常用方法 :

    同步代码块synchronized(lock) { 操作共享资源代码块 }

    同步方法synchronized 返回值类型 方法名(参数列表)

  • //同步代码块例子
    public class Demo5 {  
        public static void main(String[] args) {
            //自定义线程对象
            SaleTicket sale = new SaleTicket();
            //创建四个线程,模拟四个窗口售票
            Thread t1 = new Thread(sale, "窗口1");
            Thread t2 = new Thread(sale, "窗口2");
            Thread t3 = new Thread(sale, "窗口3");
            Thread t4 = new Thread(sale, "窗口4");
    
            t1.start();
            t2.start();
            t3.start();
            t4.start();
        }
    
    }
    
    class SaleTicket implements Runnable {
        int num = 50;
        @Override
        public void run() {
            while (true) {
                //创建“同步代码块”
                synchronized ("锁") {
                    if (num > 0) {
                        System.out.println(Thread.currentThread().getName() + "售出了第" + num + "号票。");
                        num--;
                    } else {
                        System.out.println("票售完了。");
                        break;
                    }
                }
            }
        }
    }
    
  • public class Demo6 {
        public static void main(String[] args) {
    
            TicketThread2 t = new TicketThread2();
    
            Thread t1 = new Thread(t, "窗口1");
            Thread t2 = new Thread(t, "窗口2");
            Thread t3 = new Thread(t, "窗口3");
            Thread t4 = new Thread(t, "窗口4");
    
            t1.start();
            t2.start();
            t3.start();
            t4.start();
        }
    }
    
    class TicketThread2 implements Runnable {
        //总票数
        int num = 50;
    
        @Override
        public void run() {
            while (true) {
                //调用售票的方法
                sale();
                if (num <= 0) {
                    System.out.println("票卖完了。");
                    break;
                }
            }
        }
    
        private synchronized void sale() { //该方法是同步的
            if (num > 0) {
                System.out.println(Thread.currentThread().getName()+"售出了"+num+"号票。"); //获得线程名字等配合其他信息使用
                num --;
            }
        }
    }
    

8.2 多线程通信

  • 线程通信 指的是一个线程完成了自己的任务时,要通知另外一个线程去完成另外一个任务

  • 要完成线程之间的通信,就需要控制多个线程按照一定的顺序轮流执行

  • Object类中提供了wait(), notify()notifyALL()方法用于解决线程之间的通信问题

  • 这三个方法的调用者都应该是同步锁对象,否则会抛出IllegalMonitorStateException异常

  • 方法名称 功能描述
    void wait() 使当前线程放弃同步锁进入等待,直到其他线程进入此同步锁,并调用notify()或notifyAll()方法唤醒该线程为止。
    void notify() 唤醒同步锁上等待的第一个调用wait()方法的线程。
    void notifyAll() 唤醒此同步锁上调用wait()方法的所有线程。

你可能感兴趣的:(Java知识点-整合,java,开发语言,多线程,同步锁,线程安全)