Contents
  1. 1. 问过的问题
  2. 2. java锁机制的实际运用
  3. 3. synchronized系列(对象锁)
    1. 3.1. synchronized的用法
    2. 3.2. 注意事项:
  4. 4. volatile关键字
  5. 5. wait/notify/notifyall sleep yeild await/singnal/singnalAll的用法(生产消费模型中 obj.wait())
  6. 6. java 死锁是否会发生以及如何避免死锁
  7. 7. 创建线程的3种方式(Thread和Callable接口有返回值)和比较
    1. 7.1. 3种方式的比较
  8. 8. Thread的一些常见方法
  9. 9. ReentrantLock API
  10. 10. 在Java中Lock接口比synchronized块的优势是什么?
  11. 11. JUC下的工具类(底层都是基于Lock的操作)
  12. 12. 线程池的使用
  13. 13. 线程池的关闭

问过的问题

  • java锁机制和运用–> 我说了 Socket编程当中IO流的 线程池使用,还有ConcurrentHashMap中的分段锁和volatile的使用,问CHMap中size()的实现原理(Done)
  • String final类的实现原理
  • Syncizaer的用法, Object.wait 和notify的使用,可不可以不再Syniczer中使用wait方法,实际不可以,会报错。以及java加锁的时候会不会出现死锁的情况,如何解决?(Done)

多线程:

  • 创建线程的几种方式,那种最佳(根据业务场景,不复用的话 内部类、复用的话 Thread重写run方法 ,Runnable接口实现),实际上还有Future等等啊(Done)
  • Tomcat的线程管理和如何控制并发,讲了一下JAVA的JUC下的线程池,构造函数,然后他问实际使用场景。
  • JUC下的Executors框架熟悉吗? 不熟悉,那么数据库的线程池熟悉吗? 超过连接数如何处理

java锁机制的实际运用

  • 阻塞队列,ArrayBlockingQueue一个锁、LinkedBlockingQueue两把锁
  • ConcurrentHashMap中
  • 生产和消费场景中,手动实现或者阻塞队列来实现

synchronized系列(对象锁)

synchronized的用法

  1. 修饰普通方法,相当于锁当前对象,调用者,也指 this对象
  2. 修饰静态方法,相当于锁当前类对象,也指 Test.class
  3. 修饰代码块,可以缩小锁的范围,提升性能
  4. 锁对象(this,类对象,普通对象)

注意事项:

  1. 锁普通方法和锁this的代码块,都是对象锁,锁静态方法和Test.class是类锁。
  2. 有锁方法和无锁方法互相不影响
  3. 类锁和对象锁互相不影响
  4. 释放锁的时候,修改的变量对所有线程可见,满足HB原则,从而实现线程安全

volatile关键字

公用的对象存放在主内存当中,每个线程去处理公用对象的时候会拷贝镜像到本地内存当中,在CPU进行读取,修改,写回到本地内存,最后写回到主内存当中,这时候线程之间不可见。volatile关键字会让读取和写的操作的时候,会立刻通知更新主内存同步,并不保证线程安全。

  • 轻量级的同步机制,实现变量的改变对所有线程可见,并不能避免线程安全
  • 每次读的时候去读主存上的值而不是本地栈中的值,每次写都写到主存当中
  • 该方法中禁止指令重排序优化

wait/notify/notifyall sleep yeild await/singnal/singnalAll的用法(生产消费模型中 obj.wait())

  • 三个方法都必须在synchronized 同步关键字所限定的作用域中调用,否则会报错java.lang.IllegalMonitorStateException
  • 调用wait方法,释放对象所,阻塞当前线程,不会参与CPU的竞争,等待被notify()
  • 当其他线程调用了wait方法时,该对象的锁被释放,被B线程获取之后,B执行各种操作之后,调用该对象的notify方法,但是不会立刻释放CPU资源,一直到该静态代码块执行完,然后再释放对象锁,A线程被唤醒,继续执行
    obj.wait()
    obj.notify() 仅通知一个obj的等待线程,结束时间是静态代码块执行完,而不是该线程执行完
    obj.notifyall() 通知所有的obj的等待线程

  • JUC里面有Lock替代synchronized,所以就需要有condition.await() 来实现object的wait()功能,否则实现不了线程通讯。

  • Thread.sleep(1000); 使当前线程睡眠1秒钟,1秒钟后重新开始争夺CPU资源。
  • Thread.yeild() hit调度器出让线程的争夺权,当然也不一定有效

java 死锁是否会发生以及如何避免死锁

  • 不同线程同时争抢多个锁的时候,会导致死锁。
    避免方法:
  1. 创建一个抢锁对象,比如Common.class,先获取到抢锁对象后,再进行多锁的方法操作,可以避免死锁的发生。
  2. 改用juc下面的定时锁,还有trylock,快速返回的,失败就释放当前锁,可以避免

创建线程的3种方式(Thread和Callable接口有返回值)和比较

  1. 线程类继承并重写 Thread的run方法,然后new出来.start()即可
  2. 实现runnable接口的类,作为new Thread(参数)的参数,调用start()即可
  3. A类实现Callable接口的call方法,然后作为FutureTask的构造参数,最后加入到线程池里面,或者Thread的构造参数里面启动,然后result.get()获取返回值
    匿名内部类也有上面2种不同的实现Thread方式

    3种方式的比较

  4. runnable接口和callable接口的方式是以实现的方式,java是单继承,所以更好。
  5. 接口实现类作为target可以复用
  6. runnable有异步的返回值

