详解 Java 多线程(笔记)

什么是进程和线程?

几乎所有的操作系统都支持进程的概念,所有运行中的任务通常对应一个进程 (Process)。当一个程序进入内存运行时,即变成一个进程。进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。

  • 独立性:进程是系统中独立存在的实体,它可以拥有自己独立的资源,每一个进程都拥有自己私有的地址空间。在没有经过进程本身允许的情况下,一个用户进程不可以直接访问其他进程的地址空间。

  • 动态性:进程与程序的区别在于,程序只是一个静态的指令集合,而进程是一个正在系统中活动的指令集合。在进程中加入了时间的概念。进程具有自己的生命 周期和各种不同的状态,这些概念在程序中都是不具备的。

  • 并发性:多个进程可以在单个处理器上并发执行,多个进程之间不会互相影响。

大部分操作系统都支持多进程并发运行,现代的操作系统几乎都支持同时运行多个任 务。

多线程则扩展了多进程的概念,使得同一个进程可以同时并发处理多个任务。线程 (Thread)也被称作轻量级进程(Lightweight Process),线程是进程的执行单元。 就像进程在操作系统中的地位一样,线程在程序中是独立的、并发的执行流。当进程被初始化后,主线程就被创建了。对于绝大多数的应用程序来说,通常仅要求有一个主线程,但也可以在该进程内创建多条顺序执行流,这些顺序执行流就是线程,每个线程也是互相独立的。 线程是进程的组成部分,一个进程可以拥有多个线程,一个线程必须有一个父进程。 线程可以拥有自己的堆栈、自己的程序计数器和自己的局部变量,但不拥有系统资 源,它与父进程的其他线程共享该进程所拥有的全部资源。因为多个线程共享父进程 里的全部资源,因此编程更加方便;但必须更加小心,因为需要确保线程不会妨碍同 一进程里的其他线程线程可以完成一定的任务,可以与其他线程共享父进程中的共享 变量及部分环境,相互之间协同来完成进程所要完成的任务。

简单来说:

  • 进程:进程是系统进行资源分配和调度的基本单位,可以将进程理解为一个正在执行的程序。

  • 线程:线程是程序执行的最小单位,一个进程可由一个或多个线程组成

总结:操作系统可以同时执行多个任务,每个任务就是进程;进程可以同时执行多个任务,每个任务就是线程。

Java 线程实现

Java 提供了三种实现线程的方式:

  • 继承 Thread 类创建线程类

  • 实现 Runnable 接口创建线程类

  • 实现 Callable 接口,重写call方法

一、继承 Thread 类创建线程类

实现步骤:

  1. 定义类继承Thread类,并重写Thread类的run()方法,该run()方法的方法体就代表了线程需要完成的任务。因此把run()方法称为线程执行体。

  2. 创建Thread子类的实例,即创建了线程对象。

  3. 调用线程对象的start()方法来启动该线程。

public class ThreadDemo {
    public static void main(String[] args) {
        MyThread mt1 = new MyThread();
        MyThread mt2 = new MyThread();
        // 调用start方法方可启动线程
        // 不能调用run()方法,run方法只是thread的一个普通方法,还是在主线程里执行。
        mt1.start();
        mt2.start();

        System.out.println("---------------------------------");
    }
}
/**
 * 继承Thread类创建线程类 继承Thread类,重写run方法
 */
class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("线程ID:" + getId());
        System.out.println("线程名称:" + getName());

        for (int i = 0; i < 50; i++) {
            System.out.println("线程ID:" + getId() + ";线程名称:" + getName());
        }
    }
}

运行结果:

 从程序可以看出,现在的两个线程对象是交错运行的,哪个线程对象抢到了 CPU 资源,哪个线程就可以运行,所以程序每次的运行结果肯定是不一样的,在线程启动虽然调用的是 start() 方法,但实际上调用的却是 run() 方法定义的主体。

不建议使用:避免 OOP 单继承局限性

二、实现Runnable接口创建线程类

实现步骤:

  1. 定义类实现Runnable接口,并重写Runnable接口的run()方法,该run()方法的 方法体就代表了线程需要完成的任务

  2. 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对 象,该Thread对象才是真正的线程对象

  3. 调用该Thread对象的start()方法来启动该线程

