96SEO 2026-02-20 05:45 33
教问题Synchronized是乐观锁但是当发现竞争频繁则悲观锁如何检测数据访问是否冲突

自旋锁不放弃CPU轻量挂起等待锁重量好比追女生自旋锁一直问Synchronized就是通过自旋实现轻量级
ConcurrentHashMap在jdk1.8做了哪些优化4)
Hashtable和HashMap、ConcurrentHashMap
之间的区别?COncurrentHashMap为什么不能插入null
死锁死锁是什么如何避免死锁相关面试题谈谈死锁是什么如何避免死锁避免算法
有synchronized两个方法两个线程分别同时用这个方法请问会发生什么10)
面试题并行和并发有什么区别问题进程和线程有什么区别什么是进程什么是线程
问题多线程有什么优缺点问题线程的创建方式有哪些问题runnable
notifyAll()有什么区别问题如何简单的使用线程问题用户线程和守护线程有什么区别问题线程常用的方法有哪些问题start
有什么区别问题怎么终止线程问题线程和线程池有什么区别问题如何创建线程池问题推荐使用哪种方式来创建线程池问题说一下
的参数含义问题说一下线程池的执行流程问题线程池的拒绝策略都有哪些问题如何实现自定义拒绝策略问题如何实现线程池的扩展问题线程池的状态和线程的状态一样吗问题什么情况会导致线程池的状态发生改变问题线程池中
有什么区别问题多线程存在什么问题问题为什么会有线程安全问题问题如何解决线程安全问题问题synchronized
修饰静态方法和普通方法有什么区别吗问题synchronized
能保证线程安全吗问题说一下指令重排序及其问题和单例模式的懒汉模式结合问题volatile
使用场景有哪些问题产生死锁的原因有哪些问题如何解决死锁问题问题常见的锁策略有哪些问题乐观锁和
有什么区别问题线程通讯的方式线程等待和唤醒的实现手段如何停止线程线程池的优点如何停止线程池如何判断线程池已经完成volatile底层如何实现volatile的两个作用保证线程安全的手段synchronized
锁升级机制产生死锁的条件什么是AQS2022-08-10_CPU工作原理_作业2022-08-12_CPU工作原理等_作业操作系统操作系统如何进行进程调度
2022-08-13_线程概念等_作业进程和线程的区别和联系5种创建线程的方式编写代码,
2022-08-14_线程概念等_作业在子线程执行完毕后再执行主线程代码Thread数组请说明Thread类中run和start的区别java线程的状态
2022-09-06_线程安全_作业以下可以完全解决线程安全问题的选项是使用两个线程来累加
2022-09-15_线程安全等_作业顺序打印-进阶版方法一三个线程竞争同一个锁通过count判断是否打印方法二三个线程同时start分别上锁从a开始打印后唤醒b
2022-09-18_多线程代码案例简述线程池有什么优点编写代码,
2022-09-20_多线程面试题描述一下线程池的执行流程和拒绝策略有哪些使用ThreadPoolExecutor创建一个忽略最新任务的线程池
2022-09-25_多线程面试题编写代码实现两个线程增加同一个变量总结
总是假设最坏的情况每次去拿数据的时候都认为别人会修改所以每次在拿数据的时候都会上锁这样别人想拿这个数据就会阻塞直到它拿到锁。
假设数据一般情况下不会产生并发冲突所以在数据进行提交更新的时候才会正式对数据是否产生并发冲突进行检测如果发现并发冲突了则让返回用户错误的信息让用户决定如何去做。
参考资料https://blog.csdn.net/u010723709/article/details/48007881
Hashtable和HashMap、ConcurrentHashMap
有synchronized两个方法两个线程分别同时用这个方法请问会发生什么
悲观锁同学请教问题Synchronized是乐观锁但是当发现竞争频繁则悲观锁如何检测数据访问是否冲突
自旋锁不放弃CPU轻量挂起等待锁重量好比追女生自旋锁一直问Synchronized就是通过自旋实现轻量级
ConcurrentHashMap在jdk1.8做了哪些优化4)
Hashtable和HashMap、ConcurrentHashMap
之间的区别?COncurrentHashMap为什么不能插入null
死锁死锁是什么如何避免死锁相关面试题谈谈死锁是什么如何避免死锁避免算法
有synchronized两个方法两个线程分别同时用这个方法请问会发生什么10)
面试题并行和并发有什么区别问题进程和线程有什么区别什么是进程什么是线程
问题多线程有什么优缺点问题线程的创建方式有哪些问题runnable
notifyAll()有什么区别问题如何简单的使用线程问题用户线程和守护线程有什么区别问题线程常用的方法有哪些问题start
有什么区别问题怎么终止线程问题线程和线程池有什么区别问题如何创建线程池问题推荐使用哪种方式来创建线程池问题说一下
的参数含义问题说一下线程池的执行流程问题线程池的拒绝策略都有哪些问题如何实现自定义拒绝策略问题如何实现线程池的扩展问题线程池的状态和线程的状态一样吗问题什么情况会导致线程池的状态发生改变问题线程池中
有什么区别问题多线程存在什么问题问题为什么会有线程安全问题问题如何解决线程安全问题问题synchronized
修饰静态方法和普通方法有什么区别吗问题synchronized
能保证线程安全吗问题说一下指令重排序及其问题和单例模式的懒汉模式结合问题volatile
使用场景有哪些问题产生死锁的原因有哪些问题如何解决死锁问题问题常见的锁策略有哪些问题乐观锁和
有什么区别问题线程通讯的方式线程等待和唤醒的实现手段如何停止线程线程池的优点如何停止线程池如何判断线程池已经完成volatile底层如何实现volatile的两个作用保证线程安全的手段synchronized
锁升级机制产生死锁的条件什么是AQS2022-08-10_CPU工作原理_作业2022-08-12_CPU工作原理等_作业操作系统操作系统如何进行进程调度
2022-08-13_线程概念等_作业进程和线程的区别和联系5种创建线程的方式编写代码,
2022-08-14_线程概念等_作业在子线程执行完毕后再执行主线程代码Thread数组请说明Thread类中run和start的区别java线程的状态
2022-09-06_线程安全_作业以下可以完全解决线程安全问题的选项是使用两个线程来累加
2022-09-15_线程安全等_作业顺序打印-进阶版方法一三个线程竞争同一个锁通过count判断是否打印方法二三个线程同时start分别上锁从a开始打印后唤醒b
2022-09-18_多线程代码案例简述线程池有什么优点编写代码,
2022-09-20_多线程面试题描述一下线程池的执行流程和拒绝策略有哪些使用ThreadPoolExecutor创建一个忽略最新任务的线程池
2022-09-25_多线程面试题编写代码实现两个线程增加同一个变量总结
核上按细分的时间片轮流(交替)执行从逻辑上来看那些任务是同时执行。
参考答案进程Process是操作系统分配资源的基本单位一个进程拥有的资源有自己的堆、栈、虚存空间页表、文件描述符等信息。
线程Thread是操作系统能够进行运算调度的基本单位。
它包含在进程中是进程中的实际运行单位。
在
线程是轻量级的进程一个进程中包含了多个线程因此多个线程间可以共享进程资源线程和进程的关系如下图所示
其中堆和方法区是可以共享的区域而程序计数器和栈是每个线程私有的。
程序计数器是一块内存区域用来记录线程当前要执行的指令地址栈是用来记录每个线程自己的局部变量的堆中存放的是当前程序创建的所有对象方法区存放的是常量和静态变量等信息。
从属关系不同进程是正在运行程序的实例进程中包含了线程而线程中不能包含进程描述侧重点不同进程是操作系统分配资源的基本单位而线程是操作系统调度的基本单位共享资源不同多个进程间不能共享资源每个进程有自己的堆、栈、虚存空间页表、文件描述符等信息而线程可以共享进程资源文件堆和方法区上下文切换速度不同线程上下文切换速度很快上下文切换指的是从一个线程切换到另一个线程而进程的上下文切换的速度比较慢操纵者不同一般情况下进程的操纵者是操作系统而线程的操纵者是编程人员。
创建和销毁不同
小结简单来说进程是操作系统分配资源的基本单位而线程是操作系统调度的基本单位一个进程中至少包含一个线程线程不能独立于进程而存在进程不能共享资源而线程可以线程可以看作是轻量级的进程它们的主要区别体现在从属关系、描述侧重点、共享资源、上下文切换速度和操纵对象等不同线程可以看作是一个轻量级的“进程”。
多线程的缺点是它带来了编码的复杂度并且带来了**线程安全性问题**也就是程序的执行不符合预期结果的问题。
在Java中Runnable和Callable都是用于多线程编程的接口但它们有以下几个区别
Runnable接口只有一个run()方法该方法不返回任何值因此无法抛出任何checked
Callable接口则有一个call()方法它可以返回一个值并且可以抛出一个checked
Runnable的run()方法没有返回值只是一个void类型的方法。
Callable的call()方法却必须有一个返回值并且返回值的类型可以通过泛型进行指定。
在Runnable中我们无法对run()方法抛出的异常进行任何处理。
但在Callable中自定义的call()方法可以抛出一个checked
Runnable适用于那些不需要返回值且不会抛出checked
Exception的情况比如简单的打印输出或者修改一些共享的变量。
Callable适用于那些需要返回值或者需要抛出checked
Exception的情况比如对某个任务的计算结果进行处理或者需要进行网络或IO操作等。
在Java中常常使用Callable来实现异步任务的处理以提高系统的吞吐量和响应速度。
notify()和notifyAll()都是用来用来唤醒调用wait(方法进入等待锁资源队列的线程区别在于
如果有多个线程在等待则选择其中一个随机唤醒由调度器决定唤醒的线程享有公平竞争资源的权利
Thread-b);thread1.start();thread2.start();try
{e.printStackTrace();}synchronized
{System.out.println(Thread.currentThread().getName()
获得了锁);System.out.println(notify前
thread1.getState());System.out.println(notify前
thread2.getState());lock.notify();
{TimeUnit.MILLISECONDS.sleep(300);
{e.printStackTrace();}System.out.println(notify后
thread1.getState());System.out.println(notify后
thread2.getState());}Overridepublic
{System.out.println(Thread.currentThread().getName()
{e.printStackTrace();}System.out.println(Thread.currentThread().getName()
可以看到子线程在wait()后释放了锁并进入WAITTINT状态主线程获得锁后调用notify()这时候其中一个线程(栗子中是Thread-b也有可能是a)被唤醒并获取到锁继续执行到TERMINATED而另一个线程a一直WAITTINT状态
Thread-b);thread1.start();thread2.start();try
{e.printStackTrace();}System.out.println(哈哈哈哈);synchronized
{System.out.println(Thread.currentThread().getName()
获得了锁);System.out.println(notify前
thread1.getState());System.out.println(notify前
thread2.getState());lock.notifyAll();
{TimeUnit.MILLISECONDS.sleep(300);
{e.printStackTrace();}System.out.println(notify后
thread1.getState());System.out.println(notify后
thread2.getState());}Overridepublic
{System.out.println(Thread.currentThread().getName()
{e.printStackTrace();}System.out.println(Thread.currentThread().getName()
Thread.)).start();问题用户线程和守护线程有什么区别
语言中线程分为两类用户线程和守护线程默认情况下我们创建的线程或线程池都是用户线程所以用户线程也被称之为普通线程。
Thread也被称之为后台线程或服务线程守护线程是为用户线程服务的当程序中的用户线程全部执行结束之后守护线程也会跟随结束。
守护线程的角色就像“服务员”而用户线程的角色就像“顾客”当“顾客”全部走了之后全部执行结束那“服务员”守护线程也就没有了存在的意义所以当一个程序中的全部用户线程都结束执行之后那么无论守护线程是否还在工作都会随着用户线程一块结束整个程序也会随之结束运行。
小结默认情况下我们创建的线程或线程池都是用户线程守护线程是为用户线程服务的当一个程序中的所有用户线程都执行完成之后程序就会结束运行程序结束运行时不会管守护线程是否正在运行由此我们可以看出守护线程在
start()启动线程wait()实现线程等待notify()/notifyAll()唤醒线程sleep(xxx)带有结束时间让线程休眠的方法yield()交出
run方法的作用是描述线程具体要执行的任务start方法的作用是直正的去申请系统线程
run方法是一个类中的普通方法主动调用和调用普通方法一样会顺序执行一次;start调用方法后,start方法内部会调用Java
(封装了对系统底层的调用)真正的启动线程并执行run方法中的代码run
方法之所以不能被重复调用的原因是线程的状态是不可逆的Thread
方法都可以让线程进入休眠状态但二者是完全不同的它们的区别主要体现在以下几点。
初看代码好像没啥问题编译器也没报错然而当我们运行以上程序时就会发生如下错误
参考答案线程的生命周期指的是线程从创建到销毁的整个过程通常情况下线程的生命周期有以下
NEW初始化状态RUNNABLE可运行/运行状态BLOCKED阻塞状态WAITING无时限等待状态TIMED_WAITING有时限等待状态TERMINATED终止状态
种NEW初始化状态、RUNNABLE可运行/运行状态、BLOCKED阻塞状态、WAITING无时限等待状态、TIMED_WAITING有时限等待状态、TERMINATED终止状态。
线程生命周期的转换流程如下图所示
自定义中断标识符的停止方法此方法的缺点是不能及时响应中断请求使用
中断线程方法此方法是发送一个中断信号给线程它可以及时响应中断也是最推荐使用的方法最后是
方法虽然它也可以停止线程但此方法已经是过时的不建议使用的方法在
Pool把一个或多个线程通过统一的方式进行调度和重复使用的技术避免了因为线程过多而带来使用上的开销。
可重复使用已有线程避免对象创建、消亡和过度切换的性能开销。
避免创建大量同类线程所导致的资源过度竞争和内存溢出的问题。
支持更多功能比如延迟任务线程池newScheduledThreadPool和缓存线程池newCachedThreadPool等。
Executors.newFixedThreadPool创建一个固定大小的线程池可控制并发的线程数超出的线程会在队列中等待。
Executors.newCachedThreadPool创建一个可缓存的线程池若线程数超过处理所需缓存一段时间后会回收若线程数不够则新建线程。
Executors.newSingleThreadExecutor创建单个线程数的线程池它可以保证先进先出的执行顺序。
Executors.newScheduledThreadPool创建一个可以执行延迟任务的线程池。
Executors.newSingleThreadScheduledExecutor创建一个单线程的可以执行延迟任务的线程池。
Executors.newWorkStealingPool创建一个抢占式执行的线程池任务执行顺序不确定【JDK
添加】。
ThreadPoolExecutor手动创建线程池的方式它创建时最多可以设置
的方式来创建线程池这样的处理方式让写的读者更加明确线程池的运行规则规避资源耗尽的风险。
Integer.MAX_VALUE可能会堆积大量的请求从而导致
Integer.MAX_VALUE可能会创建大量的线程从而导致
threadFactory,RejectedExecutionHandler
NullPointerException();this.acc
:AccessController.getContext();this.corePoolSize
corePoolSize;this.maximumPoolSize
unit.toNanos(keepAliveTime);this.threadFactory
corePoolSize核心线程数。
maximumPoolSize最大线程数。
keepAliveTime空闲线程存活时间。
TimeUnit时间单位。
BlockingQueue线程池任务队列。
ThreadFactory创建线程的工厂。
RejectedExecutionHandler拒绝策略。
这就好比古代大户人家会长期雇佣一些“长工”来给他们干活这些人一般比较稳定无论这一年的活多活少这些人都不会被辞退都是长期生活在大户人家的。
最大线程数线程池允许创建的最大线程数量当线程池的任务队列满了之后可以创建的最大线程数。
这是古代大户人家最多可以雇佣的人数比如某个节日或大户人家有人过寿时因为活太多仅靠“长工”是完不成任务这时就会再招聘一些“短工”一起来干活这个最大线程数就是“长工”“短工”的总人数也就是招聘的人数不能超过
空闲线程存活时间当线程池中没有任务时会销毁一些线程销毁的线程数maximumPoolSize最大线程数-corePoolSize核心线程数。
还是以大户人家为例当大户人家比较忙的时候就会雇佣一些“短工”来干活但等干完活之后不忙了就会将这些“短工”辞退掉而
ArrayBlockingQueue一个由数组结构组成的有界阻塞队列。
LinkedBlockingQueue一个由链表结构组成的有界阻塞队列。
SynchronousQueue一个不存储元素的阻塞队列即直接提交给线程不保持它们。
PriorityBlockingQueue一个支持优先级排序的无界阻塞队列。
DelayQueue一个使用优先级队列实现的无界阻塞队列只有在延迟期满时才能从中提取元素。
LinkedTransferQueue一个由链表结构组成的无界阻塞队列。
与SynchronousQueue类似还含有非阻塞方法。
LinkedBlockingDeque一个由链表结构组成的双向阻塞队列。
线程工厂线程池创建线程时调用的工厂方法通过此方法可以设置线程的优先级、线程命名规则以及线程类型用户线程还是守护线程等。
设置线程优先级最大值10thread.setPriority(Thread.MAX_PRIORITY);//......return
LinkedBlockingQueue(),threadFactory);
使用自定义的线程工厂threadPoolExecutor.submit(new
Thread.currentThread();System.out.println(String.format(线程%s线程优先级%d,thread.getName(),
从上述执行结果可以看出自定义线程工厂起作用了线程的名称和线程的优先级都是通过线程工厂设置的。
拒绝策略当线程池的任务超出线程池队列可以存储的最大值之后执行的策略。
CallerRunsPolicy使用当前调用的线程来执行此任务。
DiscardOldestPolicy抛弃队列头部最旧的一个任务并执行当前任务。
corePoolSize核心线程数线程池正常情况下保持的线程数大户人家“长工”的数量。
maximumPoolSize最大线程数当线程池繁忙时最多可以拥有的线程数大户人家“长工”“短工”的总数量。
keepAliveTime空闲线程存活时间没有活之后“短工”可以生存的最大时间。
TimeUnit时间单位配合参数
的时间单位。
BlockingQueue线程池的任务队列用于保存线程池待执行任务的容器。
ThreadFactory线程工厂用于创建线程池中线程的工厂方法通过它可以设置线程的命名规则、优先级和线程类型。
RejectedExecutionHandler拒绝策略当任务量超过线程池可以保存的最大任务数时执行的策略。
false则把任务添加到任务队列中等待线程执行否则则判断当前线程数量是否超过最大线程数如果结果为
false则新建线程执行此任务否则将执行线程池的拒绝策略如下图所示
参考答案当任务过多且线程池的任务队列已满时此时就会执行线程池的拒绝策略线程池的拒绝策略默认有以下
AbortPolicy中止策略线程池会抛出异常并中止执行此任务CallerRunsPolicy把任务交给添加此任务的main线程来执行DiscardPolicy忽略此任务忽略最新的一个任务DiscardOldestPolicy忽略最早的任务最先加入队列的任务。
{System.out.println(当前任务被执行,执行时间:
Thread.currentThread().getName());try
执行自定义拒绝策略的相关操作System.out.println(我是自定义拒绝策略~);}});//
个任务threadPool.execute(runnable);threadPool.execute(runnable);threadPool.execute(runnable);threadPool.execute(runnable);
方法实现的我们可以在扩展方法中添加日志或者实现数据统计比如统计线程的执行时间如下代码所示
{Thread.currentThread().getName();});}}/***
(单位纳秒)localTime.set(sTime);System.out.println(String.format(%s
执行总时间System.out.println(String.format(%s
毫秒,Thread.currentThread().getName(),
1000000.0)));super.afterExecute(r,
种状态NEW初始化状态、RUNNABLE可运行/运行状态、BLOCKED阻塞状态、WAITING无时限等待状态、TIMED_WAITING有时限等待状态和
RUNNING运行状态线程池创建好之后就会进入此状态如果不手动调用关闭方法那么线程池在整个程序运行期间都是此状态。
SHUTDOWN关闭状态不再接受新任务提交但是会将已保存在任务队列中的任务处理完。
STOP停止状态不再接受新任务提交并且会中断当前正在执行的任务、放弃任务队列中已有的任务。
TIDYING整理状态所有的任务都执行完毕后也包括任务队列中的任务执行完当前线程池中的活动线程数降为
程序不会报错也不会立即终止线程它会等待线程池中的缓存任务执行完之后再退出执行了
java.lang.InterruptedException:
参考答案多线程的优点可以同时执行多个任务而缺点是多线程存在线程安全问题也就是线程的执行不符合预期结果的问题。
多线程抢占式执行多线程同时修改同一个变量非原子性操作原子性操作是指操作不能再被分割就叫原子性操作而导致线程安全性问题的一大因素就是非原子性操作内存可见性多个线程同时操作统一变量但因为某些原因导致变量已经被一个线程修改但另一个线程不可见从而导致了线程安全性问题指令重排序指令重排序是指
程序为了提高程序的执行速度所以会对一下操作进行合并和优化的操作比如某些操作本来的顺序是
B但这样重排之后就会导致程序的执行结果和预期的结果不相符的问题。
线程安全类通常是使用锁机制乐观锁或悲观锁来保证程序的正常执行的。
修饰普通方法时被修饰的方法被称为同步方法其作用范围是整个方法作用的对象是调用这个方法的对象。
修饰静态方法时其作用范围是整个程序这个锁对于所有调用这个锁的对象都是互斥的。
所谓的互斥指的是同一时间只能有一个线程能使用其他线程只能排队等待。
加锁是全局的也就是整个程序运行期间所有调用这个静态方法的对象都是互斥的而普通方法是针对对象级别的不同的对象对应着不同的锁。
参考答案监视器是一个概念或者说是一个机制它用来保障在任何时候只有一个线程能够执行指定区域的代码。
一个监视器像是一个建筑建筑里有一个特殊的房间这个房间同一时刻只能被一个线程所占有。
一个线程从进入该房间到离开该房间可以全程独占该房间的所有数据。
进入该建筑叫做进入监视器entering
严格意义来说监视器和锁的概念是不同的但很多地方也把二者相互指代。
//等待线程组成的双向循环链表_WaitSet是第一个节点_WaitSetLock
//_owner从该双向循环链表中唤醒线程结点_EntryList是第一个节点_SpinFreq
_count记录该线程获取锁的次数也就是前前后后这个线程一共获取此锁多少次。
_recursions锁的重入次数。
_ownerThe
ObjectMonitor监视器对象的线程_EntryListEntryList
监控集合存放的是处于阻塞状态的线程队列在多线程下竞争失败的线程会进入
会按照上述先后顺序依次升级我们把这个升级的过程称之为“锁膨胀”。
刚开始程序先是无锁状态当一个线程访问同步代码块并获取锁时会在对象头的
尝试获取锁如果获取成功则进入同步块执行代码否则会将锁的状态升级为轻量级锁轻量级锁之后会通过自旋来获取锁自旋执行一定次数之后还未成功获取到锁此时就会升级为重量级锁并且进入阻塞状态。
是自适应自旋锁。
自适应自旋锁是指线程自旋的次数不再是固定的值而是一个动态改变的值这个值会根据前一次自旋获取锁的状态来决定此次自旋的次数。
比如上一次通过自旋成功获取到了锁那么这次通过自旋也有可能会获取到锁所以这次自旋的次数就会增多一些而如果上一次通过自旋没有成功获取到锁那么这次自旋可能也获取不到锁所以为了避免资源的浪费就会少循环或者不循环以提高程序的执行效率。
简单来说如果线程自旋成功了则下次自旋的次数会增多如果失败下次自旋的次数会减少。
Synchronizer队列同步器实现的它默认是通过非公平锁实现的在它的内部有一个
AQSAbstractQueuedSynchronizer程序级别的
时一定要记得释放锁否则就会导致该锁一直被占用其他使用该锁的线程则会永久的等待下去所以我们在使用
中释放锁这样就可以保证锁一定会被释放加锁和释放锁的次数需相同加锁方法
的操作次数一一对应且不能出现一个锁被释放多次的情况因为这样就会导致程序报错lock
代码中这样会导致未加锁成功就执行了释放锁的操作从而导致程序执行异常。
{interruptLock.lock();//interruptLock.lockInterruptibly();
java.lang.InterruptedException}
可以保证多个线程在操作同一个变量时始终可以读取到最新的数据并且使用
可以禁止指令重排序从而关闭掉系统优化所给程序代码执行结果不一致的风险。
指令重排序的实现初衷是好的但是在多线程执行中如果执行了指令重排序可能会导致程序执行出错。
指令重排序最典型的一个问题就发生在单例模式中比如以下问题代码
Singleton();”这行代码看似只是一个创建对象的过程然而它的实际执行却分为以下
将会得到一个被实例化“一半”的对象从而导致程序执行出错这就是为什么要给私有变量添加
集合它在操作的时候会把全部数据复制出来对写操作加锁修改完之后再使用
可以使读线程很快的告知到数组被修改不会进行指令重排操作完成后就可以对其他线程可见了。
Lock指的是两个或两个以上的运算单元进程、线程或协程都在等待对方释放资源但没有一方提起释放资源从而造成了一种阻塞的现象就称为死锁。
互斥条件指运算单元进程、线程或协程对所分配到的资源具有排它性也就是说在一段时间内某个锁资源只能被一个运算单元所占用。
请求和保持条件指运算单元已经保持至少一个资源但又提出了新的资源请求而该资源已被其它运算单元占有此时请求运算单元阻塞但又对自己已获得的其它资源保持不放。
不可剥夺条件指运算单元已获得的资源在未使用完之前不能被剥夺。
环路等待条件指在发生死锁时必然存在运算单元和资源的环形链即运算单元正在等待另一个运算单元占用的资源而对方又在等待自己占用的资源从而造成环路等待的情况。
所谓的顺序锁指的是通过有顺序的获取锁从而避免产生环路等待条件从而解决死锁问题的。
B之后再尝试获取对方的锁从而造成了环路等待问题最后造成了死锁的问题。
的线程就可以成功获取到锁资源执行后续的代码这样就不会出现死锁的问题了。
{e.printStackTrace();}System.out.println(线程
{e.printStackTrace();}System.out.println(线程
轮询锁是通过打破“请求和保持条件”来避免造成死锁的它的实现思路简单来说就是通过轮询来尝试获取锁如果有一个锁获取失败则释放当前线程拥有的所有锁等待下一轮再尝试获取锁。
java.util.concurrent.locks.Lock;
java.util.concurrent.locks.ReentrantLock;public
{Thread.sleep(1000);System.out.println(线程
{Thread.sleep(1000);System.out.println(线程
悲观锁悲观锁认为对于同一个数据的并发操作一定是会发生修改的哪怕没有修改也会认为修改。
因此对于同一个数据的并发操作悲观锁采取加锁的形式。
悲观地认为不加锁的并发操作一定会出问题乐观锁乐观锁正好和悲观锁相反它获取数据的时候并不担心数据被修改每次获取数据的时候也不会加锁只是在更新数据的时候通过判断现有的数据是否和原数据一致来判断数据是否被其他线程操作如果没被其他线程修改则进行数据更新如果被其他线程修改则不进行数据更新公平锁公平锁是指多个线程按照申请锁的顺序来获取锁非公平锁非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序有可能后申请的线程比先申请的线程优先获取锁独占锁独占锁是指任何时候都只有一个线程能执行资源操作共享锁共享锁指定是可以同时被多个线程读取但只能被一个线程修改。
比如
就是共享锁的实现方式它允许一个线程进行写操作允许多个线程读操作可重入锁可重入锁指的是该线程获取了该锁之后可以无限次的进入该锁锁住的代码自旋锁自旋锁是指尝试获取锁的线程不会立即阻塞而是采用循环的方式去尝试获取锁这样的好处是减少线程上下文切换的消耗缺点是循环会消耗
元但因为程序的问题启动了两个线程线程一和线程二进行比对扣款线程一获取原本有
问题的方案加版本号来区分值是否有变动。
以磊哥取钱的例子为例如果加上版本号执行流程如下。
AtomicStampedReferenceString(name,
JUCjava.util.concurrent下的包但它们的功能有所不同。
CountDownLatch闭锁可以看作一个只能做减法的计数器可以让一个或多个线程等待执行。
以生活中的情景为例比如去医院体检通常人们会提前去医院排队但只有等到医生开始上班才能正式开始体检医生也要给所有人体检完才能下班这种情况就要使用
CyclicBarrier循环屏障通过它可以实现让一组线程等待满足某个条件后同时执行。
经典使用场景是公交发车为了简化理解我们这里定义每辆公交车只要上满
参考答案Semaphore信号量用于管理多线程中控制资源的访问与使用Semaphore
个车位门卫可以先放两辆车进去等有车出来之后再让后面的车进入它的实现代码如下
堵塞获取许可semaphore.acquire();System.out.println(Thread
Thread.currentThread().getName()
LocalDateTime.now());TimeUnit.SECONDS.sleep(2);//
参考答案线程安全的单例模式常见的实现方法有两种一种是天生线程安全的饿汉模式另一种是双重效验锁实现的懒汉单例模式具体实现如下。
饿汉模式也叫预加载模式它是在类加载时直接创建并初始化单例对象所以它并不存在线程安全的问题。
它是依靠
类机制在程序启动时只加载一次因此不存在线程安全问题它的实现代码如下
缺点类加载时就创建了对象创建之后如果没被使用就造成了资源浪费的情况。
懒汉模式和饿汉模式正好是相反的所谓的懒汉模式也就是懒加载延迟加载指的是它只有在第一次被使用时才会被初始化它的实现代码如下
来保证线程安全的从上述代码可以看出无论是饿汉模式还是懒汉模式它们的实现步骤都是一样的
对象这样创建出来的对象就不是单例对象了。
创建一个私有变量来保存单例对象。
提供一个公共的方法返回单例对象。
懒汉模式相比于饿汉模式来说不会造成资源的浪费但写法要复杂一些。
为每一个线程维护变量的副本把共享数据的可见范围限制在同一个线程之内因此
Key,Value因此当我们使用线程池来存储对象时因为线程池有很长的生命周期所以线程池会一直持有
value所以就会导致内存一直被占用从而导致内存溢出问题的发生。
Entry并且没有办法访问这些数据如果当前线程再迟迟不结束的话这些
{System.out.println(threadLocal.get());
用于实现同步机制是利用锁的机制使变量或代码块在某一时刻只能被一个线程访问是一种“以时间换空间”的方式而
为每一个线程提供了独立的变量副本这样每个线程的变量操作都是相互隔离的这是一种“以空间换时间”的方式。
{System.out.println(线程1);}}private
{System.out.println(我是线程2);}}public
{System.out.println(我是线程3通过内部类创建Thread
{System.out.println(我是线程4通过内部类创建Runable接口);}});Thread
System.out.println(我是线程5));t1.start();t2.start();t3.start();t4.start();t5.start();}}
{operator.addEvenSum(arr[i]);}});Thread
{operator.addOddSum(arr[i]);}});//
operator.result());System.out.println(总耗时
evenSum);System.out.println(奇数和
{//内部类使用外部的变量必须是final修饰System.out.println(n);}});}for(Thread
threads){//同时执行20个线程再等待所有线程执行完毕t.join();}System.out.println(OK);}请说明Thread类中run和start的区别
等待线程执行完成t1.join();t2.join();System.out.println(累加结果
打印cSystem.out.print(Thread.currentThread().getName()
打印bSystem.out.print(Thread.currentThread().getName()
打印aSystem.out.print(Thread.currentThread().getName()
cba的顺序打印ta.start();tb.start();tc.start();}
方法一通过count计数打印三个线程上同样的锁打印一个召唤所有锁如果不满足条件则wait等待锁自动解锁
创建三个线程并指定线程名每个线程名分别用A,B,C表示Thread
满足输出条件打印线程名每条线程打印的内容不同System.out.print(Thread.currentThread().getName());//
{e.printStackTrace();}}System.out.print(Thread.currentThread().getName());COUNTER;lock.notifyAll();}}},
换行打印System.out.println(Thread.currentThread().getName());COUNTER;lock.notifyAll();}}},
启动线程t1.start();t2.start();t3.start();}
{locker1.wait();}System.out.print(A);synchronized
{e.printStackTrace();}});Thread
{locker2.wait();}System.out.print(B);synchronized
{e.printStackTrace();}});Thread
{locker3.wait();}System.out.println(C);synchronized
{e.printStackTrace();}});t1.start();t2.start();t3.start();Thread.sleep(1000);//
声明并用volatile修饰保证在多线程环境下的有序性private
获取到锁后再检查一遍确认instance没有被实例化后去真正的实例化if
com.bitejiuyeke.exercises;import
这里使用while循环使每次唤醒后都重新去做一次判断while
System.out.println(队列满了暂停生产...);this.wait();}//
这里使用while循环使每次唤醒后都重新去做一次判断while
System.out.println(队列空了暂停消费...);this.wait();}//
blockingQueue.take();System.out.println(消费了
random.nextInt(10000);blockingQueue.put(num);System.out.println(生产了:
通过PriorityBlockingQueue来组织任务private
每增加一个任务就唤醒一次worker看有没有要执行的任务synchronized
{System.out.println(时间到了我开始执行...);}};timer.schedule(runnable,
当前线程池中有若干个任务都持有上述队列并从中获取任务private
循环获取任务队列中的任务如果队列为空就阻塞非空就获取执行Runnable
把线程添加到List中进行组织管理workers.add(worker);}}//
创建线程池com.bitejiuyeke.exercises.MyThreadPool
com.bitejiuyeke.exercises.MyThreadPool(10);//
使用ThreadPoolExecutor创建一个忽略最新任务的线程池
使用ThreadPoolExecutor创建一个忽略最新任务的线程池创建规则*
ThreadPoolExecutor.DiscardPolicy());
{System.out.println(Thread.currentThread().getName()
把1000拆成10组分别用10个线程去执行最后汇总结果private
1000拆成果10组数据分别用10个线程去执行最后统计结果int
Executors.newFixedThreadPool(5);//
提交到线程池并保存返回的FutuerFutureInteger
停止线程池pool.shutdown();System.out.println(methods_02结果为
创建FutureTask并绑定Callable任务FutureTaskInteger
FutureTask也实现了Runnable可以做为入参Thread
futureTask.get();System.out.println(methods_01结果为
原子类里要通过这个方法来完成counter.getAndIncrement();}});Thread
原子类里要通过这个方法来完成counter.getAndIncrement();}});//
CountDownLatch(playerCount);static
准备就绪计数减1System.out.println(Thread.currentThread().getName()
等待开始比赛start.await();System.out.println(Thread.currentThread().getName()
10;Thread.sleep(time);System.out.println(Thread.currentThread().getName()
等待参赛选手准备就绪System.out.println(所有参赛选手准备就绪比赛开始...);//
比赛中等待选手到达终点finished.await();System.out.println(比赛结束.);}
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback