1.基础概念:程序、进程、线程

  • 程序(Program):是为完成特定任务、用某种语言编程的一组指令的集合。即指一段静态的代码,静态的对象
  • 进程(Process):是程序的一次执行过程,或者正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。—生命周期
    • 程序是静态的,进程是动态的
    • 进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域
  • 多线程(thread):进程可进一步细化为线程,是一个程序内部的一条执行路径。
    • 一个进程同一时间并行执行多个线程,就是支持多线程的
    • 线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(PC),线程切换的开销小
    • 一个进程中的多个线程共享相同的内存单元/内存地址空间->它们从同一堆中分配对象,可以访问相同的变量和对象。这就使得线程间通信更简便、高效。但多个线程操作共享的系统资源可能就会带来安全隐患

举个栗子:

  • 一栋楼是一台超级计算机(多台电脑)
  • 一层楼就是一个服务器(一台电脑,多个房间(多个进程))
  • 一个房间(进程)里可以同时合租几个人(同时运行多个线程)
  • 当有个人占用厕所时(线程资源占用),其他线程就无法所用厕所(线程堵塞),就会带来安全隐患

  • 单核CPU和多核CPU的理解
    • 单核CPU其实是一种假的多线程,因为在一个时间单元内,也只能执行一个线程任务。
    • 多核CPU才能更好的发挥出多线程的效率
    • 一个java应用程序java.exe,其实至少有三个线程:
      • main()主线程、gc()垃圾回收线程、异常处理线程。如果发生异常会影响主线程。
  • 并行和并发
    • 并行:多个CPU同时执行多个任务。比如:多个人同时做不同的事。
    • 并发:一个CPU(采用时间片)同时执行多个任务。比如:秒杀、多个人做同一件事

多线程的优点

  1. 提高应用程序的响应。对图形化界面更有意义,可增强用户体验。
  2. 提高计算机系统CPU的利用率
  3. 改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利用理解和修改

    2.线程的创建和使用

    1. class MyThread extends Thread{
    2. public void run(){
    3. System.out.println("线程指令集");
    4. }
    5. }
    6. public class ThreadTest{
    7. public static void main(String[] args){
    8. MyThread t1 = new MyThread();
    9. t1.start();
    10. // 1. 调用start(),启动新线程run()
    11. // 2. run()只会运行,不会启动新线程
    12. // t1.run();
    13. // 3.一个对象一个线程无法再使用start(),启动新的线程
    14. MyThread t1 = new MyThread();
    15. t2.start();
    16. }
    17. }
    运行匿名线程
    1. new Thread(){
    2. @Override
    3. public void run(){
    4. System.out.println("线程指令集");
    5. }
    6. }
    常用方法
  • start()启动当前线程
  • run()需要重写Thread类中的此方法,将创建的线程写入此方法中
  • currentThread()静态方法,返回执行当前代码的线程
  • Thread.currentThread().getName()显示线程名
  • setName设置线程名
  • yield()释放当前CPU的执行权
  • join() 继续执行阻塞线程,直到join()线程加载完位置
  • sleep(loog millitime)指定睡眠millitime毫秒
  • stop()强制线程生命期结束(不推荐使用)
  • isAlive()返回Boolean,判断线程是否还活着
  • setDaemon(true)线程设置成守护线程,主线程结束跟着关闭。在start前面

线程优先级

  • 优先级常量
    • MAX_PRIORITY 最高优先级
    • MIN_PRIORITY 最低优先级
    • NORM_PRIORITY 默认优先级
  • 优先级操作

    • getPriority() 获取优先级
    • setPriority() 设置优先级

      第二种创建线程的方法和使用

      1. class MThread implements Runnable{
      2. @Override
      3. public void run(){
      4. System.out.println("线程指令集");
      5. }
      6. }
      7. public class ThreadTest{
      8. public static void main(String[] args){
      9. MThread mThread = new MThread();
      10. Thread t1 = new Thread(mThread);
      11. t1.start();
      12. Thread t2 = new Thread(mThread);
      13. t2.start();
      14. }
      15. }

      3.线程的生命周期

      image.png
      image.png

      4.线程同步和线程安全

      1. 同步代码块

      1. synchronized(同步监视器){
      2. // 需要监视的代码
      3. }
  • 操作共享的代码,即为需要被同步的代码

  • 共享数据:多线程共同操作的变量
  • 同步监视器(锁)。任何一个类的对象,都可以充当锁
    • 要求:多个线程必须要共用同一把锁
  • 在实现runnable接口创建多线程的方法中,可以用this当同步监视器(this要唯一性)

