《Java多线程编程核心技术》

@author ergwang

https://www.cnblogs.com/ergwang/

文章末尾附pdf和png下载链接

第1章 Java多线程技能

1. 进程与线程 区别? 联系?

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()方法的优先处理,其他的不处理了

思维导图

PDF下载: https://img.lyy52.wang/blogs/Java多线程编程核心技术.pdf

PNG下载:https://img.lyy52.wang/blogs/Java多线程编程核心技术.png

《Java多线程编程核心技术》知识梳理的更多相关文章

  1. 《Java 多线程编程核心技术》- 笔记

    作为业务开发人员,能够在工作中用到的技术其实不多.虽然平时老是说什么,多线程,并发,注入,攻击!但是在实际工作中,这些东西不见得用得上.因为,我们用的框架已经把这些事做掉了. 比如web开发,外面有大 ...

  2. Java多线程编程核心技术(一)Java多线程技能

    1.进程和线程 一个程序就是一个进程,而一个程序中的多个任务则被称为线程. 进程是表示资源分配的基本单位,线程是进程中执行运算的最小单位,亦是调度运行的基本单位. 举个例子: 打开你的计算机上的任务管 ...

  3. Java多线程编程核心技术---学习分享

    继承Thread类实现多线程 public class MyThread extends Thread { @Override public void run() { super.run(); Sys ...

  4. Java多线程编程核心技术---对象及变量的并发访问(二)

    数据类型String的常量池特性 在JVM中具有String常量池缓存的功能. public class Service { public static void print(String str){ ...

  5. Java多线程编程核心技术

    Java多线程编程核心技术 这本书有利于对Java多线程API的理解,但不容易从中总结规律. JDK文档 1. Thread类 部分源码: public class Thread implements ...

  6. 《Java多线程编程核心技术》推荐

    写这篇博客主要是给猿友们推荐一本书<Java多线程编程核心技术>. 之所以要推荐它,主要因为这本书写得十分通俗易懂,以实例贯穿整本书,使得原本抽象的概念,理解起来不再抽象. 只要你有一点点 ...

  7. 《java多线程编程核心技术》(一)使用多线程

    了解多线程 进程和多线程的概念和线程的优点: 提及多线程技术,不得不提及"进程"这个概念.百度百科对"进程"的解释如下: 进程(Process)是计算机中的程序 ...

  8. Thread.currentThread()和this的区别——《Java多线程编程核心技术》

    前言:在阅读<Java多线程编程核心技术>过程中,对书中程序代码Thread.currentThread()与this的区别有点混淆,这里记录下来,加深印象与理解. 具体代码如下: pub ...

  9. Java多线程编程核心技术(三)多线程通信

    线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体.线程间的通信就是成为整体的必用方案之一,可以说,使线程间进行通信后,系统之间的交互性会更强大,在大大提高CPU利用率的同时 ...

随机推荐

  1. suse 12 二进制部署 Kubernetets 1.19.7 - 第12章 - 部署dashboard插件

    文章目录 1.12.0.创建namespace 1.12.1.创建Dashboard rbac文件 1.12.2.创建dashboard文件 1.12.3.查看pod以及svc 1.12.4.获取 d ...

  2. netty系列之:channel,ServerChannel和netty中的实现

    目录 简介 channel和ServerChannel netty中channel的实现 AbstractChannel和AbstractServerChannel LocalChannel和Loca ...

  3. Spring 类名后缀理解

    Aware 理解 实现Spring的Aware接口. 定义为感知.意识,核心意义在于通过Aware可以把spring底层组件注入到自定义的bean中. 对于bean与容器的关系,bean不应该知道自身 ...

  4. 关于 Xcode 更新 appleID 更换

    可能不少人会遇到 前一位同事走之后,他的 appID帐号下载的东西更新不了 下面给予大家一个解决办法  例如 Xcode  1.打开引用程序目录 2.找到Xcode,右键"显示包内容&quo ...

  5. IDE 、SDK 、API区别、库、框架、组件、CLI

    IDE:集成开发环境:包括代码编辑器.代码检测.代码调试器.译器/解释器.以及其他工具 SDK:SDK是IDE的基础引擎 ,比IDE更基本,因为它通常没有图形工具.工程师为辅助开发某类软件的相关文档. ...

  6. Apache Ranger 编译安装部署

    1. 概述 Apache Ranger是大数据领域的一个集中式安全管理框架,目的是通过制定策略(policies)实现对Hadoop组件的集中式安全管理.用户可以通过Ranger实现对集群中数据的安全 ...

  7. Chrome:F12开发者模式下console不打印信息

    控制台不打印信息的解决方法 你要看看你是否在之前进行过查找关键字操作,操作之后忘记删去这个关键字,导致console中只会留下对于该关键字的查询结果.

  8. 关于Union和 Union all,以及出现 ORA-12704:字符集不匹配问题

    一.Union和 Union all 1.Union 对两个结果集进行并集操作: 对结果进行去重操作,不包括重复行: 并进行默认排序. -----效率相对较低 2.Union all 对两个结果集进行 ...

  9. 如何为k8s中的pod配置QoS等级?

    1.概述 本文介绍如何为pod分配特定的QoS等级. 我们知道,在k8s的环境中,通过使用QoS等级来做决定,在资源紧张的时候,将哪些的pod进行驱逐,或者说如何对pod进行调度. OK,话不多说,让 ...

  10. HelloWorld:通过demo,构建黑盒模型

    在<源码阅读四步走,这才是阅读源码的正确姿势>一文中,给出了源码阅读的完整步骤. 本篇是<如何高效阅读源码>专题的第四篇,正式开始讲解阅读源码的具体方法! 程序界有个老传统,学 ...