0
点赞
收藏
分享

微信扫一扫

Java中的多线程详解


基本概念

  • 进程:进程就是一个正在运行中的程序(进程是驻留在内存中的)
  • 进程是独立的应用程序,占用cpu资源和物理内存
  • 是系统执行资源分配和调度的独立单位
  • 每一个进程都有属于自己的存储空间和系统资源
  • 注意:多个线程是共享进程的内存的,包括堆和方法区内存,可以共享对象和变量。但是,每个线程都有自己的线程栈,用于存储本地数据。请注意线程之间的数据共享可能会引起线程安全问题,需要使用同步机制来确保正确的访问和修改共享数据。
  • 线程:线程是进程中虚拟的时间片,所谓的多线程并发实际上就是时间片的轮转或者抢占。
  • 线程就是进程中的单个顺序控制流,也可以理解为是一条执行路径。
  • 单线程:一个进程中包含一个顺序控制流(一条执行路径)。
  • 多线程:一个进程中包含多个顺序控制流(多条执行路径)。
  • 在Java语言中:
  • 线程之间堆内存和方法区共享,栈内存私有独立且不共享(一个线程一个栈,OpenJDK中的默认线程栈大小为1MB),还有程序计数器也是独立不共享的。
  • 假设启动10个线程,会有10个栈空间,每个栈和每个栈之间,互不干扰,各自执行各自的,这就是多线程并发。
  • java中之所以有多线程机制,目的就是为了提高程序的处理效率。
  • 对于单核的CPU来说,不能够做到真正的多线程并发,但是可以做到给人一种“多线程并发”的感觉。对于单核的CPU来说,在某一个时间点上实际上只能处理一件事情,但是由于CPU的处理速度极快,多个线程之间频繁切换执行,跟人来的感觉是多个事情同时在做。

并行:

  • 多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时
  • 通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。并发往往在场景中有公用的资源,那么针对这个公用的资源往往产生瓶颈,我们会用TPS或者QPS来反应这个系统的处理能力。
  • 线程安全:指在并发的情况之下,该代码经过多线程使用,线程的调度顺序不影响任何结果。这个时候使用多线程,我们只需要关注系统的内存,cpu是不是够用即可。反过来,线程不安全就意味着线程的调度顺序会影响最终结果。
  • 同步:通过人为的控制和调度,保证共享资源的多线程访问成为线程安全,来保证结果的准确。

线程的状态

Java官方API将线程的整个生命周期分为六个状态,分别是NEW(新建状态)、RUNNABLE(可运行状态)、BLOCKED(阻塞状态)、WAITING(等待状态)、TIMED_WAITING(定时等待状态)和TERMINATED(终止状态)。线程的不同状态表明了线程当前正在进行的活动,在程序中,通过一些操作,可以使线程在不同状态之间转换。

NEW 新建状态

  • 当一个线程创建以后,就处于新建状态。只有它调用start()方法的时候这个状态才会改变,线程会进入锁池状态。

BLOCKED 锁池状态

  • 进入锁池以后就会参与锁的竞争,当它获得锁以后还不能马上运行,因为一个单核CPU在某一时刻,只能执行一个线程,所以他需要操作系统分配给它时间片,才能执行。

RUNNABLE 就绪、运行状态(可执行状态)

  • Java 把就绪(Ready)和执行(Running)两种状态合并为一种状态:
  • 可执行(RUNNABLE)状态(或者可运行状态)。
  • 调用了线程的 start()实例方法后,线程就处于就绪状态。
  • 就绪状态就绪状态仅仅表示线程具备运行资格,如果没有被操作系统的调度程序挑选中,线程就永远是就绪状态;当前线程进入就绪状态的条件,大致包括以下几种:
  • 调用线程的 start()方法,此线程进入就绪状态。
  • 当前线程的执行时间片用完。
  • 线程睡眠(sleep)操作结束。
  • 对其他线程合入(join)操作结束。
  • 等待用户输入结束。
  • 线程争抢到对象锁(Object Monitor)。
  • 当前线程调用了 yield 方法出让 CPU 执行权限。
  • 当一个持有对象锁的线程获得CPU时间片以后,开始执行 run( )方法中的业务代码,线程处于执行状态。
  • 执行状态:线程调度程序从就绪状态的线程中选择一个线程,作为当前线程时线程所处的状态。这也是线程进入执行状态的唯一方式

