首页 > 编程语言 >Java多线程技能

Java多线程技能

时间:2024-10-21 13:22:35浏览次数:7  
标签:执行 Java 对象 同步 线程 多线程 方法 public 技能

 

 2.创建多线程的方式,有几种?怎么创建

  • 继承Thread类 (一般不单独用)
  • 实现Runnable接口 + Thread对象
  • 实现Callable接口+FutureTask<>对象+Thread对象
  • 线程池 + (实现Callable接口+FutureTask<>对象)或者(实现Runnable接口)

3. Thread类的常见API

  • currentThread() 获取当前线程的信息

  • isAlive() 验证当前线程是否存活

  • sleep(long millis) 线程休眠

  • 线程堆栈相关

    • StackTraceElement[] getStackTrace()
      获取一个标识该线程堆栈跟踪元素数组
    • dumpStack()
      将当前线程的堆栈跟踪信息输出至标准错误流
      (该方法只用于调试)
    • Map<> getAllStackTraces()
      返回所有活动线程的堆栈跟踪的映射
  • getId() 获取线程的唯一标识、Id

  • 停止线程的几种方式

    • 使用退出标志使流程正常退出,
      如在线程中用使用break、抛异常等

    • 使用stop()强行终止
      【可能会丢数据,已被弃用】

    • 使用interrupt()方法中断线程
      执行这个方法后,会将线程标记为中断,
      【但还会继续执行,直到执行完毕】

      • interrupted()
        判断线程是否已经中断
        【执行后,会将中断线程状态清除!】
      • this.isInterrupted()
        判断this关键字所在的类的对象是否已中断
  • 线程的暂停与重启

    • suspend() 暂停线程
      缺点:【独占资源】【易造成数据不完整】
    • resume()
      重启线程
  • yield() 释放当前线程所占用的CPU资源

  • 线程优先级

    • getPriority() 获取当前线程的优先级
      【最大10 ,最小1】
    • setPriority() 设置当前线程的优先级
      MIN_PRIORITY = 1;
      NORM_PRIORITY = 5;
      MAX_PRIORITY = 10;
    • 优先级具有【继承性】
      A线程启动B线程,则B优先级等于A
  • setDaemon(true) 设置当前线程为守护线程

    • Java中的线程分为:
      守护线程
      用户线程(非守护线程)

第2章 线程同步(对象及变量的并发访问)

synchronized 同步方法

  • 方法内的变量永远线程安全

  • 实例变量线程不安全,怎么解决

    • 同步方法
      同步代码块
  • synchronized在字节码指令中的原理

    • 同步方法:
      在class文件中标记了ACC_SYNCHRONIZED
    • 同步代码块:
      class文件中用monitorenter和monitorexit分别表示同步代码块的开始和结束
  • 脏读问题

    • 发生脏读:
      因为读取成员变量(实例变量)时,此值已经被其他线程修改了
  • synchronized 锁重入

    • 锁重入:
      可以重复加锁,
      有几个锁加几个锁,一层套一层

    • 支持父子类继承

      • 子类同步方法调用父类同步方法,有一层锁是一层,就当是在自己方法里面一样,不会出现线程安全问题
  • 重写synchronized方法

    • 如果重写方法不加synchronized关键字,会破坏同步方法
  • 抛异常,锁自动释放

  • println() 也是同步方法,线程安全

  • 【同步方法】锁是对象的锁,不同的对象是不同的锁

  • 【缺点】一个对象中多个同步方法,用的同一个锁(都是对象锁),Thread1调用A方法一直没执行完毕,从而阻塞Thread2调用的B方法。