Thread的一些常见方法

  1. join():父线程创建子线程,并start子线程后,调用 子线程.join()方法,可以阻塞父线程,直到子线程结束,这里面满足 HB原则
  2. yeild():尝试出让cpu的执行权
  3. wait():线程调度里面,在一个锁里面,调用 obj.wait方法,放弃当前线程的执行权,让其他线程开始争夺锁。wait方法必须在 锁里面才可以调用,不然报错。notify、notifyAll来唤醒
  4. sleep():阻塞当前线程,但是并不释放锁,一定时间后,重新争夺CPU执行权。
  5. interrupt() :并不阻塞当前线程,而是修改子线程的打断标志位,可用于线程之间的调度。如果线程不在运行状态,会报错。

ReentrantLock API

  1. lock(true,false):无条件地轮询获取锁的方式
  2. lockInterruptibly()提供了可中断的锁获取方式,争夺锁的过程时,会check 是否中断的状态位,如果true,就抛出异常
  3. trylock()、 tryLock(timeout),立刻返回结果,避免死锁

在Java中Lock接口比synchronized块的优势是什么?

提供轮询锁避免死锁、定时轮询锁、可中断的锁、锁分块技术CHashMap、公平锁等等高级灵活的功能。但是缺点是一定要在finally里面 unlock()资源才行。不然会一直在。

  • 实现原理不同,synchronized基于JVM的实现,根据对象的消息头、唯一对应的monitor、线程栈桢中的minitor record来实现的,会根据锁竞争的程度不同,动态的升级 偏向锁、轻量级锁和重量级锁。而JUC下的Lock是基于JDK实现的,实现了更多的灵活的功能。
  • 使用方法不同,synchronized的使用多种方式,修饰方法、修饰代码块,锁this对象、obj、类对象,而且无需手动释放,相对而言,面向开发人员更简约。Lock用法lock的API很多,unlock等操作。
  • ReentrantLock实现了synchronized的公平队列功能,而Synch都是非公平锁
  • ReentrantReadWriteLock类实现了读写锁的功能,而synchronized做不到
  • ReentrantLock提供了trylock() 和 trylock(tryTimes)的功能,不等待或者限定时间等待获取锁,更灵活。可以避免死锁的发生。
  • ReentrantLock提供了lockInterruptibly()的功能,可以中断争夺锁的操作
  • ReentrantLock提供了比Sync更精准的线程调度工具,Condition,一个lock可以有多个Condition,但是Synizer只要一个WaitSet

JUC下的工具类(底层都是基于Lock的操作)

  1. BlockQueue:多种策略实现方式,解决有界的生产和消费队列问题
  2. CountDownLatch:2种方法配合,主要解决阻塞主线程,需要等待多个子线程都完成某一操作时,主线程再执行某一个操作,比如下载多sheet的文件。
  3. CyclicBarrier:1个方法,主要解决类似于集合的业务场景,指定数量的线程调用await方法时,所有的阻塞线程再同时执行某一操作,比如跑步开枪比赛,所有人到齐才开始跑。
  4. Semaphore:2个方法配合,主要解决有限资源无限竞争,比如多个人抢5个茅坑,一次最多5个人上一样,其它等待。

线程池的使用

  1. 5层继承关系:Executor顶层接口(execute方法) |ExcutorService接口类(submit、terminate) |AbstractExecutorService |ThreadPoolExecutor |ScheduledThreadPoolExecutor

  2. ThreadPoolExcutor的7个核心参数(coresize、maxSize、BlockingQueue、Time、Union、ThreadFactory、抛弃策略)

  3. Excutors类的常见的4个实现类(结合阻塞队列)

  • FixedThreadPool(无界的LinkedBlockingQueue)
  • SingleThreadPool(无界的LinkedBlockingQueue,不仅单个线程,而且是严格有序的执行)
  • CachedThreadPool,同步线程池,coreSize=0,SynchronousQueue
  • ScheduledThreadPool,构造函数用到ScheduledThreadPoolExecutor
  1. 线程池的管理:执行、关闭、执行状态查询和结果获取
  • 两种执行方式:execute 不会返回线程执行是否成功,submit 返回一个future对象可以阻塞获取某线程的执行情况
  • shutdown或shutdownNow方法来关闭线程池
  • submit返回Future,查询线程执行情况:cancel()、isCancel()、isDone()、get()

线程池的关闭

shutdown或shutdownNow方法来关闭线程池,但是它们的实现原理不同,

  • shutdown的原理是只是将线程池的状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程。
  • shutdownNow的原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止。shutdownNow会首先将线程池的状态设置成STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表。
Contents
  1. 1. 问过的问题
  2. 2. java锁机制的实际运用
  3. 3. synchronized系列(对象锁)
    1. 3.1. synchronized的用法
    2. 3.2. 注意事项:
  4. 4. volatile关键字
  5. 5. wait/notify/notifyall sleep yeild await/singnal/singnalAll的用法(生产消费模型中 obj.wait())
  6. 6. java 死锁是否会发生以及如何避免死锁
  7. 7. 创建线程的3种方式(Thread和Callable接口有返回值)和比较
    1. 7.1. 3种方式的比较
  8. 8. Thread的一些常见方法
  9. 9. ReentrantLock API
  10. 10. 在Java中Lock接口比synchronized块的优势是什么?
  11. 11. JUC下的工具类(底层都是基于Lock的操作)
  12. 12. 线程池的使用
  13. 13. 线程池的关闭