Java语言基础--进程与线程


概念

进程就是运行在系统中的代码集合(程序,它占用一定的内存和CPU资源,存在生存和消亡的动态过程

线程由进程创建,是进程的实体,一个进程可以有多个线程

并发:同一时刻,多个任务交替执行,貌似同时的错觉(单核CPU实现多任务就是并发)

并行:同一时刻,多个任务同时执行(多核CPU可以实现并行)

并行和并发可能同时存在

类图

使用

  • 继承Thread类(源码解读)

    public class Thread01 {
        public static void main(String[] args) throws InterruptedException {
            //创建Cat对象,可以当做线程使用
            Cat cat = new Cat();
            //启动线程-> 执行抽象方法start0->底层使用C实现根据不同系统多线程->根据Runnable接口原则来调用run方法
            cat.start();
            }
        }
    }
    //Cat类继承Thread类
    class Cat extends Thread{
        @Override
        public void run(){
            //业务代码
        }
    }
    
  • 实现Runnable接口(源码解读:代理模式)

    public class Thread02 {
        public static void main(String[] args) {
          Dog dog = new Dog();
            Thread thread = new Thread(dog);
            thread.start();
            /*
            Thread类使用了代理模式,内部使用以下代码来实现代理,原理上还是使用Thread类
            
            private Runnable target = null;//属性,类型是 Runnable
            @Override
            public void run() {
                if (target != null) {
                    target.run();//动态绑定(运行类型Dog)
                }
            }
            
            */
        }
    }
    //通过实现Runnable接口,开发线程
    class Dog implements Runnable { 
        @Override
        public void run() {
            //业务代码
        }
    }
    

多线程安全

编程模拟三个售票窗口,售票100,分别使用继承和实现方式来实现,发现线程间出现超卖现象
后面学习同步机制(Synchronized)可以解决以上问题

线程终止

  • 线程完成任务后,自动终止

  • 通过变量控制终止,即通知终止

    public class ThreadExit_ {
        public static void main(String[] args) throws InterruptedException {
            T t1 = new T();
            t1.start();
    
            //如果希望main线程去控制t1 线程的终止, 必须可以修改 loop
            //让t1 退出run方法,从而终止 t1线程 -> 通知方式
    
            //让主线程休眠 10 秒,再通知 t1线程退出
            System.out.println("main线程休眠10s...");
            Thread.sleep(10 * 1000);
            t1.setLoop(false);
        }
    }
    
    class T extends Thread {
        private int count = 0;
        //设置一个控制变量
        private boolean loop = true;
        @Override
        public void run() {
            while (loop) {
    
                try {
                    Thread.sleep(50);// 让当前线程休眠50ms
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("T 运行中...." + (++count));
            }
    
        }
    
        public void setLoop(boolean loop) {
            this.loop = loop;
        }
    }
    

线程常用方法

  • setName : 设置线程名称,使之与参数name相同

  • getName:获取该线程名称

  • start:开启线程执行,Java虚拟机底层调用该线程的start0方法

  • run:底层调用的抽象接口方法,里面是具体的业务代码

  • setPriority:设置线程优先级(MAX=10,MIN=1,NORM=5)

  • getPriority:获取线程优先级

  • sleep:在指定毫秒数内让当前线程休眠(暂停),方法中可能会抛出InterruptedException异常,要明确继续抛或者捕捉

  • interrupt:中断线程,不是终止线程,一般用于中断线程休眠行为(主动抛出InterruptedException异常给线程)

  • yield:线程的礼让,让出cpu,让其他线程执行,但礼让的时间不确定,所以不一定礼让成功(静态方法,归属Thread类)

  • join:线程的插队,插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务(调用对方的join方法,让对方插队)

  • 用户线程(又名工作线程)和守护线程

    • 用户线程:当线程的任务执行完成自然结束或者已通知方式结束
      • 使用start方法启动
    • 守护线程:一般是为用户线程服务和管理,当所有的用户线程结束,守护线程自动结束(常见守护线程-垃圾回收机制)
      • 先使用线程方法setDaemon(true)将线程属性标记为守护线程
      • 使用start方法启动

线程的生命周期

NEW: 尚未启动的线程处于此状态

RUNNABLE: 在Java虚拟机中执行的线程处于此状态

BLOCKED: 被阻塞等待监视器锁定的线程处于此状态

WAITING: 正在等待另一个线程执行特定动作的线程处于此状态

TIMED_WAITING: 正在等待另一个线程执行动作达到指定等待时间的线程处于此状态

TERMINATED: 已退出的线程处于此状态

同步机制(Synchronized)

在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就是用同步访问技术,保证数据在同一时刻,最多有一个线程访问,以保证数据的完整性,但会导致执行效率要降低

Java中引入了对象互斥锁,来保证共享数据操作的完整性,使用关键字synchronized来修饰对象来表明该对象在任意时刻只能由一个线程访问(颗粒理解为在对象身上寄存锁,当一个线程是抢到使用权限时会锁上,避免其他线程执行这段代码,其他线程都要等该线程使用完后开锁,才可以继续抢使用权,但是这个机制并不公平,可以让同一个线程一直抢到)

使用

  • 同步代码块
synchronized (对象){ 得到对象的锁,才能操作同步代码
    //需要被同步的代码;
}
  • 同步方法
public synchronized void m(String name){
    //需要被同步的代码
}

注意:

  1. 在同一时刻,只能有一个线程来执行被标记为同步的代码

  2. 非静态修饰锁是this(实例化对象),也可以匙其他对象(但必须是同一个对象),静态修饰锁是当前类本身(当前类名.class),也就是说,要让所有涉及的线程都使用同一把锁(这里的锁在同一对象上)才能保证同步机制正常运行

  3. 使用步骤:分析上锁的代码,选择同步代码块或者同步方法,要求多线程的锁对象为同一个即可

死锁

要避免此现象,多个线程互相占用对方锁,不肯相让,导致死锁

演示核心代码

释放锁

下面操作会释放锁

  • 当前线程的同步代码执行结束
  • 当前线程在同步代码中遇到break、return
  • 当前线程在同步代码中出现了未处理的Error或Exception,导致异常结束
  • 当前线程在同步代码中执行了线程对象的wait()方法,当前线程暂停,并释放锁

下面操作不会释放锁

  • 线程执行同步代码中执行了Thread.sleep()、Thread.yield()暂停线程的执行,不会释放锁
  • 线程执行同步代码时,其他线程调用了该线程的suspend()方法来挂起该线程,并不会释放锁(应该避免使用suspend、resume,它们已被弃用)