synchronized 同步代码块

  • 同步和异步

    • 区别和联系

      • 同锁 -> 同步
        异锁 -> 异步
    • 各有什么优缺点

      • 同步执行 成员变量线程安全 但效率低
        异步执行 成员变量线程不安全 效率高
    • 什么时候用同步/异步?

      • 操作同一个成员变量的不同方法,要用同一把锁,保证成员变量的线程安全,如果是不同业务,比如执行完毕后发送邮件,就异步执行
  • 同步代码块相比于同步方法的优势

    • 同步方法的锁: 同步方法所在类的对象
      同步代码块的锁: 很灵活, 可以是当前方法所在类的对象,也可以是其他类对象(比如Class类对象,String类对象等)
  • 锁对象

    • this

      • 当前方法所在类的对象
    • 同步方法的锁对象

      • 当前方法所在类的对象
    • 类名.class

      • Class类的对象
    • 静态同步方法的锁对象

      • Class类的对象
    • 其他类对象

      • 如String、Object等类对象
  • 【注1】String做锁对象时

    • 受JVM常量池的影响,如果String的值被改变了,锁就变成了不同锁,会造成线程异步进而可能导致线程不安全问题
  • 【注2】锁对象修改对线程同步的影响

    • String常量修改 =》 会影响线程锁
    • 对象属性修改 =》 不会影响线程锁
  • 死锁问题

    • 两个线程相互等待对方释放自己的锁

synchronized 同步写法总结

public class MyService{
    public synchronized static void method1(){}
    public static void method2(){
         ssynchronized(MyServcie.class){}
    }
    public synchronized void method3(){}
    public static void method4(){
         ssynchronized(this){}
    }
    public static void method5(){
         ssynchronized("abc"){}
    }
  }

(A) method1 和 method2 持有同一把锁,即:MyService.java 对应的 Class类对象(其实就是Class对象)
(B) method3 和 method4 持有同一把锁,即:MyService.java类的对象 (等同于this)
(C) method5 持有的锁是字符串对象

synchronized 关键字

  • 原子性

    • 使用synchronized实现了同步,同步实现了原子性,保证了被同步的代码段在同一时间只被一个线程操作,故实现了原子性
  • 可见性

    • B线程马上就能看到A线程修改的数据
      【原理】用volatile或者synchronized修饰的方法,修改读取变量时,强制从公共堆栈读,不从线程私有堆栈中读取。
      【注】线程修改数据,写都是写到公共堆栈中
  • 禁止代码重排序

    • JAVA程序运行时,JIT(即时编译器)会根据代码执行时间等动态调整执行顺序,而volatile和synchronized关键字会隔断这种调整,隔断成两块后,前面可以内部调整,后面可以内部调整,但是两块之间不能相互调整了

volatile 关键字

  • 原子性

    • 32位系统中,未用volatile声明的long和double数据类型是非原子的,64位则要根据具体实现判断。
      【注】无论32位还是64位,无论用不用volatile声明,i++ 都是一个非原子操作,除非用AtomicInteger声明变量
  • 可见性

    • 和synchronized一样,都是强制从公共堆栈读,不从线程私有堆栈中读取。
  • 禁止代码重排序

    • 和synchronized一样

总结

  • synchronized关键字

    • 作用:保证同一时刻,只有一个线程能执行某个方法或代码块。
      修饰:可以修饰方法、代码块。
      特征:可见性、原子性、禁止代码重排序。
      使用场景:多个线程对同一个对象中的同一个成员变量变量操作时,为了避免出现线程安全问题时使用。
  • volatile关键字

    • 主要作用:让其他线程能看到修改后的最新的值。
      修饰:只能修饰变量。
      特征:可见性、原子性、禁止代码重排序。
      使用场景:欲实现一个变量在被A线程修改后,其他线程立马能获取到最新值时候,就用volatile修饰这个变量。

第3章 线程间通信

