Java多线程编程核心技术——学习笔记

非线程安全问题主要指多个线程对同一个对象中的同一个实例变量进行操作时会出现值被更改、值不同步的情况,进而影响程序执行流程

多线程常用方法

  • currentThread()方法返回代码正在被哪个线程调用

  • isAlive()方法是判断当前的线程是否存活

  • StackTraceElement[] getStackTrace() 方法是作用是返回一个表示该线程堆栈跟踪元素数组

  • static void dumpStack() 方法的作用是将当前线程的堆栈跟踪信息输出至标准错误流。该方法仅用于调试

  • static Map<Thread,StackTraceElement[]> getAllStackTrace() 方法的作用是返回所有活动线程的堆栈跟踪的一个映射。映射键是线程,每个映射值都是一个StackTraceElement数组。

  • getId() 方法用于获取线程的唯一标识

  • sleep() 让当前正在执行的线程休眠指定毫秒数

  • interrupt() 方法仅仅是在当前线程中做了一个停止的标志,并不是真正停止线程

    不管其调用顺序,只要interrupt() 和 sleep() 方法遇到一起就会产生异常

  • 判断线程是否为停止状态:

    • public static boolean interrupted(): 测试currentThread()是否已经中断,执行后具有清楚状态标志值为false的功能。

    • public boolean this.isInterrupted(): 测试this关键字所在类的对象是否已经中断,不清除状态标志。

  • stop() 方法暴力停止线程(已被弃用):调用时,会抛出 java.lang.ThreadDeath 异常

    • stop() 方法呈删除线程状态,容易造成业务处理的不确定性
    • 使用stop() 会释放锁给数据造成不一致的结果
  • suspend() 方法和 resume() 方法的缺点:

    • 极易造成公告同步对象被独占,其他线程无法访问公告同步对象的结果

    • 容易因为线程暂停,造成数据不完整的情况

  • yield() 方法:让当前线程放弃当前的CPU资源,放弃的时间不确定。

  • 线程优先级设置通过 setPriority() 方法,在Java中优先级分为1~10个等级,如果参数小于1或大于10会抛出异常,同时线程具有继承性。(如:A线程启动B线程,则B线程的优先级与A线程一样)

    • 优先级的规律性:CPU尽量将执行资源让给优先级比较高的线程

    • 优先级的随机性:优先级高的线程不一定每一次都先执行完

    • 优先级高的线程运行的速度快

**守护线程:**守护线程是一种特殊的线程,当进程中不存在非守护线程了,则守护线程自动销毁。最典型的守护线程就是垃圾回收线程,守护线程的作用是为其他线程的运行提供便利服务(凡是调用setDaemon(true) 代码并传入true值的线程才是守护线程)。

Synchronized

  1. 关键字 Synchronized 可用来保障原子性、可见性和有序性

    原理:Synchronized 关键字通过使用 flag 标志 ACC_SYN-CHRONIZED,当调用方法时,调用指令会检查方法的 ACC_SYN-CHRONIZED 访问标志是否设置,如果设置了,执行线程先持有同步锁,然后执行方法,最后在方法完成时释放锁。

  2. 如果使用了 Synchronized 代码块,则使用 monitorenter 和 monitorexit 指令进行同步处理。

  3. 只有多个线程执行相同业务对象中的同步方法时,线程与业务对象属于多对一的关系时,为了避免出现非线程安全问题,所以使用 Synchronized。

  4. 在方法声明处添加 Synchronized 并不是锁方法,而是锁当前类的对象,Java中只有“将对象作为锁”的说法,如果在对象中使用了 Synchronized 关键字声明非静态方法,则对象就被当成锁

  5. 关键字 Synchronied 拥有重入锁的功能,即在使用时,当一个线程得到一个对象锁后,再次请求此对象锁时是可以得到该对象锁的。锁重入也支持继承的环境。

  6. 当一个线程执行的代码出现异常时,其所持有的锁会自动释放。

  • public static boolean holdsLock( Object obj ) 方法的作用是当 currentThread 在指定的对象上保持锁定时,才返回 true。

  • 当一个线程访问 object 的一个 synchronized(this) 同步代码块时,其他线程对同一个 object 中所有其他 synchronized(this) 同步代码块的访问将被阻塞,这说明 synchronized 使用的对象监视器是同一个锁对象。

  • 使用“ synchronized (非this对象x) 同步代码块 ” 格式进行同步操作时,锁必须是同一个,如果不是同一个锁,则运行结果就是异步调用,交叉运行。

  • 同步代码块放在非同步 synchronized 方法中进行声明,并不能保证调用方法的线程的执行同步(顺序性),也就是线程调用方法的顺序是无序的,虽然在同步代码块中执行的顺序是同步的。

  • synchronized (非 this 对象 x ) 格式的写法是将x对象本身作为“对象监视器”,这样得出3个结论:

    • 当多个线程同时执行 synchronized (x) {} 同步代码块呈同步效果
    • 当其他线程执行 x 对象中 synchronized 同步方法时呈同步效果
    • 当其他线程执行 x 对象方法里面的 synchronized (this) 代码块时呈现同步效果
  • 每一个 *.java 文件对应 Class 类的实例都是一个,在内存中是单例存在的

  • Synchronized 关键字加到 static 静态方法上的方式是将 Class 类对象作为锁,而 synchronized 关键字加到非 static 静态方法上的方式是将方法所在类的对象作为锁。

  • Class 锁可以对类的所有对象实例起作用,即使用 synchronized static 和 synchronized(this) 。

  • 在将任何数据类型作为同步锁时,需要注意是否有多个线程同时争抢锁对象。如果多个线程同时争抢相同的锁对象,则这些线程之前就是同步的;如果多个线程分别获得自己的锁,则这些线程之间就是异步的。

