并发编程相关

并发编程的优缺点为什么要使用并发编程?
答:充分利用多核CPU,对业务拆解,提升系统的并发能力和性能,提高执行效率。缺点:内存泄漏、上下文切换、线程安全、死锁。
并发编程三要素是什么?
答:原子性、可见性(一个线程对共享变量修改后,其他线程能够立刻看到)、有序性(处理器对指令进行重排序)
解决办法:
synchronized关键字:被修饰的内容是同步的,任意时刻只有一个线程能执行该方法/代码块。不仅可以确保可见性,还可以确保原子性,开销更大,性能上差一点。
volatile关键字:声明变量是易变的,直接与主内存进行交互而不是与线程的本地缓存交互,确保多线程情况下,某个线程修改之后其他线程可见。保证可见性,不保证原子性,会阻止指令重排。

并发和并行
并发是指不同线程在cpu上按时间片交替执行,逻辑上看是连续的,并行是指多个线程在不同的CPU上可以同时在一个时间段内执行。
什么是多线程?优劣?
答:是指一个程序内有多个执行流,即一个程序中可以同时运行多个线程来执行不同的任务;
优:提高CPU利用率,提升程序执行效率。
劣:线程也是程序,需要占用内存,线程越多占用内存越多;线程需要调度和管理;线程对共享资源的访问会互相影响。
线程和进程区别 什么是线程和进程?
进程是一个程序实例,每个进程有自己独立的空间,一个进程不能直接访问另一个进程的资源。线程是轻量级进程,被包含在进程中,不能独立执行,一个进程中可以有多个线程,线程之间有共享的资源。进程是资源分配的基本单位,线程是CPU调度的基本单位。
什么是上下文切换?
答:多线程编程中一般线程个数大于CPU的核的个数,但是一个CPU在任意时刻只能被一个线程使用,CPU采用的策略是为每个线程分配时间片并轮转。当一个线程分配的时间片用完之后,需要切换到另一个线程执行,在切换之前需要保存该线程当前的运行状态,以便下次切换到该线程时恢复任务。
一般需要保存的内容包括:程序计数器、栈和堆状态(存储的数据状态)。
什么是线程死锁?如何避免线程死锁?
定义:线程死锁涉及两个或两个以上的线程,其中每个线程执行下去所需要的资源都被另一进程持有,导致线程之间陷入互相等待的过程,如果没有外部干预,会一直等下去。
必要条件:互斥;持有并等待;不可剥夺;循环等待。
解决:获得所有资源再执行;如果过了一段时间仍然申请不到资源的话,主动释放资源;按顺序申请资源,释放资源的时候反序释放,破坏循环等待条件。
创建线程有哪几种方式?
继承thread类:创建一个新的类继承thread,重写它的run方法,创建这个新类的实例并且调用实例start这个线程。
实现runnable接口:创建一个新的类实现runnable接口,实现run方法,创建thread的类并传入runnable对象,然后调用start来开启线程。

实现callable接口:创建myCallable类实现callable接口;将myCallable对象作为参数传入FutureTask新建对象,将FutureTask对象作为参数传给thread新建线程,通过start开启线程。可以通过FutureTask对象的get()方法来获取线程执行完毕后的返回值。

FutureTask是一个异步运算的任务,可以传入一个callable的具体实现类,可以对这个异步任务进行获取、取消等。

实现 Runnable 接口和 Callable 接口的区别

答:unnnable没有返回值,callable有返回值,并且经常跟futuretask结合,由futuretask获取返回结果。所以如果需要获取返回结果的不应该使用runnable而应该使用callable。

使用 Executors 工具类创建线程池

答:提供方法创建多种类型的线程池,如单线程池、固定大小的线程池、缓存线程池等。

