Java基础复习之多线程(3)

本篇博客的思维导图原图在公众号里面下载即可。

多线程实现方式

有三种:
1.继承Thread类,重写run方法。
2.实现Runnable接口。然后通过Thread构造方法传入该实现类然后调用start方法。
3.实现Callable接口。该接口可以实现多线程有返回值。

public class Test1 {
    public static void main(String[] args) throws Exception {
        C c = new C();
        Integer call = c.call();
        System.out.println(call);
    }
}
class C implements Callable<Integer>{

    @Override
    public Integer call() throws Exception {
        int sum=0;
        for (int i=0;i<100;i++){
            sum+=i;
        }
        return sum;
    }
}

有关线程池的概念

Java中线程池的顶级接口是Executor,但其严格来说是一个执行线程的工具,真正的线程池接口是ExecutorService
线程池有四种:如下图
在这里插入图片描述

线程的生命周期

线程有五种状态:如下图。

在这里插入图片描述

线程状态的转化

线程的几种状态中,除了新建和死亡,其他三种状态是会相互转变的。
在这里插入图片描述

终止线程的四种方式

  • 【线程正常结束】。
  • 【使用标识符结束线程】,我们可以定义一个使用volatile修饰的boolean变量结合while来判断线程结束的条件。
  • 【Interrupt方法终止线程】,并且分两种情况:
    第一种:当线程处于阻塞状态时,如使用了 sleep,同步锁的 wait,socket 中的 receiver,accept 等方法。此时如果调用Interrupt方法时,线程会抛出InterruptException异常.阻塞中的代码抛出该异常,通过代码进行捕获,然后break跳出循环状态,从而实现线程的结束。不是调用了Interrupt方法就可以结束线程了,一定要先捕获异常之后通过break来跳出循环才可以正常结束run方法
    第二种:当线程未处于阻塞状态时,使用isInterrupt方法来判断线程的中断标志来退出循环
  • 【stop方法终止线程】,通过调用stop方法会直接强制线程终止,具体产生不安全的原因如下:当调用stop方法后,创建子线程的线程就会抛出ThreadDeatherror的错误,并且会释放子线程所持有的所有锁,这样就导致了该线程持有的锁突然释放,会出现其保护的数据出现不一致性,其他线程再使用这个产生影响的数据后会出现其他不知明的错误产生,使用使用该方法十分危险。

Sleep和wait的区别

  • sleep属于Thread类的方法,wait属于Object类中的方法
  • sleep方法导致了程序暂停执行指定的时间,让出CPU资源给其他线程,但是它的监视状态依旧是保持者,当指定的时间到了又自动恢复运行状态
  • 在调用sleep方法时,线程不会释放对象锁
  • 调用wait方法时,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。

start和run方法的区别

  • start()方法真正实现了多线程运行。这时无需等待方法体代码执行完毕就可以继续执行下面的代码。
  • 通过调用Thread类的statr()方法来启动一个线程,此时线程处于就绪状态,并没有运行。
  • run()方法称为线程体,包含了要执行的线程的内容,线程进入运行状态,开始运行函数中的代码。run方法运行结束,线程结束,然后CPU再度调用其他线程。

Java中的锁

  • 【乐观锁】:该锁其实是一种乐观的思想,它认为读多写少,遇到并发的可能性低,每次去拿数据的时候都认为别人不会修改,所以不会上锁。但是在更新的时候还是会判断一下在此期间别人有没有去更新这个数据,采取在写数据时先读出当前版本号,然后加锁操作(比较上一次的版本号,如果一样则更新),如果失败则要重复读-比较-写这个步骤。java中的乐观锁基本都是通过CAS操作实现的,CAS是一种更新的原子操作,比较当前值跟传入的值是否一样,一样则更新,否则失败。
  • 【悲观锁】也是一种思想,是一种悲观的思想,认为写多读少,遇到的并发写的可能性高,每次去拿数据的时候都认为别人会修改,所以每次在读写数据的时候都会上锁,这样别人想读写这个数据就会block直到拿到锁。java中的悲观锁就是Synchronized。AQS框架下的锁则是先去尝试cas乐观锁去获取锁,获取不到,才会转换为悲观锁,如RetreenLock.
  • 【自旋锁】如果持有锁的线程能在很短的时间内释放锁资源,那么那些等待竞争锁的线程就不需要做内核态和用户态之间的切换进去阻塞挂起状态,它们只要等一等(自旋),等持有锁的线程释放后立即获取锁,这样就避免用户线程和内核的切换的消耗。线程自旋是需要消耗cpu的,其实就是让cpu做无用功,占着cpu资源,如果一直获取不到锁,那么线程也不能一直占用cpu自旋做无用功,所以需要设定一个自旋等待的最大时间。如果持有锁的线程执行的时间超过自旋等待的最大时间仍然没有释放锁,那么就会导致其他争用锁的线程在最大等待时间内还是获取不到锁,这时,争用线程就会停止自旋进入阻塞状态。