Volatile

  1. volatile 关键字其主要作用是强制线程访问公共栈堆并从中取值,只能修饰变量

  2. volatile 本身并不处理 int i++ 操作的原子性,可以使用 Atomic 原子类进行 i++ 操作实现原子性,但在有逻辑性的情况下,原子类的输出结果具有随机性

  3. 使用 volatile 可以禁止代码的重排序(作为一个屏障,不能跨过屏障重排,synchronized 也有相应的作用)

线程间通信

  • wait() 方法:立即释放锁

  • sleep() 方法:不释放锁

  • notify() 方法:不立即释放锁,必须执行完 notify() 方法所在的同步 synchronized 代码块后才会释放锁。

  • notifyAll() 方法

  • join() 方法:作用是使所属的线程对象 x 正常执行 run() 方法中的任务,而使当前线程 z 进入无限期的阻塞,等待线程 x 销毁后再继续执行 z 之后的代码,具有串联执行的效果,join() 方法在内部使用了 wait() 方法进行等待。(立即释放锁)

ThreadLocal

  • 作用:是将数据放入当前线程对象中的 Map 中,执行后,每个线程中的 Map 存有自己的数据,Map中的 key 存储的是 ThreadLocal 对象,value 就是存储的值,解决的是变量在不同线程的隔离性。

  • 通过重写 initialValue() 方法,可以使 get() 方法返回的默认值不为 null

  • 不能实现值的继承, 可以使用 InheritableThreadLocal 类使子线程继承父线程的值,重写 childValue() 可以对父线程继承的值进行加工修改

Lock对象

  1. jdk1.5新增加 ReentrantLock 类具有与 Synchronized 关键字同样的效果,并且扩展功能更加强大,具有嗅探锁定,多路分支通知等功能。

  2. lock() 方法获取锁,unlock() 方法释放锁

  3. ReentrantLock 结合 Condition 类可以实现“选择性通知”,Condition 对象的作用是控制并处理线程的状态,可以使线程呈 wait 状态,也可以让线程继续运行。

private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
  1. await() 方法的作用是使当前线程在接到通知或被中断之前一直处于等待 wait 状态,与 wait() 方法一样。在方法前要先用 lock() 方法获得锁,否则会报错。

  2. 使用 await() / signal() 实现 wait / notify 模式,signalAll() 方法相当于 notifyAll()。

  • getHoldCount() 方法:调用 lock() 方法的次数

  • getQueueLength() 方法:返回正在等待获取此锁的线程估计数

  • getWaitQueueLength( Condition condition ) 方法:返回等待与此锁相关的给定条件 Condition 的线程的估计数

  • hasQueueThread( Thread thread ) 方法:查询指定线程是否正在等待获取此锁

  • hasQueueThreads() 方法:查询是否有线程正在等待获取此锁

  • hasWaiters( Condition condition ) 方法:查询是否有线程正在等待与此锁有关的 condition 条件,也就是是否有线程执行了 condition.await() 而呈等待状态

  • isFair() 方法:判断是否是公平锁,ReentrantLock 默认情况下非公平锁

  • isHeldByCurrentThread() 方法:查询当前线程是否保持此锁

  • isLocked() 方法:查询此锁是否由任意线程保持,并没有释放

  • void lockInterruptibly() 方法:作用是当某个线程尝试获得锁并且阻塞在 lockInterruptibly() 方法时,可以被中断

  • boolean tryLock() 方法:作用是嗅探拿锁,如果当前线程发现锁被其他线程持有了,则返回 false,程序继续执行后面的代码,而不是阻塞等待锁的状态。

  • void awaitUninterruptibly() 方法:实现线程在等待的过程中,不允许被中断。

  • ReentrantLock 类具有完全的互斥排他效果,保证了线程的安全性,但效率十分低下,因此 JDk 提供了一种读写锁——ReentrantReadWriteLock 类,使用它进行读操作时,不需要同步操作。

  • 读写锁有两个锁:一个读操作的锁,也称共享锁;另一个写操作的锁,也称排他锁,

定时器Timer

  • TimerTask 类的主要作用是封装任务,是一个抽象类,把要执行的任务代码放到 TimerTask 的子类中

  • schedule( TimerTask task, Date time) :指定日期执行一次某一任务

  • schedule( TimerTask task, Date time, long period) :在指定日期之后按指定的间隔周期,无限期地循环某一任务

  • schedule( TimerTask task, long delay) :以当前时间为参考,在此时间的基础上延迟指定毫秒数后执行一次 TimerTask 任务

  • schedule( TimerTask task, long delay, long period) :以当前时间为参考,在此时间的基础上延迟指定毫秒数后无线次数的执行 TimerTask 任务

  • TimerTask 类的 cancel() 方法是将自身从任务队列中清除

  • Timer 类的 cancel() 方法是将任务队列中的全部任务清空

猜你喜欢

转载自blog.csdn.net/weixin_45636641/article/details/108297371