wait / notify 机制

  • 原理

    • 持有相同锁(对象级别的)的多个线程,在wait()处暂停执行,释放锁,直到接到通知,notify() 或者 notifyAll()再获取锁,继续执行。
      【注】锁必须是对象级别的,因为wait(), notify(), notifyAll()是Object类中的,不是Thread类中的方法,所以必须要对象。
  • wait()

    • 暂停当前线程,释放锁
      【注】执行wait()方法后,马上暂停线程么,并释放锁
  • notify()

    • 发出通知,wait状态的线程可以准备获取锁,开始执行了,只能通知“一个”线程,唤醒顺序同执行wait()顺序一致
      【注】执行notify()方法后,不是马上暂停当前线程,而是要将当前线程同步代码块执行完毕后,才释放锁,故wait状态的线程也要等它执行完毕才能抢到锁
  • notifyAll()

    • 发出通知,wait状态的线程可以准备获取锁,开始执行了,默认按照执行wait()相反的顺序依次唤醒全部线程
      【注】锁释放时机同notify()
  • 释放锁的时机

    • wait() 立即释放
    • notify() / notifyAll() 同步代码块执行完毕后再释放
  • wait(long time)

    • 如果线程超过time时间没有被唤醒,则自动醒来,但是要执行的前提条件是再次持有锁,没有持有锁的话,一直等待,直到拿到锁才开始执行
      time的单位为毫秒,其他用法一致
  • wait() 与 sleep()的区别

    • wait() 线程阻塞,立即释放锁
      sleep() 线程阻塞,不 释放锁
  • 用while替代if

    • 执行wait()后,线程阻塞,当线程醒来的时候

      • 用if 不会再判断条件,直接运行
      • 用while 会再次判断条件,满足条件才运行
  • 生产者消费者模型的几种实现

    • 不带缓冲区

      • 1生产 1消费 操作值
      • 多生产 多消费 操作值
    • 带缓冲区

      • 1生产 1消费 操作栈
      • 1生产 多消费 操作栈
      • 多生产 1消费 操作栈
      • 多生产 多消费 操作栈
  • 管道流通信

    • 特殊的流,用于在不同的线程间直接传输数据
    • 字节流
    • 字符流
  • 【案例】利用wait/notify实现数据库交叉备份

join() 方法的使用

  • 使用场景

    • 主线程要获取子线程的结果时,要用到join(),和使用Callable接口和FutureTask效果类似
  • join() 原理

    • 主线程中,子线程调用此方法,则会相当于主线程执行wait()方法,直到子线程执行完毕,会通知主线程,主线程拿到锁以后继续执行
  • join() 与 sleep() 的区别

    • join() 是线程间通信,释放锁 (主线程执行wait(),子线程执行完毕后通知主线程)
      sleep() 是线程内部通信,不释放锁
  • join(long time)

    • 不管子线程是否执行完毕,到时间后,且主线程拿到锁后,主线程就继续往下执行
  • join() 和 interrupt() 同时使用出现异常

    • 彼此遇到会出现 InterruptedException,终止的是主线程,子线程还在继续
  • join(long time) 后面的代码可能提前运行,其实就是锁的问题

类ThreadLocal 的使用

  • 原理及作用
  • get() / set()
  • 隔离性验证
  • 重写initialValue() 方法解决get() 方法返回null的问题
  • 不能实现值继承

类InheritableThreadLocal 的使用

  • 原理及其作用

  • 验证

    • 子线程将父线程中的table对象以 复制 的方式赋值给子线程的table
  • 值继承

    • 父线程新值 子线程旧值
    • 父线程旧值 子线程新值
  • 对象继承

    • 父子统一
  • 重写childValue() 方法,对值进行加工

线程的生命周期

第4章 Lock 对象的使用

使用ReentrantLook类

