*阿炮的Java笔记012号*-面试令人困扰的多线程!!

157 阅读5分钟

老生常谈…对于大佬们根本都不用看的博客!

对于像我这种菜鸡还是得一步一步的来!如果有错误请大佬们指点指点!

并行和并发

  • 并行:两个或两个以上的事件在同一时刻同时发生。
  • 并发:两个或两个以上的事件在同一时间段内发生。

在操作系统中,并发是在一段时间内宏观上有多个程序同时运行,在是单CPU的系统上,每一时刻只能有一个程序在运行,但是这些程序是分时交替运行的,给人一种同时运行的错觉。交替的时间非常非常短。而在多CPU系统中,这些程序可以分配到多个CPU上进行运行,实现了多任务并行运行。 所以多核CPU,核越多,并行处理的程序越多,能够大大的提高电脑的运行效率。

单核CPU:只能并发

多核CPU:并发+并行

通俗理解:

并行:很多工作分配给很多工人一起执行,然后在进行汇总;

并发:一个工人要干很多工作,轮到哪个工作就执行哪个工作;工人相当于资源,这些工作抢这个工人。

进程和线程

  • 进程:一个在内存中运行的应用程序,每个进程都有一个独立的内存空间;进程也是程序的一次执行过程,是系统运行程序的基本单位,系统运行一个程序是一个进程从创建到运行再到死亡的过程。
  • 线程:线程是进程中的一个执行单元,负责当前进程中的程序执行,一个进程中可以有多个线程,但至少有一个线程。

注意(重点要考):进程是操作系统调度和分配资源的最小单位,线程是CPU调度的最小单位。不同的进程之间不共享内存,进程之间的可以进行通信但成本很高。而不同的线程也有自己独立的内存空间,对于方法区、堆中的同一个对象的内存,线程之间可以共享,但栈的局部表里永远是独立的。

了解线程调度

  • 分时:所有的线程轮流调用CPU,平均分配每个线程占用CPU的时间。
  • 抢占:优先级高的先使用CPU,如果相同则随机选择。

线程的生命周期

线程生命周期.png

只要知道5中状态就可以:新建,就绪,阻塞,运行,死亡。

线程的创建与使用(走进多线程)

JAVA通过调用Thread类来进行多线程的编写,每个线程都要重写run()方法来进行操作,run()方法也被称为线程体。然后我们通过调用start()方法来启动线程,切记并非直接调用run().

创建方式

  • 继承Thread类
  • 实现Runnable接口

继承Thread类

public class TestThread extends Thread {
    //重写线程体
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println(Thread.currentThread().getName() + ":run--" + i);
        }
    }
}
public class Test {
    public static void main(String[] args) {
        //创建线程对象
        TestThread thread = new TestThread();
        //调用start启动线程
        thread.start();
        //main的方法用来体现交叉运行的结果
        for (int i = 0; i < 50; i++) {
            System.out.println(Thread.currentThread().getName() + ":run--" + i);
        }
    }
}

我们需要通过用main方法进入运行程序,main方法就相当于程序的入口。

来展示一下运行的结果:

调用start.png

实现 Runnable 接口

public class TestRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println(Thread.currentThread().getName() + ":run--" + i);
        }
    }
}
public class Test {
    public static void main(String[] args) {
        //创建runnable的实现类的对象
        TestRunnable runnable = new TestRunnable();
        //然后放入Thread类中,需要几个线程就创建几个
        Thread thread1 = new Thread(runnable);
        Thread thread2 = new Thread(runnable);
        thread1.start();
        thread2.start();
    }
}

运行结果如下:

runnable.png

两种方法的区与好处

区别:

  • 继承 Thread 类:需要运行代码存放在 Thread 子类的 run() 方法中。
  • 实现 Runnable 接口: 需要运行代码存放在接口的子类的 run() 方法中。 实现 Runnable 接口的好处:
  • 避免了单继承的局限性
  • 多个线程可以共享同一个接口实现类的对象

线程的优先级

为什么会有优先级?就相当于人为什么分为三六九等一样!优先级越高的线程就会获得更多的执行机会

我们可以通过调用Tread类的setPriority(int newPriority)方法来设置线程的优先级,还可以通过getPriority()方法来获取该线程的优先级。

优先级我们需要输入范围在 [1,10] 之间的整数,通常会有三个推荐的优先级常量:

public final static int MIN_PRIORITY = 1;
public final static int NORM_PRIORITY = 5;
public final static int MAX_PRIORITY = 10;

注意

  • 线程创建时继承的是父线程的优先级;
  • 低优先级只是获得调度的概率低,并非一定是在高优先级线程之后才被调用;

插队(join)和礼让(yied)

现实生活中,我们在排队的时候,就想插队。所以我们可以通过join 方法,人为的让等待的线程提前运行,关系户就是厉害昂!

礼让就是这个正在运行的程序,被咱看着不顺眼让他待会执行,先让优先级高或相同的线程来执行,我们就可以通过yied方法来执行,如果队列里没有和它能力一样或者比它强的,这个方法没啥用!