自旋锁的优点自旋锁的缺点
自旋锁可以尽可能减少线程的阻塞,这对于锁的竞争来说是不激烈的,并且对于占用锁时间非常短的代码块来说性能会大幅度提升 ,因为自旋的消耗会小于线程阻塞挂起再唤醒的操作的消耗,这些操作会导致线程发送两次上下文切换但当锁的竞争激烈,或者持有锁的线程需要长时间占用锁执行同步代码块,这个时候就不适合使用自旋锁了,因为这会造成cpu资源的浪费,我们此时需要关闭自旋锁
  • 【Synchronized同步锁】synchronized可以把任意一个非null的对象当作锁,它属于独占式的悲观锁,同时也是可重入锁。下面是Synchronized锁的作用范围和核心组件以及其实现过程。
    在这里插入图片描述

在这里插入图片描述

  • 【ReentrantLock】该锁继承了Lock接口并且实现了其方法,属于一种可重入锁。它除了可以完成Synchronized锁可以完成的所有功能外,还提供了诸如可响应中断锁、可轮询锁请求、定时锁等避免多线程死锁的方法。下面是其方法的具体内容:
    在这里插入图片描述
  • 【公平锁】公平锁指的是锁的分配机制是公平的,通常先对锁提出获取请求的线程会先被分配到锁,ReentrantLock 在构造函数中提供了是否公平锁的初始化方式来定义公平锁。
  • 【非公平锁】JVM 按随机、就近原则分配锁的机制则称为不公平锁,ReentrantLock 在构造函数中提供了是否公平锁的初始化方式,默认为非公平锁。非公平锁实际执行的效率要远远超出公平锁,除非程序有特殊需要,否则最常用非公平锁的分配机制。
  • 【ReadWriteLock 读写锁】为了提高性能,Java 提供了读写锁,在读的地方使用读锁,在写的地方使用写锁,灵活控制,如果没有写锁的情况下,读是无阻塞的,在一定程度上提高了程序的执行效率。读写锁分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,这是由 jvm 自己控制的,你只要上好相应的锁即可。
  • 【共享锁和独占锁】
共享锁独占锁
共享锁则允许多个线程同时获取锁,并发访问 共享资源,如:ReadWriteLock。共享锁则是一种乐观锁,它放宽了加锁策略,允许多个执行读操作的线程同时访问共享资源。独占锁模式下,每次只能有一个线程能持有锁,ReentrantLock 就是以独占方式实现的互斥锁。独占锁是一种悲观保守的加锁策略,它避免了读/读冲突,如果某个读线程获取锁,则其他读线程都只能等待,这种情况下就限制了不必要的并发性,因为读操作并不会影响数据的一致性。
  • 【重量级锁(Mutex Lock)】Synchronized 是通过对象内部的一个叫做监视器锁(monitor)来实现的。但是监视器锁本质又是依赖于底层的操作系统的 Mutex Lock 来实现的。而操作系统实现线程之间的切换这就需要从用户态转换到核心态,这个成本非常高,状态之间的转换需要相对比较长的时间,这就是为什么Synchronized 效率低的原因。因此,这种依赖于操作系统 Mutex Lock 所实现的锁我们称之为“重量级锁”。JDK 中对 Synchronized 做的种种优化,其核心都是为了减少这种重量级锁的使用。JDK1.6 以后,为了减少获得锁和释放锁所带来的性能消耗,提高性能,引入了“轻量级锁”和“偏向锁”。
  • 【轻量级锁】锁的状态总共有四种:无锁状态、偏向锁、轻量级锁和重量级锁。
  • 【偏向锁】偏向锁的目的是在某个线程获得锁之后,消除这个线程锁重入(CAS)的开销,看起
    来让这个线程得到了偏护。引入偏向锁是为了在无多线程竞争的情况下尽量减少不必要的轻量级
    锁执行路径。偏向锁则是在只有一个线程执行同步块时进一步提高性能。
  • 【分段锁】分段锁也并非一种实际的锁,而是一种思想 ConcurrentHashMap 是学习分段锁的最好实践

线程的基本方法

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值