重进入锁

  • 相比于synchronize关键字实现线程间同步,ReentrantLook更加灵活,如具有嗅探锁定、多路分支通知等功能

  • 线程间通信

    • ReentrantLock实现了java.util.concurrent.locks包中的Lock接口,利用ReentrantLock对象中的lock()和unlock() 方法可以实现线程间同步,ReentrantLock具有互斥排他性。
  • 结合Condition对象实现线程间通信

    • 利用Condition对象的await()和signal() 方法实现wait()/notify()机制

    • 线程对象注册在不同的Condition中,可以实现有选择性地进行线程通知(多路分支通知)

    • 【注】必须在condition.await() 之前调用lock.lock()获取锁,因为Condition对象的await()方法执行后会将线程转换为wait状态,并释放锁

    • await() 方法暂停线程运行的原理
      (这一块暂时不理解)

      • 内部执行了Unsafe类中的park() 方法
  • 生产者消费者模型

  • 公平锁与非公平锁

    • 公平锁

      • 先用先得,必须排队,排队尾
    • 非公平锁

      • “有机会插队”,先抢锁,抢不到再排到队尾去
  • 实现

    • ReentrantLock默认是非公平锁
    • 构造公平/非公平锁的构造函数
      public ReentrantLock(boolean fair)
      true => 公平锁
      false => 非公平锁
  • API

    • public int getHoldCount()

      • 查询“当前线程”保持锁定的个数,
        即调用lock()方法的次数
    • public final int getQueueLength()

      • 获取正等待获取此锁的估计数,
        【注】已经获取锁的不算
    • public int getWaitQueueLength(Condition condition)

      • 获取与此锁相同的condition且正等待中的线程估计数
    • public final boolean hasQueuedThread(Thread thread)

      • 判断参数中的线程是否在等待获取锁的队列中
    • public final boolean hasQueuedThreads()

      • 判断是否在等待获取当前锁的队列中
    • public final boolean hasWaiters(Condition condition)

      • 查询是否有线程执行了参数中的condition.await() 方法而呈等待状态
    • public final boolean isFair()

      • 判断当前锁是不是公平锁
    • public boolean isHeldByCurrentThread()

      • 判断当前线程是否持有当前锁
    • public final boolean isLocked()

      • 判断当前锁是不是已经被获取且没有释放
    • public void lockInterruptibly()

      • 当某个线程尝试获得锁并且阻塞在lockInterruptibly() 方法时,该线程可以被中断
    • public boolean tryLock()

      • 嗅探拿锁,判断当前锁能不能拿到(没有被持有),能就拿到返回true
    • public boolean tryLock(long timeout, TimeUnit unit)

      • 嗅探拿锁,判断当前锁能不能在有限时间内(timeout)拿到(没有被持有),能就拿到返回true
    • public boolean await(long timeout, TimeUnit unit)

      • 线程等待,一段时间后自动唤醒线程,单位毫秒
    • public long awaitNanos(long nanosTimeout)

      • 线程等待,一段时间后自动唤醒线程,单位纳秒
    • public boolean awaitUntil(Date deadline)

      • 线程等待,在deadline时自动唤醒,单位毫秒
    • public void awaitUninterruptiably()

      • 线程等待过程中,不允许被中断

使用ReentrantReadWriteLook类

读写锁

  • 原理与使用

    • 读操作相关的锁——共享锁

    • 写相关的锁——排他锁

    • 读写分离,提高系统效率
      因为读几乎不需要同步,大家都可以读,但是写必须保证数据同步,所以可以只加“写锁”,不加“读锁”

    • Demo

      • ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
        ……
        lock.readLock().lock();
        lock.writeLock().lock();
        ……
        lock.readLock().unlock();
        lock.writeLock().unlock();
  • 读读共享

  • 写写互斥

  • 读写互斥

  • 写读互斥

第5章 定时器 Timer

原理及使用

    1. MyTask继承TimerTask类,重新run()方法
  1. 新建Timer对象,调用其不同的schedule方法实现定时任务
  • 创建Timer对象的时候,会启动一个新的非守护线程TimerThread,且线程中存在一个死循环导致线程一直运行,可以调用timer.cancel()方法终止计时器

  • timer.cancel()方法
    在多任务的Timer对象中调用时,会优先清空任务队列(已经在执行的任务不受影响),然后再销毁进程

  • 多任务Timer对象中执行task任务的算法
    每次最后一个执行的放入队列头,
    如:第一次 ABC,
    第二次 CAB,
    第三次 BCA

  • 执行情况

    • 正常执行
      单个TimerTask任务
      多个TimerTask任务,但时间没有交集,
    • 立即执行
      计划执行的时间早于当前时间
    • 延时执行
      因为前面的任务可能消耗的时间比较长,后面的任务就会被延后

API

  • schedule(TimerTask task, Date time)

    • 指定时间执行一次某任务
  • schedule(TimerTask task, Date firstTime, long period)

    • 指定时间之后,按照间隔时间无限循环执行某一个任务
  • schedule(TimerTask task, long delay)

    • 以当前时间点为基准,delay毫秒后执行一次任务
  • schedule(TimerTask task, long delay, long period)

    • 以当前时间点为基准,delay毫秒后
      按照间隔时间无限循环执行任务
  • scheduleAtFixedRate(TimerTask task, long firstTime, long period)

    • 与schedule()方法一样,只是加了【追赶性】
      【大白话】这个方法就是要把无论因为某某原因导致延迟、没执行的任务,全部补上,补上后就和schedule()一样了

第6章 单例模式与多线程

单例模式特征

  • 单例模式特点:
  1. 一个类只有一个实例
  2. 自行实例化,并且向整个系统提供
  3. 反序列化时不会重新实例化对象
  • 实现关键点:
  1. 私有化构造函数
  2. 自行实例化单个对象
  3. 使用静态方法提供自行实例化的单个对象

