多线程简介

1.Process与Thread

  • 程序本身是指定和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。
  • 进程则是执行程序中的一次执行过程,是一个动态的概念。是系统能够资源分配的单位。
  • 通常在一个进程里,可以包含若干个线程,当然一个进程至少有一个线程,不然没有存在的意义。
  • 线程是CPU调度和执行的单位,线程是独立的执行路径

注:很多的多线程是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务器。如果模拟出来的多线程,即在一个cpu的情况下,在同一个时间点,cpu只能执行一个代码,因为切换的很快,所有就有同时执行的错觉

  • 在程序运行时,及时没有为自己创建线程,后台也会有多个线程,如主线程,gc线程等。
  • main()称为主线程,为系统的入口,用于执行整个程序
  • 在一个线程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为的干预的。
  • 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制。
  • 线程会带来额外的开销,如cpu调度时间,并发控制开销
  • 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致
  • 对现在的程序来说至少需要两个线程,一个是执行main方法的主线程,一个垃圾回收(gc)线程.

2.线程的创建

​ 我们现在一共有三种方式创建线程,分别是继承Threa类,

2.1通过继承Thread类创建

//创建方式一:继承Thread类,重写run()方法,调用start开启线程
package com.thread;

public class ThreadTest01 {
    public static void main(String[] args) {
        MyThread01 myThread01 = new MyThread01();
        myThread01.start();
        //start()方法的作用是:启动一个分支线程,在JVM中开辟一个新的栈空间,这段代码在任务完成之后,瞬间就结束了
        //这段代码的任务只是为了开启一个新的栈空间,只要新的栈空间开出来,start ()方法就结束了,线程就启动成功了
        //启动成功的线程会自动调用run方法,并且run方法在分支线的栈底部(压线)
        //run()方法在分支栈的栈底部,man方法在主栈的栈底部,run和main是平级的
        //muThread01.run;不会启动线程,不会分配新的分支线(这就是单线程)
        for (int i = 0; i < 100; i++) {
            System.out.println("主线程"+i);
        }

    }
    static class MyThread01 extends Thread{
        @Override
        public void run() {
            //编写程序,这段程序运行在分支线程中
            for (int i = 0; i < 100; i++) {
                System.out.println("分支线程"+i);

            }
        }
    }

输出:因为输出较多,我们截取一部分,可以看出,是交替执行的。

主线程8
分支线程68
分支线程69
主线程9
分支线程70
分支线程71
分支线程72
分支线程73
分支线程74
分支线程75
分支线程76
主线程10
分支线程77
主线程11
分支线程78
主线程12
分支线程79
主线程13
分支线程80
主线程14
分支线程81
主线程15
主线程16
分支线程82
主线程17

线程开启不一定立即执行,是由cpu安排调度的

2.2编写一个类实现java.lang.Runnable接口

代码示例:

public class ThreadTest03 {
    public static void main(String[] args) {
        //创建线程对象
        Thread t = new Thread(new MyRunnable());
        //启动线程
        t.start();
        for (int i = 0; i < 100; i++) {
            System.out.println("主线程"+i);
        }
    }
    //这并不是一个线程类,是一个可运行的类,还不是一个线程
    static class MyRunnable implements Runnable{
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println("分支线程"+i);
            }
        }
    }
}

部分输出结果:

分支线程31
分支线程32
主线程45
主线程46
主线程47
主线程48
主线程49
主线程50
主线程51
主线程52
主线程53
主线程54
主线程55
主线程56
主线程57
主线程58
主线程59
主线程60
主线程61
分支线程33
分支线程34
分支线程35
分支线程36
分支线程37
分支线程38
分支线程39

注:第二种方式实现接口比较常见,因为一个类实现了皆可以,还可以去继承其它的类,更加灵活!

3.多线程并发

  • 进程与进程之间的内存独立不共享

  • 在一个进程中,线程和线程的堆内存和方法区内存共享。但是栈内存独立,一个线程一个栈

假设启动十个线程,就会有十个栈空间,每个栈与每个栈之间,互不干扰,各自执行,这就是多线程并发