image.png

  1. 当有多个线程通过
  2. 只允许一个线程通过,然后一个对象(obj)将其堵塞
  3. 只有一个线程调用tick时
  4. 安全输出数据,不会对数据照成错乱
  5. 退出后将对象(obj)停止堵塞

    2. 同步方法

    1. class MThread implements Runnable{
    2. @Override
    3. public void run(){
    4. System.out.println("线程指令集");
    5. }
    6. public synchronized void show(){
    7. // 需要
    8. }
    9. }

    3. Lock锁

    1. import java.util.concurrent.locks.ReentrantLock;
    2. class win_port4 implements Runnable{
    3. private int ticket = 100;
    4. // 是否公平?意思是均匀分配资源
    5. private ReentrantLock lock = new ReentrantLock(true);
    6. @Override
    7. public void run(){
    8. while(true){ // 无法退出,暂不解决
    9. try{
    10. lock.lock(); //调用lock方法
    11. if(ticket>0){
    12. System.out.println(Thread.currentThread().getName() + ":卖了一张票,剩余" + ticket + "张票");
    13. ticket--;
    14. }else break;
    15. }finally{
    16. lock.unlock();
    17. }
    18. }
    19. }
    20. }

    Lock和synchroonized的区别

  • 相同点:都能解决安全问题
  • 不同点:
    • synchronized机制在执行完相当的同步代码以后,自动的释放同步监视器
    • Lock需要手动的启动同步(lock()),同时结束同步也需要手动的实现(unlock())
  • 建议使用优先级

    • lock
    • 同步代码块(已经进入了方法体,分配了相应资源)
    • 同步方法(在方法体之外)

      懒汉式线程安全

      1. // 线程安全的单列式之懒汉式
      2. class Bank{
      3. private Bank(){};
      4. private static Bank instance = null;
      5. // 方法一:效率稍差
      6. // public static Bank getInstance(){
      7. // synchronized (Bank.class){
      8. // if(instance == null)
      9. // instance = new Bank();
      10. // return instance;
      11. // }
      12. // }
      13. // 方法二:效率高
      14. public static Bank getInstance(){
      15. if(instance == null){
      16. synchronized (Bank.class){
      17. if(instance == null)
      18. instance = new Bank();
      19. }
      20. }
      21. return instance;
      22. }
      23. }

      死锁

      定义
  • 不同的线程分别占用对方需要的同步资源不放弃,都等待对方放弃自己需要的同步资源,就形成了死锁

  • 出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续

解决方法

  • 专门的算法、原则
  • 尽量减少同步资源的定义
  • 尽量避免嵌套同步

image.png

5.线程的通信

  • wait()当前线程进入阻塞状态,并释放同步监视器
  • notify()唤醒一个被wait的一个线程,如果有多个就唤醒优先级高的
  • notifyall()会唤醒所有被wait的线程

    sleep()wait()的异同

    相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态
    不同点:

  • 两个方法的声明位置不同:

    • Thread类中声明sleep()
    • Object类中声明wait()
  • 调用的要求不同:
    • sleep()可以在任何需要的场景下调用
    • wait()必须在同步代码块中
  • 关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中

    • sleep()不会释放锁
    • wait()会释放锁

      6. JDK5.0新增线程创建方式

      新增方式一:实现Callable接口

  • Callable相比run()方法,可以有返回值

  • 方法可以抛出异常
  • 支持泛型的返回值
  • 需要借助FutureTask类,比如获取返回结果

Future接口

  • 可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等。
  • FutrueTask是Futrue接口的唯一的实现类
  • FutureTask 同时实现了Runnable, Future接口。它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值

新增方式二:使用线程池

好处:

  • 提高响应速度(减少了创建新线程的时间)
  • 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
  • 便于线程管理

    • corePoolSize:核心池的大小
    • maximumPoolSize:最大线程数
    • keepAliveTime:线程没有任务时最多保持多长时间后会终止
    • ….

      线程池相关API

      线程池相关API
  • JDK 5.0起提供了线程池相关API:ExecutorService 和 Executors

  • ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
    • void execute(Runnable command) :执行任务/命令,没有返回值,一般用来执行Runnable
    • Future submit(Callable task):执行任务,有返回值,一般又来执行Callable
    • void shutdown() :关闭连接池
  • Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
    • Executors.newCachedThreadPool():创建一个可根据需要创建新线程的线程池
    • Executors.newFixedThreadPool(n); 创建一个可重用固定线程数的线程池
    • Executors.newSingleThreadExecutor() :创建一个只有一个线程的线程池
    • Executors.newScheduledThreadPool(n):创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行

深入理解线程操作

wait:等待,释放资源等待notify唤醒 notify:唤醒,随机唤醒一个被wait唤醒的线程