单例模式的使用场景

  • 最好只能有一个对象存在的场景时使用单例模式。
    如:1. 日志系统,只需要一个日志系统记录全局的
    2. id生成器,保证id唯一性,单例更合适
    3. 计时器、计数器,都是保证数据准确
    4. 多数多线程的线程池,方便线程管理
    5. 数据库连接,资源重用,减少频繁开关的资源消耗

几种不同单例模式的实现

  • 饿汉模式 / 立即加载

    • 类加载的时候实例化
      优点:线程安全
      缺点:
  1. 资源可能浪费
  2. 不能出现其他实例变量(不能传参),否则可能线程不安全
  • 懒汉模式 / 延迟加载

    • DCL 双检查锁

      • 第一次调用时实例化
        优点:延迟加载,可能节省资源
        缺点:必须实现同步,否则线程不安全
      • DCL——Double-Check Locking,双检查锁
        保证线程安全,提高多线程下效率
      • 用volatile修饰实例变量的必要性
  1. 实现实例变量线程间可见

  2. 禁止实例化时代码重排序

    • 静态内部类

      • 第一次调用时实例化
        优点:
  3. 线程安全

  4. 延迟加载
    缺点:
    实例化时不能传参

    • 实现原理:
      利用类加载的特点(即:外部类加载时,并不需要立即加载内部类,内部类不被加载则不实例化)实现单例实例化和延迟加载,又因为静态内部类线程安全的特性,保证了整体的线程安全。
      【内部类线程安全的原理不理解,摘抄自网上】

    • 内部类线程安全的原理:
      虚拟机会保证一个类的()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的()方法,其他线程都需要阻塞等待,直到活动线程执行()方法完毕。如果在一个类的()方法中有耗时很长的操作,就可能造成多个进程阻塞(需要注意的是,其他线程虽然会被阻塞,但如果执行()方法后,其他线程唤醒之后不会再次进入()方法。同一个加载器下,一个类型只会初始化一次。)
      【引用】https://blog.csdn.net/mnb65482/article/details/80458571

    • static代码块

      • 原理
        利用静态代码块的特性(使用类时加载静态代码块)实现
        优点:
  5. 延迟加载

  6. 线程安全
    缺点:不能传参

    • 【我感觉】这东西就是饿汉模式实现了延迟加载,也是一种懒汉模式的实现吧

    • enum枚举

      • 原理
        利用枚举类的特性(使用枚举类时,自动调用其构造方法)实现
        优点:延迟加载、线程安全
        缺点:不能传参
      • 和静态代码块实现差不多呀。。。。

序列化与反序列化

  • 使用默认反序列行为
    单例模式下的对象也会变成多例
  • 保证序列化和反序列化后单例的条件:
  1. 单例模式的类必须实现Serializable接口
  2. 且必须在同一个类中实现序列化和反序列化操作
  3. 且反序列化必须调用readResolve()方法

第7章 拾遗补漏

线程的状态

  • 状态信息存储在Thread.State枚举类中

  • 线程的五种状态

    • NEW

      • 至今尚未启动的线程状态
    • RUNABLE

      • 正在Java虚拟机中执行的线程状态
    • BLOCKED

      • 受阻塞,且等待某个监视器锁的线程状态
    • WAITING

      • 无限等待另一个线程执行某一操作的线程状态
    • TIMED_WAITING

      • 有时间限制地等待另一个线程执行某一操作的线程状态
    • TERMINATED

      • 已退出的线程状态
  • 线程的生命周期

  • 根据书上的图简化了一点

线程组

  • 线程组实现和特性

    • ThreadGroup类
    • 一级关联(常用)
      多级关联(不常用)
    • 线程自动归组属性
  • ThreadGroup类中的API

    • activeCount()

      • 获取当前线程组中子线程组的数量
    • enumerate(ThreadGroup array[])

      • 将当前线程组中的子线程 复制 到数组中
      • enumerate(ThreadGroup array[], boolean recurve)
        true 递归复制,false 非递归
    • ThreadGroup.getParent()

      • 获取父线程组
      • 【根线程组】system,再往上就抛空指针了
  • Thread类中的API

    • activeCount()

      • 返回 当前线程 的线程组 中活动线程的数目
    • enumerate(Thread tarray[])

      • 调用 当前线程 的线程组 的enumerate()方法
  • 再次实现线程执行有序性

    • “没看懂这个。。。。”

