1. 多线程

2. 并行和并发

并行:在同一时刻,有多个指令在多个cpu上同时执行

并发:在同一时刻,有多个指令在单个CPU上交替执行

3. 进程和线程

3.1. 进程

进程:是正在运行的软件

独立性:进程是一贯能独立运行的基本单位

动态性:进程的实质是程序的一次执行过程

并发性:任何进程都可以同其他进程一起并发执行

3.2. 线程

线程:是进程中的单个顺序控制流,是一条执行路径

  • 单线程:一个进程只有一条执行路径
  • 多线程:一个进程拥有多个执行路径

4. 继承Thread

  • 类继承Thread类

  • 重写run()方法

    1. public static class xian extends Thread {
    2. @Override
    3. public void run() {
    4. super.run();
    5. }
    6. }
  • 创建类对象

  • 调用用start()方法 启动线程 交由JVM调用此线程的run()方法

    1. public static void main(String[] args) {
    2. xian x = new xian();
    3. x.start();
    4. }

5. Runnable接口

  • 类实现Runnable接口

  • 重写run方法

    1. public static class xian2 implements Runnable{
    2. @Override
    3. public void run() {
    4. System.out.println("线程启动");
    5. }
    6. }
  • 创建类对象

    1. xian2 x =new xian2();
  • 创建Thread类对象,把类对象作为构造方法的参数

    1. Thread t1 =new Thread(x);
  • 启动线程

    1. t1.start();

6. Callable接口与Future

  • 类实现Callable接口

  • 重写call()方法

    1. // 接口的泛型是call方法返回的类型
    2. public static class xian3 implements Callable<String> {
    3. @Override
    4. public String call() throws Exception {
    5. //返回的为线程执行完毕的结果,执行语句在方法体写
    6. return "你好多线程";
    7. }
    8. }
  • 创建类对象

    1. xian3 x = new xian3();
  • 创建Future的实现类FutureTask对象,并将类对象作为构造方法参数传递

    1. FutureTask<String> ft = new FutureTask<>(x);
  • 创建Thread类对象,并把FutureTask对象作为构造方法参数传递

    1. Thread t1 = new Thread(ft);
  • 启动线程

    1. t1.start();
  1. // 获取线程执行完毕的结果,get方法一定在线程启动之后,否则get下面语句不执行
  2. String s = ft.get();
  3. System.out.println(s);

7. Thread

7.1. getName

获取线程名称,线程有默认名称为 Thread-线程数

7.2. setName

也可以通过构造方法设置线程名称

Thead类中带有带参构造方法,可以给线程设置名称,但是继承的类必须使用super关键字引用.

7.3. currentThread

返回当前正在执行的线程对象

  1. String name = Thread.currentThread().getName();

如果Runnable接口或Callble接口想要使用Thread的方法可以

先捕抓到当前执行的线程 再使用Thread的方法

8. 线程休眠

sleep()方法 让线程休眠指定毫秒

  1. Thread.sleep(1000);

9. 线程调度

  • 分时调度模型:所有线程轮流使用cou的使用权,平均分配每个线程占用cpu的时间片
  • 抢占式调度模型:优先让优先级高的线程使用cpu,如果优先级相同,那么会随机选择一个,优先级高的线程获取cpu时间片相对多一些

10. 线程的优先级

10.1. getProiority

获取指定线程的优先级

  1. Thread.currentThread().getPriority()

10.2. setPriority

设置指定线程的优先级

默认为5 范围1-10

  1. Thread.currentThread().setPriority(6);

优先级高不代表运行的时间相对减少

11. 后台线程/守护线程

11.1. setDaemon

需要传递一个布尔值,true为设置为守护线程

当普通线程执行完后,那么守护线程没有继续执行下去的必要(自动结束)

12. 线程的安全问题

12.1. 同步代码块

锁多条语句操作共享数据,可以使用同步代码块实现

  1. Object obj =new Object();
  2. // 传递任意对象,注意要是唯一的,否则线程认为是不同的同步锁
  3. synchronized (obj){
  4. // 线程共享的操作数据
  5. }

默认情况是打开的,只要有一个线程进去执行代码了,锁就会关闭,只有等代码块执行完毕才重新打开

同步的好处:

解决了多线程的数据安全问题

弊端:

当线程过多时,因为每个线程都会判断同步上的锁,耗费系统资源,运行效率降低

12.2. 同步方法

在方法返回值前面加上关键字synchronized,该方法的所有代码都加上锁

此同步方法锁对象为this

如果此同步方法 是静态的 则锁对象为 类名.class

13. Lock锁

Lock是接口不能直接实例化,我们通过它的实现类ReentrantLock来实例化

  1. private ReentrantLock lock =new ReentrantLock();

13.1. lock

  1. lock.lock();

加锁

13.2. unlock

  1. lock.unlock();

释放锁

14. 死锁

线程死锁是指由于两个或者多个线程互相持有对象所需要的资源,导致这些线程处于等待状态,无法前往执行

15. 生成者消费者(等待 唤醒机制)

15.1. 等待 wait

同步代码块中锁的对象是什么则 wait方法在该对象调用

15.2. 唤醒 notify

会随机唤醒该进程里的任意线程

15.3. 唤醒所有 notifyall

锁的步骤:

  1. while(true) 死循环
  2. synchronized 同步锁,锁对象要唯一
  3. 判断,共享数据是否结束 结束的操作
  4. 判断,共享数据是否结束 没有结束的操作

16. 阻塞队列实现等待唤醒机制

16.1. ArrayBlockingQueue

底层是数组,有界,创建时通过带参构造方法,定义该阻塞队列的边界

16.1.1. put

put(“元素”),存储,底层有lock锁

16.1.2. take

take()取出,如果取不出则会一直等待,直到下一关元素put进阻塞队列

16.2. LinkedBlockingQueue

底层是链表,无界.但不是真正的无界,默认最大为int的最大值,也可以通过带参构造方法,定义阻塞队列的边界