Java 多线程

2021-07-09  本文已影响0人  bowen_wu

概述

Thread 线程 => 是操作系统能够进行运算调度的最小单位。大部分的情况下,它被包含在进程中,是进程中的实际运作单位

线程状态

多线程

线程难的原因:需要看着同一份代码,想象不同的人在疯狂的以乱序执行它 => 多个线程同时访问同一个共享变量时,由于变量不是原子的,以致于过程是乱序的,不知道什么时候会发生不正常的,有可能是正常的,有可能是不正常的

// 最终结果不是 1000
private static int j = 0;
public static void main(String[] args) {
    for (int i = 0; i < 1000; i++) {
        new Thread(() -> {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(++j);

        }).start();
    }
}
result

多线程适用场景

线程不安全

线程不安全的表现

数据错误 => 不是原子操作

result

死锁

synchronized => 同步 => 锁 => 同一个时刻只能有一个线程拿到同一把锁 => 在 Java 中任何一个对象都可以当成锁🔐

// Thread1 和 Thread2 使用了不同的顺序来获得资源锁
private static final Object lock1 = new Object();
private static final Object lock2 = new Object();

public static void main(String[] args) {
    new Thread1().start();
    new Thread2().start();
}

static class Thread1 extends Thread {
    @Override
    public void run() {
        synchronized (lock1) {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            synchronized (lock2) {
                System.out.println("");
            }
        }
    }
}

static class Thread2 extends Thread {
    @Override
    public void run() {
        synchronized (lock2) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            synchronized (lock1) {
                System.out.println("");
            }
        }
    }
}
死锁宏观表现

排查死锁问题

  1. jps => 查看当前所有的 Java 进程
  2. 找到死锁所运行的进程 ID
  3. jstack <java process id> > <file> => 将死锁的进程栈信息保存到指定文件
  4. 分析文件 | 排查调用栈,看每个 Thread 在哪个方法处停滞了

预防死锁产生的原则

所有的线程都按照相同的顺序获得资源的锁🔐

线程安全

实现线程安全的基本手段

synchronized 🔐

// 在语句外加 synchronized
private static final Object lock = new Object();

synchronized(lock) {
    i += 1;
}

// 在方法上声明 synchronized 关键字
public static synchronized void modifySharedVariable () {}

问:同步块同步了什么东西?

JUC 包 java.util.concurrent

java.lang.Object

为什么 Java 中所有对象都可以成为锁?

合理的使用 wait + notify 就可以达到调度不同线程的目的 => wait() 之后 notify 进程才会一直走下去,如果先 notifywait 那么进程将会一直等待 notify

synchronized(obj) {
    while(condition does no hold) {
        obj.wait();
    }
}

synchronized(obj) {
    obj.notify();
}

线程池

线程是非常昂贵的 => Java 线程模型的缺陷 => Java 的线程调度完全依赖于操作系统的依赖调度,所以线程是非常昂贵的,每个线程都要占用操作系统一部分资源

线程池是预先定义好的若干个线程 => 省去每次创建 | 销毁线程的开销 => java.concurrent.Executors + java.util.Callable + java.util.concurrent.Future

创建多线程方法

调度线程方法

知识点

  1. 如何查看一个数据是否是线程安全的?
    进入方法后,搜索 thread => <strong>Note that this implementation is not synchronized.</strong> => 线程不安全
  2. HashMap 死循环
  3. 除非特意提及线程安全,不然都是线程不安全的
  4. Collections.synchronized 方法可以将一些线程不安全的类 | 方法变为 synchronized
  5. monitor => 在 Java 世界中,使用 monitor 代表 synchronized 锁住的对象
  6. Runnable 中的 run
    • 没有返回值
    • 没有声明抛出的异常
  7. // TODO 广度优先算法???实现广度优先遍历一棵树 => 使用队列,可以学习队列的相关知识
  8. ArrayList 从尾部删除更有效率
  9. SpotBugs => 自动化字节码 Bug 检查工具
  10. == 在 Java 中永远不会被自动处理成 .equals()
    Integer i = 1;
    if(i == 1) {} // Integer 自动拆箱,调用 intValue() 方法,导致空指针异常
    
  11. flyway => 数据库迁移工具 => 数据库结构的版本控制 => 可以方便的生成数据库结构
  12. ORM => Object Relation Mapping => 对象关系映射 => ORM 框架 mybatis
  13. DAO => Database Access Object
  14. DateInstant 都可以用来表示时间戳,Instant 是 1.8 出来的,尽可能的使用 Instant
  15. BufferedReaderreadLine 方法,必须套 while(true)
    BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
    String line;
    while ((line = bufferedReader.readLine()) != null) {
        // do something
    }
    
  16. 遇到 interface 实现即可
  17. Optional => A container object which may or may not contain a non-null value. If a value is present, isPresent() will return true and get() will return the value.
上一篇 下一篇

猜你喜欢

热点阅读