public class RunnableDemo {
    public static void main(String[] args) {
        //创建Runnable实现类的实例
        MyRunnable mr = new MyRunnable();

        //创建Thread类,并把Runnable实现类的实例作为参数,用来开启线程(代理)
        Thread t1 = new Thread(mr);
        Thread t2 = new Thread(mr);
        //开启线程
        t1.start();
        t2.start();
    }
}
class MyRunnable implements Runnable{

    @Override
    public void run() {
        // 实现Runnable接口的,无法直接使用getId(),getName()等方法
        // 需要使用Thread.currentThread() 来获取到当前对象才行
        System.out.println("线程ID:" + Thread.currentThread().getId());
        System.out.println("线程名称:" + Thread.currentThread().getName());

        for (int i = 0; i < 50; i++) {
            System.out.println("线程ID:" + Thread.currentThread().getId() + ";线程名称:" + Thread.currentThread().getName());
        }
    }
}

 

运行结果:

 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用

通过 Thread 类和 Runable 接口都可以实现多线程,那么两者有哪些联系和区别呢?

Thread 类的定义

public class Thread extends Object implements Runnable{}

从 Thread 类的定义可以清楚的发现,Thread 类也是 Runnable 接口的子类,但在Thread类中并没有完全实现 Runnable 接口中的 run() 方法。在 Thread 类中的 run() 方法调用的是 Runnable 接口中的 run() 方法,也就是说此方法是由 Runnable 子类完成的,所以如果要通过继承 Thread 类实现多线程,则必须覆写 run()。

实际上 Thread 类和 Runnable 接口之间在使用上也是有区别的,如果一个类继承 Thread类,则不适合于多个线程共享资源,而实现了 Runnable 接口,就可以方便的实现资源的共享。

三、实现 Callable 接口

实现步骤:

  • 实现 Callable 接口,需要返回值类型

  • 重写 call 方法,需要抛出异常

  • 创建目标对象

  • 创建执行服务

  • 提交执行,获取结果

  • 关闭服务

public class CallableDemo {
    //实现 Callable 接口,需要返回值类型
    public static class MyCallableClass implements Callable<String>{
        //标志位
        private int flag = 0;

        public MyCallableClass(int flag) {
            this.flag = flag;
        }

        //重写 call 方法,需要抛出异常
        @Override
        public String call() throws Exception {
            if (this.flag == 0){
                // 如果flag的值为0,则立即返回
                return "flag = 0";
            }
            if (this.flag == 1){
                // 如果flag的值为1,做一个无限循环
                try{
                    while (true){
                        System.out.println("looping...");
                        Thread.sleep(2000);
                    }
                }catch (InterruptedException e){
                    System.out.println("Interrupted");
                }
                return "false";
            }else {
                // falg不为0或者1,则抛出异常
                throw new Exception("Bad flag value!");
            }
        }
    }

    public static void main(String[] args) {
        //创建目标对象
        MyCallableClass task1 = new MyCallableClass(0);
        MyCallableClass task2 = new MyCallableClass(1);
        MyCallableClass task3 = new MyCallableClass(2);

        //创建一个执行任务的服务
        ExecutorService es = Executors.newFixedThreadPool(3);// nThreads 池中的线程数
        try {
            // 提交并执行任务,任务启动时返回了一个 Future对象
            // 如果想得到任务执行的结果或者是异常可对这个Future对象进行操作
            Future<String> future1 = es.submit(task1);
            // 获得第一个任务的结果,如果调用get方法,当前线程会等待任务执行完毕后才往下执行
            System.out.println("task1: " + future1.get());

            Future<String> future2 = es.submit(task2);
            // 等待5秒后,再停止第二个任务。
            Thread.sleep(5000);
            //如果执行此任务的线程应该被中断;否则,允许进行中的任务完成
            System.out.println("task2 cancel: " + future2.cancel(true));

            // 获取第三个任务的输出,因为执行第三个任务会引起异常
            // 所以下面的语句将引起异常的抛出
            Future<String> future3 = es.submit(task3);
            System.out.println("task3: " + future3.get());


        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值