线程的 run() start() 有什么区别?为什么不能直接调用run方法?
答:start用于启动线程,而run是线程体的方法,是线程执行时的具体代码。run可以重复调用,而start只能调用一次,调用start之后无需等待run方法执行完毕,可以继续执行其他的代码,此时是就绪状态,然后通过此thread类调用run方法来执行。
调用start之后,会启动一个线程并进入就绪状态,分配时间片之后可以自动执行run方法,这样才是真正实现了多线程。如果直接执行run方法,相当于main方法中的一个普通方法,不在线程中执行,无法实现多线程。
线程生命周期的五种状态:创建、就绪、运行、阻塞、死亡。
sleep() wait() 有什么区别?调用wait方法为什么要用循环?
答:都可以暂停线程的执行。sleep()来自thread类,wait来自object类;sleep不会释放锁,wait会释放锁;sleep在时间到期后会自动苏醒,wait不会,需要别的线程调用同一个对象上的notify或者notifyall来唤醒。
主要是为了防止假唤醒,即使线程被notify唤醒了,也要判断是不是所有条件都满足了,再执行。
notify和notifyall之间的区别是什么?
触发线程数量:前者只会唤醒在该对象调用wait方法的一个线程,如果多个线程在等待,那么唤醒哪个线程是不确定的,没办法控制哪个线程被唤醒;notifyall会唤醒该对象上调用wait方法的所有线程。
效率:notify只唤醒一个线程,通常比notifyall更加高效,因为唤醒多个线程可能会触发更严重的问题。
安全性与风险:notify可能会引起线程饥饿或线程死锁;notifyall虽然某些情况下效率比较低,但是更安全,会确保所有等待的线程都会得到通知。
什么是线程同步和线程互斥?实现方法?
线程同步:线程对共享数据做出操作时,应该成为一个原子操作,不允许其他线程打扰。
线程互斥:对于进程中的共享资源,在任何时刻只能有一个进程访问。
实现方法:synchronized关键字和volatile关键字。
如何理解乐观锁和悲观锁?
答:乐观锁认为对数据修改时不会发生冲突,所以不会提前加锁,只有在实际更新时才会检查这个过程中是否被其他事务修改,常用的方法是版本标记。而悲观锁认为多个事务在访问数据时会发生冲突,所以尝试更新数据前会先进行锁定,确保其他事务无法访问。
Java 线程池有哪些参数?阻塞队列有几种?拒绝策略有几种?新线程添加的流程?
答:核心线程数,最大线程数、核心线程闲置时间、unit:参数地时间单位、任务队列、线程工厂、拒绝策略。
阻塞队列种类:基于数组的有界阻塞队列;基于链表的无界阻塞队列;支持优先级的无界阻塞队列
拒绝策略
直接抛出异常;
由调用线程处理该任务,不丢弃也不抛出异常,将任务退回给调用者,让调用者自己执行;
抛弃任务队列最前的任务,然后重新尝试执行新任务;
直接丢弃任务,不做处理。
新线程添加流程:如果没达到核心线程数,就创建一个新的核心线程执行该任务,如果达到了核心线程数,任务队列还没满,添加到任务队列中,如果任务队列满了,且当前线程数小于最大线程数,创建非核心线程来执行任务;如果队列已满,且达到最大线程数,执行拒绝策略。
Lock体系

AQS相关的不了解,先不看了。

ThreadLocal是什么?有什么使用场景?造成内存泄露的原因?有什么解决方案吗?

定义:是一个用于为线程创建本地变量副本的类,它允许每个线程创建一个变量的副本,仅线程内部可见,而不是线程之间共享,是一种线程安全的方式。当多个线程执行同一段代码,某些变量又需要进行线程隔离时,就可以使用threadLocal。

具体使用场景有:为每个线程分配一个JDBC的connection,这样每个线程在各自的连接上操作数据库,确保其他线程不会中断别的线程的操作。对于web应用,threadLocal可以用来存储session信息。

内存泄漏的原因:threadLocal设计的目的是为线程创建一个单独的变量副本,且这个变量副本是利用一个threadLocalMap对象来实现的,每个线程可以访问map对象的value。但是threadLocalMap对象的键是弱引用,它的value是强引用,在有些情况下,线程的生命周期比threadlocal的生命周期长很多,当threadLocal对象没有被外部强引用时,回收内存的时候,弱引用就会被回收掉,这样就会出现键为null的entry。如果不采取措施的话,永远不会被回收,有可能导致内存泄露。

解决方案每次使用完ThreadLocal,都调用它的remove()方法,显示地清除数据。

线程池有什么优点?

降低资源消耗:重用资源,避免线程频繁创建跟销毁。

提高响应速度:任务到达时,不需要等待线程创建,可以直接执行。

线程是稀缺资源,如果一直创建,不仅会消耗资源还会损害系统稳定性。

threadPoolExcutor如何创建线程池?

1、定义任务:通常是通过实现runnable接口的类来定义,会重写run方法。

2、创建线程池:使用ThreadPoolExecutor工具类的构造函数或工具类来定义线程池的变量。

3、通过循环提交任务,新建myrunnable的实例,提交给ThreadPoolExecutor实例对象。

4、关闭线程池,用完之后要shutdown。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值