SimpleDateFormat类 非线程安全

  • 非线程安全原因:单例
  • 解决办法:
  1. 多例,每次使用都new一个
  2. 使用ThreadLocal保证其线程安全

线程中异常处理

  • 异常处理

    • 线程中处理
    • 线程组内处理
  • 异常处理优先级

    • 调用过setUncaughtExceptionHandler()方法的优先处理,其他的不处理了

标签:执行,Java,对象,同步,线程,多线程,方法,public,技能
From: https://www.cnblogs.com/stevenduxiang/p/18489274

相关文章

  • Java 中的数据加密和安全传输_1
    在Java中实现数据加密和安全传输涉及几个关键技术:使用加密算法、实施安全的密钥管理、采用安全传输协议。加密算法如AES和RSA用于保护数据的机密性,防止未授权访问。安全的密钥管理确保只有授权用户才能解密数据。而安全传输协议如SSL/TLS保护数据在网络中的传输。其中,安全传输协......
  • 从零开始写多线程
    1.Java多线程的基本概念1.1线程与进程进程:是操作系统分配资源的基本单位,每个进程都有独立的内存空间。线程:是进程内的一个执行单元,同一进程中的线程共享进程的内存空间,线程间的通信更为高效。1.2线程的好处提高系统响应性:可以实现用户界面与后台处理的并发执行,使得程序......
  • Java基础·环境搭建与语法
    Java初识Java简介Java是一门面向对象的程序设计语言1995年由sun公司发布2010年sun公司被Oracle公司收购JDK版本是8.0JVMJVM(JavaVirtualMachine),Java虚拟机JVM是Java平台无关性实现的关键Java程序执行流程JDKJDK(JavaDevelopmentKit),Java语言的软件开发工具包两......
  • Java常量与变量
    标识符号的命名规则标识符可以由字母、数字、下划线()和美元符($)组成,不能以数字开头标识符严格区分大小写标识符不能是Java关键字和保留字标识符的命名最好能反映出其作用关键字变量什么是变量变量的三个元素:变量类型、变量名和变量值变量名的命名规则满足标识符命名......
  • java+vue计算机毕设废品上门回收预约系统【开题+程序+论文+源码】
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着社会经济的快速发展和人民生活水平的不断提高,各类废品的产生量也在急剧增加。传统的废品回收方式往往依赖于街头巷尾的废品收购站或回收人员,这种......
  • java+vue计算机毕设非遗优品交易系统的设计与实现【开题+程序+论文+源码】
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景在全球化与现代化的浪潮中,非物质文化遗产(简称“非遗”)作为民族文化的瑰宝,承载着丰富的历史记忆与独特的文化价值。然而,随着时代的变迁,许多非遗项目面......
  • java+vue计算机毕设电影推荐系统【开题+程序+论文+源码】
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着信息技术的飞速发展和互联网娱乐产业的蓬勃兴起,电影作为一种重要的文化娱乐形式,已经深入人们的日常生活。近年来,电影市场的规模不断扩大,电影数量......
  • java+vue计算机毕设儿童培训管理系统的设计与实现【开题+程序+论文+源码】
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着社会的快速发展和教育理念的不断进步,儿童培训行业迎来了前所未有的发展机遇。越来越多的家长开始重视孩子的全面发展,不仅关注学校的传统教育,还积......
  • 登录功能-Java实现token的生成与验证
    一、token与cookie相比较的优势1、支持跨域访问,将token置于请求头中,而cookie是不支持跨域访问的;2、无状态化,服务端无需存储token,只需要验证token信息是否正确即可,而session需要在服务端存储,一般是通过cookie中的sessionID在服务端查找对应的session;3、无需绑定到一个特殊的身份......
  • Java语言快速实现简单MQ消息队列服务
    目录MQ基础回顾主要角色自定义协议流程顺序项目构建流程具体使用流程代码演示消息处理中心Broker消息处理中心服务BrokerServer客户端MqClient测试MQ小结 MQ基础回顾在上一篇消息通讯之关于消息队列MQ必须了解的相关概念中,我们尽可能地详细的了解......