JAVA学习笔记04——线程基础01

2021年7月22日

线程介绍

前置概念

  • 程序(program)

    是为完成特定任务、用某种语言编写的一组指令的集合。简单的说:就是我们写的代码

  • 进程

    • 进程是指程序中运行的程序,比如我们使用qq,就启动了一个进程,操作系统就会为该进程分配内存空间,当我们使用迅雷,又启动了一个进程,操作系统就会为迅雷分配新的内存空间。
    • 进程是程序的一次执行过程,或者是正在运行的一个程序。是动态过程:有它自身的产生、存在和消亡的过程

什么是线程

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

线程相关概念

  • 单线程:同一个时刻,只允许执行一个线程
  • 多线程:同一个时刻,可以执行多个线程,比如:一个QQ进程,可以同时打开多个聊天窗口,一个迅雷进程,可以同时下载多个软件
  • 并发:同一个时刻,多个任务交替执行,造成一种“貌似同时”的错觉,简单的说,单核COU实现的多任务就是并发
  • 并行:同一个时刻,多个任务同时执行。多核CPU可以实现并行。

线程基本使用1——继承Thread类

  • 创建线程的两种方式

    在java中使用线程有两种方法

    • 继承Thread类,重写run方法
    • 实现Runnable接口,重写run方法
  • 案例:每隔一秒钟输出“喵喵喵”

    public class ThreadUse {
        public static void main(String[] args) {
            Cat cat = new Cat();
            // 启动线程
            cat.start();
        }
    }
    
    class Cat extends Thread {
        int times = 0;
        @Override
        public void run() {
            while (true) {
                System.out.println("喵喵喵"+times);
                times++;
                // ctrl+alt+t
                try {
                    // 休眠1秒
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (times >= 80) {
                    break;
                }
            }
        }
    }
    

多线程机制

  • package com.hspedu;
    
    public class ThreadUse {
        public static void main(String[] args) {
            Cat cat = new Cat();
            // 启动线程
            cat.start();
            // 当main线程启动子线程后Thread-0后,程序不会阻塞在start()方法,而是继续执行
          // 注意是start()不是run(),详见下文
            System.out.println("主线程继续执行" + Thread.currentThread().getName());
            for (int i = 0; i < 10; i++) {
                System.out.println("主线程 i = " + i);
                // 让主线程休眠1秒
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    class Cat extends Thread {
        int times = 0;
        @Override
        public void run() {
            while (true) {
                times++;
                System.out.println("喵喵喵" + times + "  线程名:" + Thread.currentThread().getName());
                // ctrl+alt+t
                try {
                    // 休眠1秒
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (times >= 80) {
                    break;
                }
            }
        }
    }
    //执行结果:
    //主线程继续执行main
    //主线程 i = 0
    //喵喵喵1  线程名:Thread-0
    //主线程 i = 1
    //喵喵喵2  线程名:Thread-0
    //主线程 i = 2
    //喵喵喵3  线程名:Thread-0
    //主线程 i = 3
    //喵喵喵4  线程名:Thread-0
    //主线程 i = 4
    //喵喵喵5  线程名:Thread-0
    //主线程 i = 5
    //喵喵喵6  线程名:Thread-0
    //主线程 i = 6
    //喵喵喵7  线程名:Thread-0
    //主线程 i = 7
    //喵喵喵8  线程名:Thread-0
    //主线程 i = 8
    //主线程 i = 9
    
  • 主线程结束,如果有子线程在运行,并不代表整个进程、程序结束。

    未命名文件 (2).jpg
  • run()方法只是一个普通的方法,没有开启线程

  • 读源码:

    // (1)
    public synchronized void start() {
        start0();
    }
    // (2)
    // start0() 是本地方法,是JVM调用,底层是c/c++实现
    // 真正实现多线程的效果,是start0(),而不是run()
    private native void start0();
    
  • start()方法调用start0()方法后,该线程并不一定会立马执行,只是将线程变成了可运行状态,具体什么时候执行,取决于CPU,由CPU统一调度。涉及到具体的操作系统。

线程基本使用2——实现Runnable接口

  • public class RunnableUse {
        public static void main(String[] args) {
            Dog dog = new Dog();
            // 这里不能调用start(),更不应该调用run()
            // 创建Thread对象,将实现了Runnable接口的放入
            Thread thread = new Thread(dog);
            thread.start();
        }
    }
    
    class Dog implements Runnable {
        int count = 0;
        @Override
        public void run() {
            while (true) {
                count++;
                System.out.println("汪汪汪" + count + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (count >= 80) {
                    break;
                }
            }
        }
    }
    
  • Runnable 底层使用了设计模式[代理模式]=>代码模拟实现Runnable接口

线程基本使用3——多线程执行

  • public class MoreThreads {
        public static void main(String[] args) {
            Thread1 thread1 = new Thread1();
            Thread2 thread2 = new Thread2();
            Thread th1 = new Thread(thread1);
            Thread th2 = new Thread(thread2);
            th1.start();
            th2.start();
            System.out.println("主线程完毕")
        }
    }
    
    class Thread1 implements Runnable {
        int count = 0;
        @Override
        public void run() {
            while (true) {
                count++;
                System.out.println("Hello " + count + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (count >= 20) {
                    break;
                }
            }
        }
    }
    
    class Thread2 implements Runnable {
        int count = 0;
        @Override
        public void run() {
            while (true) {
                count++;
                System.out.println("World " + count + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (count >= 25) {
                    break;
                }
            }
        }
    }
    

线程如何理解

  • 主线程创建不同线程,来执行不同的任务
  • 不同线程是否结束互不关联

继承Thread和实现Runnable的区别

  • 从JAVA设计上来看,通过继承Thread或者实现Runnable接口来创建线程本质上没有区别,从jdk帮助文档我们可以看到Thread类本身就实现了Runnable接口start()->start0()
  • 实现Runnable接口方式更加适合多个线程共享一个资源的情况,并且避免了单线程的限制。推荐使用Runnable。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。