WAITING 状态

  • 处于 WAITING(无限期等待)状态的线程不会被分配 CPU 时间片,需要被其他线程显式地唤醒,才会进入就绪状态。
  • 线程调用以下 3 种方法,会让自己进入无限等待状态:
  • Object.wait() 方法,对应的唤醒方式为: Object.notify() / Object.notifyAll()。
  • Thread.join() 方法,对应的唤醒方式为:被合入的线程执行完毕。
  • LockSupport.park() 方法,对应的唤醒方式为: LockSupport.unpark(Thread)。
  • TIMED_WAITING 状态
  • 处于 TIMED_WAITING(限时等待)状态的线程不会被分配 CPU 时间片,如果指定时间之内没有被唤醒,限时等待的线程会被系统自动唤醒,进入就绪状态。
  • 以下 3 个方法会让线程进入限时等待状态:
  • Thread.sleep(time) 方法,对应的唤醒方式为:
  • sleep 睡眠时间结束。
  • Object.wait(time) 方 法 , 对 应 的 唤 醒 方 式 为 :
  • 调 用 Object.notify() /Object.notifyAll()去主动唤醒,或者限时结束。
  • TERMINATED 状态
  • 线程结束任务之后,将会正常进入 TERMINATED(死亡)状态;或者说在线程执行过程中发生了异常(而没有被处理),也会导致线程进入死亡状态。
    Java中的多线程详解_多线程编辑
  • 注意:
  • 1.当线程调用sleep()方法或当前线程中有其他线程调用了带时间参数的join()方法的时候进入了定时等待状态(TIMED_WAITING)
  • 2.当其他线程调用了不带时间参数的join()(join内部调用的是sleep,所以可看成sleep的一种)方法时进入等待状态(WAITING)。
  • 3.当线程遇到I/O的时候还是运行状态(RUNNABLE)
  • 4.当一个线程调用了suspend()方法挂起的时候它还是运行状态(RUNNABLE)。

线程创建

        继承Thread类

  • 将一个类声明为Thread的子类。这个子类应该重写run类的方法Thread。然后可以分配并启动子类的实例。
  • 可以分为三步:
  • 自定义线程类继承Thread类。
  • 重写run()方法,编写线程执行体。
  • 创建线程对象,调用start()方法启动线程。
  • 案例一

public class Example2 {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.run();
        while (true){
            System.out.println("Demo1类的main方法运行");
        }
    }
}
class MyThread {
    public void run(){
        while (true){
            System.out.println("MyThread类的run方法运行");
        }
    }
}

Java中的多线程详解_多线程_02

        分析:

                该程序一直在运行myThread对象的run方法,而不会运行System.out.println("Demo1类的main方法运行");语句,因为该程序是一个线程程序,是一条单路径。myThread.run()语句是一个死循环程序,不运行完不会执行System.out.println("Demo1类的main方法运行");语句。

  •  案例二:多线程

package demo02;
 
public class TestThread {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
        while (true){
            System.out.println("Demo类的main运行方法");
        }
 
    }
 
}
class MyThread extends Thread{
    public void run(){
        while (true){
            System.out.println("MyThread类的run方法运行");
        }
    }
}

Java中的多线程详解_就绪状态_03

  • 案例三:多线程

package demo03;
 
public class Test {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();  //开辟新线程
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName()+"---"+i);
        }
    }
}
class MyThread extends Thread{
    @Override
    public void run() {
        Thread.currentThread().setName("子线程");
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName()+"---"+i);
        }
    }
}

Java中的多线程详解_就绪状态_04

        继承Runnable接口

  •  定义MyRunnable类实现Runnable接口
  •  实现run()方法,编写线程执行体
  • 创建线程对象,调用start()方法启动线程
  • 案例一

class MyThread implements Runnable{
    public void run(){
        while (true){
            System.out.println("MyThread类的run方法");
        }
    }
}
public class Demo01 {
    public static void main(String[] args) {
        //创建一个实现了Runnable接口的对象
        MyThread myThread = new MyThread();
        //将该对象传给Thread类的构造函数
        Thread thread = new Thread(myThread);
        //thread对象调用start方法,在这里不会调用自身的run方法,它会调用myThread对象的run方法
        thread.start();
        while (true){
            System.out.println("Demo类的main方法");
        }
    }
}

Java中的多线程详解_System_05

  • 案例二

public class MyThread implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName()+"---"+i);
        }
    }
}
class Test{
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        Thread thread1 = new Thread(myThread);
        thread1.start();
        Thread thread2 = new Thread(myThread);
        thread2.start();
    }
}

Java中的多线程详解_多线程_06

  •  总结:
  • 继承Thread类
  • 子类继承Thread类具有多线程能力
  • 启动线程:子类对象.start()
  • 不建议使用:避免OOP单继承局限性
  • 实现Runnable接口
  • 实现接口Runnable具有多线程能力
  • 启动线程:传入目标对象+Thread对象.start()
  • 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用

实现Callable接口(了解)

  • 实现Callable接口,需要返回值类型
  • 重写call方法,需要抛出异常
  • 创建目标对象
  • 创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);
  • 提交执行:Future<Boolean> result1 = ser.submit(t1);
  • 获取结果:boolean r1 = result1.get()
  • 关闭服务: ser.shutdownNow();


举报

相关推荐

0 条评论