线程与进程

    进程
    是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间
    线程
    是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行. 一个进程最少有一个线程。

    线程实际上是在进程基础之上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分成若干个线程

    线程调度

    分时调度
    所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。

    抢占式调度
    优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为 抢占式调度。
    CPU使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核新而言,某个时刻,只能执行一个线程,而 CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是 在同一时刻运行。 其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的 使用率更高。

    同步与异步&&并发与并行

    同步: 排队执行 , 效率低但是安全.
    异步: 同时执行 , 效率高但是数据不安全.

    并发: 指两个或多个事件在同一个时间段内发生。
    并行: 指两个或多个事件在同一时刻发生(同时发生)。

    线程的三种实现方式

    1.线程的实现方式一

    继承: Thread;类:

    重写Run方法: 线程对象要想开启一条线程, start 方法。
    start 方法会自动调用run方法。

    为什么重写run方法?
    因为Thread类当中提供的run方法,没有实现任何的操作。 返回了

    start 方法和 run 方法之间的区别:
    start方法: 会开启一条新的线程。
    run方法: 直接调用run方法, 是属于对象的特有的某个方法。

    2. 多线程的实现方式二:
    实现Runnable接口, 重写run方法。

    实现: Runable接口,此时该类不具备线程对象的特点,
    但是run方法, run方法当中定义的内容,称之为线程任务。

    要想使用对象具备线程特点,需要将线程任务对象作为参数传递给Thread 类。

    步骤:
    (1)定义类实现Runnable接口。 避免了单继承的局限。
    (2)重写run方法。 run定义了线程任务。 同时被执行的代码。
    (3)创建一个Thread类,然后将Runnable接口的实现类对象作为参数传递。
    (4)调用start方法, 开启新的线程。


    3.多线程的实现方式三:
    实现Callable接口,实现call方法
    image.png
    image.png
    线程安全问题

    线程同步的一个前提:

    多个线程在同步的时候,必须使用同一把锁。

    当出现了数据安全问题后,加锁。
    加锁完成后,数据安全的问题并没有得到解决。
    原因: 多个线程之间并没有使用同一把锁。
    解决: 多个线程之间要想解决数据安全问题,必须使用同一把锁。

    解决方法有三个:1.使用同步代码块synchronized (o) {}
    2.使用同步方法
    image.png
    3,使用显示锁Lock

    image.png
    l.lock();//上锁
    l.unlock();//释放锁
    image.png

    线程间通信

    Java实现多线程通信是通过系统方法实现的,只要是wait()、notify()和notifyAll()方法

    wait()方法

    wait()方法使得当前线程必须要等待,等到另外一个线程调用notify()或者notifyAll()方法。

    线程调用wait()方法,释放它对锁的拥有权,然后等待另外的线程来通知它(通知的方式是notify()或者notifyAll()方法),这样它才能重新获得锁的拥有权和恢复执行。

    注意:

    要确保调用wait()方法的时候拥有锁,即wait()方法的调用必须放在synchronized方法或synchronized块中;

    另一个会导致线程暂停的方法:Thread.sleep(),它会导致线程睡眠指定的毫秒数,但线程在睡眠的过程中不会释放掉对象的锁。

    notify()方法
    notify()方法通知等待监视器的线程,该对象的状态已经改变。会唤醒一个等待的线程。
    注意:
    如果多个线程在等待,它们中的一个将会选择被唤醒。这种选择是随意的,和具体实现有关;
    被唤醒的线程是不能被执行的,需要等到当前线程放弃这个对象的锁;
    被唤醒的线程将和其他线程以通常的方式进行竞争,来获得对象的锁。也就是说,被唤醒的线程并没有什么优先权,也没有什么劣势,对象的下一个线程还是需要通过一般性的竞争,也就是看优先级;
    notify()方法应该是被拥有对象的锁的线程所调用。也就是和wait()方法一样,必须放在synchronized方法或synchronized块中。

    notifyAll()方法
    notifyAll()方法会唤醒从同一个监视器中用wait()方法退出的所有线程,使它们按照优先级顺序重新排队

    线程池

    如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低 系统的效率,因为频繁创建线程和销毁线程需要时间. 线程池就是一个容纳多个线程的容器,池中的线程可以反复使用,省去了频繁创建线程对象的操作,节省了大量的时间和资源。

    线程池的好处 降低资源消耗。
    提高响应速度。
    提高线程的可管理性。

    四种线程池:
    1.缓存线程池
    缓存线程池.
    (长度无限制)
    执行流程: 1. 判断线程池是否存在空闲线程
    2. 存在则使用
    3. 不存在,则创建线程 并放入线程池, 然后使用
    image.png
    2.定长线程池 长度是指定的数值
    1. 判断线程池是否存在空闲线程

    2. 存在则使用
    3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用

    4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
    image.png
    3.单线程线程池同上

    4.周期性任务定长线程池

    周期任务 定长线程池.
    执行流程:
    1. 判断线程池是否存在空闲线程
    2. 存在则使用

    3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用 *
    4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程

    image.png