详细博文链接:
https://blog.csdn.net/m0_55746113/article/details/135691683
️知识点梳理
1、程序、进程和线程的区分:
一组指令的集合
。即指一段静态的代码。“栈管运行”:Java里面通过方法的调用体现运行的过程,每一个方法的调用都对应一个栈帧,要入栈运行。
“堆管存储”:对象的创建、数组的创建,这些数据都放在堆里面。
运行的时候,最小的调度单位就是“线程”,每个线程有一个栈。
2、线程调度策略
分时调度
:所有线程轮流使用
CPU 的使用权,并且平均分配每个线程占用 CPU 的时间。抢占式调度
:让优先级高
的线程以较大的概率
优先使用 CPU。如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。3、了解
详细博文链接:
线程的创建方式一:继承Thread类
https://blog.csdn.net/m0_55746113/article/details/135708814
线程的创建方式二:实现Runnable接口
https://blog.csdn.net/m0_55746113/article/details/135840102
️知识点梳理
1、线程的创建方式一:继承Thread类
1.1 步骤:
① 创建一个继承于Thread类的子类
② 重写Thread类的run() —>将此线程要执行的操作,声明在此方法体中
③ 创建当前Thread的子类的对象
④ 通过对象调用start()
: 1.启动线程 2.调用当前线程的run()
start()是父类Thread的方法。
1.2 例题:创建一个分线程1,用于遍历100以内的偶数
【拓展】 再创建一个分线程2,用于遍历100以内的偶数
2、线程的创建方式二:实现Runnable接口
2.1 步骤:
① 创建一个实现Runnable接口的类
② 实现接口中的run() -->将此线程要执行的操作,声明在此方法体中
③ 创建当前实现类的对象
④ 将此对象作为参数传递到Thread类的构造器中,创建Thread类的实例
⑤ Thread类的实例调用start():1.启动线程 2.调用当前线程的run()
2.2 例题:创建分线程遍历100以内的偶数
3、对比两种方式?
public class Thread implements Runnable
(代理模式)详细博文链接:
https://blog.csdn.net/m0_55746113/article/details/135881677
️知识点梳理
1、线程中的构造器
public Thread()
:分配一个新的线程对象。public Thread(String name)
:分配一个指定名字的新的线程对象。public Thread(Runnable target)
:指定创建线程的目标对象,它实现了Runnable接口中的run方法。public Thread(Runnable target,String name)
:分配一个带有指定目标新的线程对象并指定名字。2、线程中的常用方法
start()
:①启动线程 ②调用线程的run()。run()
:将线程要执行的操作,声明在run()中。currentThread()
:获取当前执行代码对应的线程。getName()
: 获取线程名。setName()
: 设置线程名。sleep(long millis)
:静态方法,调用时,可以使得当前线程睡眠指定的毫秒数。yield()
:静态方法,一旦执行此方法,就释放CPU的执行权。join()
: 在线程a中通过线程b调用join(),意味着线程a进入阻塞状态,直到线程b执行结束,线程a才结束阻塞状态,继续执行。isAlive()
:判断当前线程是否存活。过时方法:
stop()
:强行结束一个线程的执行,直接进入死亡状态。不建议使用。void suspend()
/ void resume()
:可能造成死锁,所以也不建议使用。3、线程的优先级:
getPriority()
:获取线程的优先级。setPriority()
:设置线程的优先级。范围[1,10]。Thread类内部声明的三个常量:
MAX_PRIORITY(10)
:最高优先级MIN _PRIORITY (1)
:最低优先级NORM_PRIORITY (5)
:普通优先级,默认情况下main线程具有普通优先级。详细博文链接:
https://blog.csdn.net/m0_55746113/article/details/135885857
️知识点梳理
JDK1.5之前:(5种状态)
JDK1.5之后:(6种状态)
Thread类中定义了一个内部类State
:
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
详细博文链接:
https://blog.csdn.net/m0_55746113/article/details/135918247
️知识点梳理
1、多线程卖票,出现的问题:出现了重票和错票。
2、什么原因导致的?
线程1操作ticket
的过程中,尚未结束的情况下,其他线程也参与进来,对ticket
进行操作。
3、如何解决?
必须保证一个线程a在操作ticket的过程中,其它线程必须等待,直到线程a操作ticket结束以后,其它线程才可以进来继续操作ticket。
4、Java是如何解决线程的安全问题的?使用线程的同步机制。
同步代码块
② 同步方法
Lock接口及其实现类
。(保证多个线程共用同一个Lock的实例)在实现Runnable接口的方式中,同步监视器可以考虑使用:this。
在继承Thread类的方式中,同步监视器要慎用this,可以考虑使用:当前类.class。
非静态的同步方法,默认同步监视器是this
静态的同步方法,默认同步监视器是当前类本身。
①方式1:同步代码块
synchronized(同步监视器){
//需要被同步的代码
}
说明:
注意:
this
。当前类.class
。②方式2:同步方法
说明:
5、synchronized
详细博文链接:
https://blog.csdn.net/m0_55746113/article/details/135971616
️知识点梳理
详细博文链接:
https://blog.csdn.net/m0_55746113/article/details/135990968
️知识点梳理
1、如何看待死锁?
不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。
我们编写程序时,要避免出现死锁。
2、诱发死锁的原因?
以上4个条件,同时出现就会触发死锁。
3、如何避免死锁?
针对条件1:互斥条件基本上无法被破坏。因为线程需要通过互斥解决安全问题。
针对条件2:可以考虑一次性申请所有所需的资源,这样就不存在等待的问题。
针对条件3:占用部分资源的线程在进一步申请其他资源时,如果申请不到,就主动释放掉已经占用的资源。
针对条件4:可以将资源改为线性顺序。申请资源时,先申请序号较小的,这样避免循环等待问题。
详细博文链接:
https://blog.csdn.net/m0_55746113/article/details/135990968
️知识点梳理
除了使用synchronized同步机制
处理线程安全问题之外,还可以使用jdk5.0提供的Lock锁
的方式。
1、步骤
步骤1:创建Lock的实例,需要确保多个线程共用同一个Lock实例。
需要考虑将此对象声明为static final
。
步骤2.:执行lock()
方法,锁定对共享资源的调用。
步骤3.:unlock()
的调用,释放对共享数据的锁定。
2、面试题
synchronized
同步的方式 与Lock
的对比 ?
synchronized
不管是同步代码块还是同步方法,都需要在结束一对{}
之后,释放对同步监视器的调用。
Lock
是通过两个方法控制需要被同步的代码,更灵活一些。
Lock
作为接口,提供了多种实现类,适合更多更复杂的场景,效率更高。
详细博文链接:
https://blog.csdn.net/m0_55746113/article/details/135998682
️知识点梳理
1、线程间通信的理解
当我们需要多个线程
来共同完成一件任务,并且我们希望他们有规律的执行,那么多线程之间需要一些通信机制,可以协调它们的工作,以此实现多线程共同操作一份数据。
2、涉及到三个方法的使用
wait()
:线程一旦执行此方法,就进入等待状态。同时,会释放对同步监视器的调用。
notify()
:一旦执行此方法,就会唤醒被wait()
的线程中优先级最高的那一个线程。(如果被wait()
的多个线程的优先级相同,则随机唤醒一个)。被唤醒的线程从当初被wait的位置继续执行。
notifyAll()
:一旦执行此方法,就会唤醒所有被wait的线程。
3、️注意点
IllegalMonitorStateException
异常。4、案例
案例1:使用两个线程打印 1-100。线程1, 线程2 交替打印。
案例2:生产者&消费者
生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品。
店员一次只能持有固定数量的产品(比如:20),如果生产者试图生产更多的产品,店员会叫生产者停一下,如果店中有空位放产品了再通知生产者继续生产;如果店中没有产品了,店员会告诉消费者等一下,如果店中有产品了再通知消费者来取走产品。
5、wait()
和 sleep()
的区别?
wait()
:声明在Object类中。sleep()
:声明在Thread类中,静态的。wait()
:只能使用在同步代码块或同步方法中。sleep()
:可以在任何需要使用的场景。wait()
:一旦执行,会释放同步监视器。sleep()
:一旦执行,不会释放同步监视器。wait()
:到达指定时间自动结束阻塞 或 通过被notify唤醒,结束阻塞。sleep()
:到达指定时间自动结束阻塞。详细博文链接:
https://blog.csdn.net/m0_55746113/article/details/136008885
️知识点梳理
1、创建多线程的方式三:实现Callable (jdk5.0新增的)
与之前的方式的对比:与Runnable方式的对比的好处
call()
可以有返回值,更灵活。call()
可以使用throws的方式处理异常,更灵活。Callable
使用了泛型参数,可以指明具体的call()
的返回值类型,更灵活。有缺点吗?如果在主线程中需要获取分线程call()
的返回值,则此时的主线程是阻塞状态的。
2、创建多线程的方式四:使用线程池
此方式的好处:
题目描述
什么是线程(*云网络)
答
进程可进一步细化为线程,是程序内部的一条执行路径。
线程作为CPU调度和执行的最小单位。
题目描述
线程和进程有什么区别(团、腾、云网络、神**岳、言有物、直*科技)
答
进程:对应一个运行中的程序。
线程:运行中的进程的一条或多条执行路径。
题目描述
多线程使用场景(嘉*医疗)
答
题目描述
如何在Java中出实现多线程?(阿校招、当置业、鸿网络、奥医药、科软、慧、上海驿软件、海科)
类似问题:
> 创建多线程用Runnable还是Thread(北京中*瑞飞)
> 多线程有几种实现方法,都是什么?(锐*(上海)企业管理咨询)
答
四种方式:
题目描述
Thread类中的start()和run()有什么区别?(北京中油**、爱信、神泰岳、直*科技,软国际,上海学网络)
答
start()
:① 开启线程 ② 调用线程的run()
run()
:调用run()
方法,线程可能根本没有开启,还在原有的线程当中,只是当前对象调用方法而已。
题目描述
启动一个线程是用run()还是start()?(*度)
答
start()
。
题目描述
Java中Runnable和Callable有什么不同?(平金服、银数据、好在、亿征信、花儿**网络)
答
与之前的方式的对比:与Runnable方式的对比的好处
call()
可以有返回值,更灵活。call()
可以使用throws的方式处理异常,更灵活。Callable
使用了泛型参数,可以指明具体的call()
的返回值类型,更灵活。有缺点吗?如果在主线程中需要获取分线程call()
的返回值,则此时的主线程是阻塞状态的。
题目描述
什么是线程池,为什么要使用它?(上海明*物联网科技)
答
此方式的好处:
题目描述
sleep() 和 yield()区别?(神*泰岳)
答
sleep()
:一旦调用,就进入“阻塞”(或TIMED_WAITING状态)。
yield()
:释放cpu的执行权,处在RUNNABLE的状态。
题目描述
线程创建的中的方法、属性情况?(招通**、数*互融)
答
①线程中的构造器
public Thread()
:分配一个新的线程对象。public Thread(String name)
:分配一个指定名字的新的线程对象。public Thread(Runnable target)
:指定创建线程的目标对象,它实现了Runnable接口中的run方法。public Thread(Runnable target,String name)
:分配一个带有指定目标新的线程对象并指定名字。②线程中的常用方法
start()
:①启动线程 ②调用线程的run()。run()
:将线程要执行的操作,声明在run()中。currentThread()
:获取当前执行代码对应的线程。getName()
: 获取线程名。setName()
: 设置线程名。sleep(long millis)
:静态方法,调用时,可以使得当前线程睡眠指定的毫秒数。yield()
:静态方法,一旦执行此方法,就释放CPU的执行权。join()
: 在线程a中通过线程b调用join(),意味着线程a进入阻塞状态,直到线程b执行结束,线程a才结束阻塞状态,继续执行。isAlive()
:判断当前线程是否存活。题目描述
线程的生命周期?(中国**电子商务中心、科软、慧)
答
JDK1.5之前:(5种状态)
JDK1.5之后:(6种状态)
题目描述
线程的基本状态以及状态之间的关系?(直*科技)
类似问题:
> 线程有哪些状态?如何让线程进入阻塞?(华*中*,*兴)
> 线程有几个状态,就绪和阻塞有什么不同。(美*)
> Java的线程都有哪几种状态(字*跳动、*东、*手)
答
题目描述
stop()
和suspend()
方法为何不推荐使用?(上海驿*软件)
答
stop()
:一旦执行,线程就结束了,导致run()有未执行结束的代码。stop()会导致释放同步监视器,导致线程安全问题。
suspend()
:与resume()搭配使用,导致死锁。
题目描述
Java 线程优先级是怎么定义的?(软*动力)
答
线程的优先级:
getPriority()
:获取线程的优先级。setPriority()
:设置线程的优先级。范围[1,10]。Thread类内部声明的三个常量:
MAX_PRIORITY(10)
:最高优先级MIN _PRIORITY (1)
:最低优先级NORM_PRIORITY (5)
:普通优先级,默认情况下main线程具有普通优先级。题目描述
你如何理解线程安全的?线程安全问题是如何造成的?(*软国际)
类似问题:
> 线程安全说一下?(奥*医药)
> 对线程安全的理解(*度校招)
> 什么是线程安全?(银*数据)
答
在实现Runnable接口的方式中,同步监视器可以考虑使用:this。
在继承Thread类的方式中,同步监视器要慎用this,可以考虑使用:当前类.class。
非静态的同步方法,默认同步监视器是this
静态的同步方法,默认同步监视器是当前类本身。
题目描述
多线程共用一个数据变量需要注意什么?(史*夫软件)
答
线程安全问题。
题目描述
多线程保证线程安全一般有几种方式?(来科技、北京信天*)
类似问题:
> 如何解决其线程安全问题,并且说明为什么这样子去解决?(北京联合**)
> 请说出你所知道的线程同步的方法。(天*伟业)
> 哪些方法实现线程安全?(阿*)
> 同步有几种实现方法,都是什么? (锐*企业管理咨询)
> 你在实际编码过程中如何避免线程安全问题?(*软国际)
> 如何让线程同步?(*手)
> 多线程下有什么同步措施(阿*校招)
> 同步有几种实现方法,都是什么?(海*科)
答
题目描述
用什么关键字修饰同步方法?(上海驿*软件)
答
synchronized
题目描述
synchronized加在静态方法和普通方法区别(来*科技)
答
同步监视器不同。
this
题目描述
Java中synchronized和ReentrantLock有什么不同(三*重工)
类似问题:
> 多线程安全机制中 synchronized和lock的区别(中*国际、*美、鸿*网络)
> 怎么实现线程安全,各个实现方法有什么区别?(美*、字*跳动)
> synchronized 和 lock 区别(阿*、*壳)
答
synchronized
不管是同步代码块还是同步方法,都需要在结束一对{}
之后,释放对同步监视器的调用。
Lock
是通过两个方法控制需要被同步的代码,更灵活一些。
Lock
作为接口,提供了多种实现类,适合更多更复杂的场景,效率更高。
题目描述
当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法?(鸿*网络)
答
需要看其他方法是否使用synchronized
修饰,同步监视器的this
是否是同一个。
只有当使用了synchronized
,且this
是同一个的情况下,就不能访问了。
题目描述
线程同步与阻塞的关系?同步一定阻塞吗?阻塞一定同步吗?(阿校招、西安创佳*)
答
同步一定阻塞;
阻塞不一定同步。
题目描述
什么是死锁,产生死锁的原因及必要条件(腾*、阿*)
答
①如何看待死锁?
不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。
我们编写程序时,要避免出现死锁。
②诱发死锁的原因?
以上4个条件,同时出现就会触发死锁。
题目描述
如何避免死锁?(阿*、北京*蓝、*手)
答
针对条件1:互斥条件基本上无法被破坏。因为线程需要通过互斥解决安全问题。
针对条件2:可以考虑一次性申请所有所需的资源,这样就不存在等待的问题。
针对条件3:占用部分资源的线程在进一步申请其他资源时,如果申请不到,就主动释放掉已经占用的资源。
针对条件4:可以将资源改为线性顺序。申请资源时,先申请序号较小的,这样避免循环等待问题。
题目描述
Java中notify()和notifyAll()有什么区别(汇*天下)
答
notify()
:一旦执行此方法,就会唤醒被wait()的线程中优先级最高的那一个线程。(如果被wait()的多个线程的优先级相同,则随机唤醒一个)。被唤醒的线程从当初被wait的位置继续执行。
notifyAll()
:一旦执行此方法,就会唤醒所有被wait的线程。
题目描述
为什么wait()和notify()方法要在同步块中调用(北京*智)
答
因为调用者必须是同步监视器。
题目描述
多线程:生产者,消费者代码(同步、wait、notifly编程)(猫*娱乐)
类似问题:
> 如何写代码来解决生产者消费者问题(上海明*物联网)
> 多线程中生产者和消费者如何保证同步(*为)
> 消费者生产者,写写伪代码(字*)
答
package yuyi04.Communication;
/**
* ClassName: ProducterConsumerTest
* Package: yuyi04.Communication
* Description:
*
* @Author 雨翼轻尘
* @Create 2024/2/2 0002 17:15
*/
public class ProducerConsumerTest {
public static void main(String[] args) {
Clerk clerk=new Clerk(); //clerk本身就充当了共享数据
Producer pro1=new Producer(clerk); //创建生产者对象pro1
Consumer con1=new Consumer(clerk); //创建消费者对象con1
Consumer con2=new Consumer(clerk); //创建消费者对象con1
pro1.setName("生产者1");
con1.setName("消费者1");
con2.setName("消费者2");
pro1.start();
con1.start();
con2.start();
}
}
class Clerk{ //店员
private int productNum=0; //产品数量
//增加产品数量的方法(对属性的修改通常用方法来体现)
public synchronized void addProduct(){ //wait()需要在同步代码块或同步方法中去使用
if(productNum>=20){ //产品数量大于等于20
//等待
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
productNum++; //让产品加一
System.out.println(Thread.currentThread().getName()+"生产了第"+productNum+"个产品");
//生产了一个就可以唤醒消费者
notifyAll();
}
}
//减少产品数量的方法
public synchronized void minusProduct(){ //wait()需要在同步代码块或同步方法中去使用
if(productNum<=0){ //产品数量小于等于0
//等待
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
System.out.println(Thread.currentThread().getName()+"消费了第"+productNum+"个产品");
productNum--; //让产品减一
//只要消费者消费了一个,就可以唤醒生产者继续生产
notifyAll();
}
}
}
class Producer extends Thread{ //生产者
private Clerk clerk;
//构造器
public Producer(Clerk clerk){
this.clerk=clerk;
}
@Override
public void run() {
while(true){
System.out.println("生产者开始生产产品啦...");
try {
Thread.sleep(50); //50ms生产一个产品
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.addProduct();
}
}
}
class Consumer extends Thread{ //消费者
private Clerk clerk;
public Consumer(Clerk clerk){ //通过构造器给clerk赋值
this.clerk=clerk;
}
@Override
public void run() {
while(true){
System.out.println("消费者开始消费产品喽...");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.minusProduct();
}
}
}
题目描述
wait()和sleep()有什么区别?调用这两个函数后,线程状态分别作何改变?(字*、*东)
类似问题:
> 线程中sleep()和wait()有什么区别?(外派*度)
> Java线程阻塞调用 wait 函数和 sleep 区别和联系(阿*)
> wait和sleep的区别,他们两个谁会释放锁(软*动力、*创)
答
wait()
:声明在Object类中。sleep()
:声明在Thread类中,静态的。wait()
:只能使用在同步代码块或同步方法中。sleep()
:可以在任何需要使用的场景。wait()
:一旦执行,会释放同步监视器。sleep()
:一旦执行,不会释放同步监视器。wait()
:到达指定时间自动结束阻塞 或 通过被notify唤醒,结束阻塞。sleep()
:到达指定时间自动结束阻塞。题目描述
手写一个单例模式(Singleton),还要安全的(通快递、君科技)
答
①饿汉式
②安全的懒汉式:
package yuyi04.singleton;
/**
* ClassName: BankTest
* Package: yuyi04.singleton
* Description:
* 实现线程安全的懒汉式
*
* @Author 雨翼轻尘
* @Create 2024/1/31 0031 10:39
*/
public class BankTest {
static Bank b1=null; //为了在main方法中调用的时候,不使用对象来调用(方便一点),这里就设置成静态的
static Bank b2=null;
public static void main(String[] args) {
Thread t1=new Thread(){
@Override
public void run() {
b1=Bank.getInstance(); //将方法返回的对象赋给b1
}
};
Thread t2=new Thread(){
@Override
public void run() {
b2=Bank.getInstance();
}
};
t1.start();
t2.start();
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(b1);
System.out.println(b2);
System.out.println(b1==b2);
}
}
class Bank {
//私有化构造器
private Bank() { //这里不提供实例变量了
}
private static volatile Bank instance = null;
//实现线程安全的方式1
/*public static synchronized Bank getInstance() { //同步监视器是Bank.class
if (instance == null) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
instance = new Bank();
}
return instance;
}*/
//实现线程安全的方式2
/*public static Bank getInstance() { //同步监视器是Bank.class
synchronized (Bank.class) {
if (instance == null) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
instance = new Bank();
}
}
return instance;
}*/
//实现线程安全的方式3:相较于方式1和方式2来说,效率更高;为了避免指令重排,需要将instance声明为volatile
public static Bank getInstance() { //同步监视器是Bank.class
if (instance == null) {
synchronized (Bank.class) {
if (instance == null) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
instance = new Bank();
}
}
}
return instance;
}
}
③内部类
题目描述
手写一个懒汉式的单例模式&解决其线程安全问题,并且说明为什么这样子去解决(5*)
类似问题:
> 手写一个懒汉式的单例模式(北京联合**)
答
同上。