多线程相关2

  • synchronized与ReentrantLock可重入锁的区别?

1、锁的实现:Synchronized是依赖于JVM实现的,而ReenTrantLock是JDK实现的

2、性能的区别:在Synchronized优化以前,synchronized的性能是比 ReenTrantLock差很多的,但是自从Synchronized引入了偏向锁,轻量级锁(自旋锁) 后,两者的性能就差不多了,在两种方法都可用的情况下,官方甚至建议使用synchronized

3、功能区别:Synchronized的使用比较方便简洁,并且由编译器去保证锁的加锁和释放,而ReenTrantLock需要手工声明来加锁和释放锁,为了避免忘记手工释放锁造成死锁,所以最好在finally中声明释放锁

4、ReenTrantLock独有的能力:

(1)ReenTrantLock可以指定是公平锁还是非公平锁。而synchronized只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁。

(2)ReenTrantLock提供了一个Condition(条件)类,用来实现分组唤醒需要唤醒的线程们,而不是像synchronized要么随机唤醒一个线程要么唤醒全部线程。

(3)ReenTrantLock提供了一种能够中断等待锁的线程的机制,通过lock.lockInterruptibly()来实现这个机制。

  • 乐观锁和悲观锁的区别?

悲观锁(Pessimistic Lock)

具有强烈的独占和排他特性。每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。

乐观锁(Optimistic Lock)

每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。

  • 如何实现一个乐观锁?

使用版本号机制和CAS算法实现

1. 版本号机制

一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。

2. CAS算法

即compare and swap(比较与交换),是一种有名的无锁算法。无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步(Non-blocking Synchronization)。CAS算法涉及到三个操作数

需要读写的内存值 V

进行比较的值 A

拟写入的新值 B

当且仅当 V 的值等于 A时,CAS通过原子方式用新值B来更新V的值,否则不会执行任何操作(比较和替换是一个原子操作)。一般情况下是一个自旋操作,即不断的重试。

  • AQS是如何唤醒下一个线程的?

用unpark函数唤醒等待队列中最前边的那个未放弃线程

  • ReentrantLock公平和非公平锁是如何实现?

非公平:

1.调用lock()方法时,首先去通过CAS尝试设置锁资源的state变量,如果设置成功,则设置当前持有锁资源的线程为当前请求线程

2.调用tryAcquire方法时,首先获取当前锁资源的state变量,如果为0,则通过CAS去尝试设置state,如果设置成功,则设置当前持有锁资源的线程为当前请求线程

公平:

1.调用lock()方法时,不进行CAS尝试

2.调用tryAcuqire方法时,首先获取当前锁资源的state变量,如果为0,则判断该节点是否是头节点可以去获取锁资源,如果可以才通过CAS去尝试设置state

  • CountDownLatch和CyclicBarrier的区别?各自适用于什么场景?

CountDownLatch : 一个线程(或者多个), 等待另外N个线程完成某个事情之后才能执行。 CountDownLatch 是计数器, 线程完成一个就记一个, 就像 报数一样, 只不过是递减的.

 CyclicBarrier : N个线程相互等待,任何一个线程完成之前,所有的线程都必须等待。CyclicBarrier更像一个水闸, 线程执行就像水流, 在水闸处都会堵住, 等到水满(线程到齐)了, 才开始泄流.

  • 使用ThreadLocal时要注意什么?比如说内存泄漏?

ThreadLocal是用来维护线程中的变量不被其他线程干扰而出现的一个结构,内部包含一个ThreadLocalMap类,该类为Thread类的一个局部变量,该Map存储的key为ThreadLocal对象自身,value为我们要存储的对象,这样一来,在不同线程中,持有的其实都是当前线程的变量副本,与其他线程完全隔离,以此来保证线程执行过程中不受其他线程的影响。

ThreadLocal的主要用途是为了保持线程自身对象和避免参数传递,主要适用场景是按线程多实例(每个线程对应一个实例)的对象的访问,并且这个对象很多地方都要用到。

 

  • 说一说往线程池里提交一个任务会发生什么?

判断当前线程数是否小于corePoolSize,如果小于,则新建核心线程,不管核心线程是否处于空闲状态

核心线程创建满之后,后续的任务添加到workQueue中

如果workQueue满了,则开始创建非核心线程直到线程的总数为maximumPoolSize

当非核心线程数也满了,队列也满了的时候,执行拒绝策略

  • 线程池的几个参数如何设置?

corePoolSize:核心线程数

    • 核心线程会一直存活,及时没有任务需要执行
    • 当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理
    • 设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭

queueCapacity:任务队列容量(阻塞队列)

    • 当核心线程数达到最大时,新任务会放在队列中排队等待执行

maxPoolSize:最大线程数

    • 当线程数>=corePoolSize,且任务队列已满时。线程池会创建新线程来处理任务
    • 当线程数=maxPoolSize,且任务队列已满时,线程池会拒绝处理任务而抛出异常
  • keepAliveTime:线程空闲时间
    • 当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize
    • 如果allowCoreThreadTimeout=true,则会直到线程数量=0
  • allowCoreThreadTimeout:允许核心线程超时
  • rejectedExecutionHandler:任务拒绝处理器

两种情况会拒绝处理任务:

    • 当线程数已经达到maxPoolSize,切队列已满,会拒绝新任务
    • 当线程池被调用shutdown()后,会等待线程池里的任务执行完毕,再shutdown。如果在调用shutdown()和线程池真正shutdown之间提交任务,会拒绝新任务
    • 线程池会调用rejectedExecutionHandler来处理这个任务。如果没有设置默认是AbortPolicy,会抛出异常

ThreadPoolExecutor类有几个内部实现类来处理这类情况:

    • AbortPolicy 丢弃任务,抛运行时异常
    • CallerRunsPolicy 执行任务
    • DiscardPolicy 忽视,什么都不会发生
    • DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务
    • 实现RejectedExecutionHandler接口,可自定义处理器

 

  • 线程池的非核心线程什么时候会被释放?

当系统中非核心线程(额外创建的线程)闲置时间超过 keepAliveTime 之后,会被释放;或者执行过程中抛出异常时也会被释放

  • 如何排查死锁?

死锁概念:1)必须是两个或者两个以上进程(线程)(2)必须有竞争资源

1.我们先用Jps来查看java进程id(或者Linux的ps命令)

2.看一下jstack的使用

3.jstack输出线程dump信息到文件

4.查看dump文件,然后进行分析

CPU100%排查

1、使用top命令查看cpu占用资源较高的PID

2、通过jps 找到当前用户下的java程序PID

执行 jps -l 能够打印出所有的应用的PID,找到有一个PID和这个cpu使用100%一样的ID!!就知道是哪一个服务了。

3、 使用 pidstat -p 1 3 -u -t

4、找到cpu占用较高的线程TID

5、将TID转换为十六进制的表示方式

6、通过jstack -l 输出当前进程的线程信息

7、查找 TID对应的线程(输出的线程id为十六进制),找到对应的代码

压力测试使用jstack找到系统的代码性能问题

1、在进行压力测试的时候,使用jps找到应用的PID

2、然后使用jstack输出出压力测试时候应用的dump信息

3、分析输出的日志文件中那个方法block线程占用最多,这里可能是性能有问题,找到对应的代码分析

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值