1.多线程回顾
1.1 几个概念
- 程序(Program):为完成特定任务,用某种语言编写的
一组指令的集合
。即指一段静态的代码
,静态对象。 进程(Process)
:程序的一次执行过程,或是正在内存中运行的应用程序。程序是静态的,进程是动态的,进程作为操作系统调度和分配资源的最小单位
(亦是系统运行程序的基本单位)线程(Thread)
:进程可进一步细化成线程,它是程序内部的一条执行路径
。线程是CPU调度和执行的最小单位
(栈管理运行,堆管理存储)
分时调度
:所有线程轮流使用
CPU的使用权,并且平均分配每个线程占用 CPU的时间。
抢占式调度
:让优先级高
的线程以较大的概率
优先使用 CPU。如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。
1.2 如何创建线程(重点)如何创建线程(重点)
方式1:继承 Thread类;
方式2:实现 Runnable接口;
方式3:实现 Callable接口;(JDK5.0新增)
方式4:使用线程池(JDK5.0新增)
1.3 Thread类的常用方法、线程的生命周期
- 线程中的构造器
//分配一个新的线程对象
public Thread();
//分配一个指定名字的新线程对象
public Thread(String name);
//指定创建线程的目标对象,它实现了 Runnable接口中的 Run()方法
public Thread(Runnable target);
//分配一个带有指定目标的新线程对象,并指定名字
public Thread(Runnable target, String name);
- 线程中的常用方法
public void run();//此线程要执行的任务,需要在此处定义代码。
public void start();//开始执行此线程;Java虚拟机调用此线程的 run()方法
public String getName();//获取当前线程的名称
public void setName(String name);//设置当前线程的名称
public static native Thread currentThread();//返回当前正在执行的线程对象的引用。在Thread子类中就是 this,通常用于主线程和 Runnable实现类
public static void sleep(long millis);//是当前正在运行的线程暂停 millis毫秒(暂时停止执行)
public static native void yield();//yield只是让当前线程暂停一下,让系统的线程调度器从新调度一次,希望优先级
- 线程的优先级
public final int getPriority():获取线程的优先级
public final int setPriority(int newPriority):改变线程的优先级,范围在[1, 10]之间。
Thread内部类声明的三个常量
MIN_PRIORITY (1) :最低优先级
NORM_PRIORITY (5) :普通优先级,默认情况下 main线程具有普通优先级。
MAX_PRIORITY (10) :最高优先级
- 线程的生命周期
jdk5.0之前:
jdk5.0及之后:
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
1.4 如何解决线程安全问题(重、难)
- 什么是线程安全问题? 多个线程操作共享数据,就有可能出现线程安全问题。
- 如何解决线程安全为题?
- 同步机制:①同步代码块;②同步方法
-
在实现Runnable接口的方式中,同步监视器可以考虑使用:this ;
-
在继承Thread类的方式中,同步监视器 慎用 this,可以考虑使用:当前类.class 。
-
非静态的同步方法,默认同步监视器是 this(要考虑是否唯一?);
-
静态的同步方法,默认同步监视器是当前类本身。
-
- jdk5.0新增:Lock接口及其实现类。(保证多个线程共用同一个Lock的实例)
- 同步机制:①同步代码块;②同步方法
1.5 同步机制相关的问题
- 懒汉式的线程安全的写法;
- 同步机制带来的问题:死锁
- 死锁产生的条件及规避方式
1.6 线程间的通信
在同步机制下考虑线程间的通信;
wait()、notify()、notifyAll() 都需要使用在同步代码块或同步方法中。
高频笔试:wait() 、sleep()
2. 面试题
2.1 线程概述
2.1.1 什么是线程?
是程序内部的一条执行路径
。线程是CPU调度和执行的最小单位
2.1.2 线程、进程区别
进程:对应一个运行中的程序;
线程:是运行中进程的一条或多条执行路径
2.1.3 多线程使用场景?
- 手机app应用的图片的下载
- 迅雷的下载
- Tomcat服务器上web应用,多个客户端发起请求,Tomcat针对多个请求开辟多个线程处理
2.2 如何实现多线程
2.2.1 如何在Java中实现多线程
类似问题
创建多线程用Runnable 还是Thread?
多线程有几种实现方式,都是什么?
四种
2.2.2 Thread类中的start()和run()有什么区别?
start():①开启线程;②调用 run()
2.2.3 Java中Runnable和Callable有什么不同?
-
实现Callable接口
与实现Runnable接口
相比有哪些优点?- call()方法有返回值,更灵活;
- call()可以使用 throws的方式处理异常,更灵活;
- Callable使用了泛型参数,可以指明具体的 call()方法的返回值类型,更灵活。
-
实现Callable接口方式创建线程有哪些缺点?
4. 如果主线程需要获取子线程 call()方法的返回值,则此时主线程是阻塞状态。
2.2.4 什么是线程池,为什么使用?
- 提高了程序执行的效率。(线程已经提前准备好了);
- 提高了资源的复用率。(执行完的线程并未销毁,而是可以继续执行其他的任务)
- 可以设置相关的参数,对线程池中的线程的使用进行管理。
2.3 常用方法、生命周期
2.3.1 sleep()和 yield()区别?
sleep():一旦调用就进入“阻塞”(或 TIMED_WAITING状态)
yield():释放 CPU的执行权,处在RUNNABLE的状态
2.3.2 线程创建中的方法?和属性情况?
略
2.3.3 线程的生命周期?
略
2.3.4 现成的基本状态以及状态之间的关系?
类似问题:
线程有哪些状态?如何让线程进入阻塞状态;
线程有几个状态?就绪和阻塞有什么不同?
Java的线程都有哪几种状态
2.3.5 stop()和suspend()方法为何不推荐使用?
stop():一旦执行,会释放同步监视器线程就结束了,导致 run()有未执行结束的代码,形成线程安全问题。
suspend():与 resume()搭配使用,导致死锁。
2.3.6 Java线程优先级是怎么定义的?
三个常量 ,[1, 10]
2.4 线程安全与同步机制
2.4.1 如何理解线程安全?它是如何造成的?
类似问题:
线程安全说一下??
对线程安全的理解?
什么是线程安全?
多个线程操作共享数据,导致出现线程不安全
2.4.2 多线程公用一个数据变量需要什么?
线程安全问题
2.4.3 多线程保证线程安全一般有几种方式?
类似问题:
- 如何理解线程安全问题,说明怎么解决线程安全问题?
- 说出你所知道线程同步的方法?
- 有哪些方法实现线程安全?
- 同步有几种实现方法,都是什么?
- 你在实际编码过程中是如何避免线程安全问题?
- 如何让线程同步?
- 多线程有什么同步措施?
- 同步有几种实现方法,都是什么?
- 同步机制:①同步代码块;②同步方法
- Lock
2.4.4 用什么关键字修饰同步方法?
synchronized
2.4.5 synchronized加在静态方法和普通方法区别?
同步监视器不同。静态方法中:当前类本身;非静态方法中:this
2.4.6 Java中synchronized和ReentrantLock区别?
类似问题:
- 多线程安全机制中 synchronized和 Lock的区别?
- 怎么实现线程安全,各个实现方法有什么区别?
- synchronized和 Lock的区别?
- synchronized不管是同步代码块还是同步方法,都需要在结束一对“{}”之后,释放对同步监视器的调用;
- Lock是通过调用两个方法控制需要被同步的代码,更灵活。
- Lock作为接口,提供了多种实现类,适合更多复杂的场景,效率更高。
2.4.7 当一个线程进入一个对象的一个 synchronized方法后,其他线程是否可进入此对象的其他方法?
需要看其他方法是否使用 synchronized修饰,同步监视器的this
是否是同一个。只有使用了 synchronized,并且是同一个this
的前提下,就不能访问了。
2.4.8 线程同步和阻塞的关系?同步一定阻塞吗?阻塞一定同步吗?
同步一定阻塞;阻塞不一定同步。
2.5 死锁
2.5.1 什么是死锁?产生的原因及必要条件?
- 出现死锁的原因?
- 互斥条件
- 占用且等待
- 不可抢夺(或不可抢占)
- 循环等待
- 如何避免死锁?
死锁一旦出现,基本很难人为干预,只能尽量规避,可以打破任意一个条件。为保证同步需要,只能对后面 3个条件进行破坏。
- 针对条件1:互斥条件基本上无法被破坏。因为线程需要通过互斥解决安全问题;
- 针对条件2:可以考虑
一次申请所有需要的资源
,就不会出现等待的问题;- 针对条件3:占用部分资源的线程,进一步申请其他资源时,若申请不到,就
主动释放掉已占用的资源
;- 针对条件4:可以
将资源改为线性顺序
。申请资源时,先申请序号较小的,这样就避免了循环等待的问题
2.5.2 如何避免死锁?
死锁一旦出现,基本很难人为干预,只能尽量规避,可以打破任意一个条件。为保证同步需要,只能对后面 3个条件进行破坏。
- 针对条件1:互斥条件基本上无法被破坏。因为线程需要通过互斥解决安全问题;
- 针对条件2:可以考虑
一次申请所有需要的资源
,就不会出现等待的问题;- 针对条件3:占用部分资源的线程,进一步申请其他资源时,若申请不到,就
主动释放掉已占用的资源
;- 针对条件4:可以
将资源改为线性顺序
。申请资源时,先申请序号较小的,这样就避免了循环等待的问题
2.6 线程通信
2.6.1 Java中notify()和 notifyAll()有什么区别?
wait()
:线程一旦执行此方法,就进入等待状态。同时,释放同步监视器的调用notify()
:线程一旦执行此方法,就会唤醒 被wait()的线程中 优先级最高的线程的那一个线程(如果优先级相同,随机选择一个)。被唤醒的线程从当初 被wait()的位置继续执行。notifyAll()
:线程一旦执行此方法,就会唤醒 被wait()的所有线程。
2.6.2 为什么wait()和 notify()方法要在同步代码块中调用?
因为调用者必须是同步监视器
2.6.3 多线程:生产者、消费者代码(同步、wait、notify编程)
类似问题
- 如何用代码解决消费者和生产者问题?
- 多线程中生产者和消费者如何保证同步?
- 为代码实现消费者和 生产者
package com.mMultithreadOther;
/**
* 生产者(Producer)&消费者(Consumer)
*/
public class Test6ProducerConsumer {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Producer pro1 = new Producer(clerk);
Consumer con1 = new Consumer(clerk);
Consumer con2 = new Consumer(clerk);
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() {
if (productNum >= 20) {
try {
wait();//等待消费者消费产品
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
productNum++;
System.out.println(Thread.currentThread().getName() + "生产了第" + productNum + "个,产品。。。");
//唤醒消费者
notify();
}
}
//减少产品数量
public synchronized void minusProduct() {
if (productNum <= 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(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.addProduct();
}
}
}
/**
* 消费者
*/
class Consumer extends Thread {
private Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
while (true) {
System.out.println("消费者开始消费");
try {
Thread.sleep(25);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.minusProduct();
}
}
}
2.6.4 wait()和 sleep()的区别?调用函数后状态有什么改变?
类似问题:
- 线程中sleep()和wait()有什么区别?
- Java线程阻塞调用 wait 函数和 sleep 的区别和联系
- wait()和 sleep()的区别,它们两个谁会释放锁?
1)相同点:
- 一旦执行,当前线程都会进入阻塞;
2)不同点:
不同 | wait() | sleep() |
---|---|---|
声明不同 | 声明在Object类中 | 声明在Thread类中,静态的 |
使用场景不同 | 只能用在同步代码块 或同步方法 中 | 可以在任何需要的场景中使用 |
同步代码块 或同步方法 | 一旦执行,会释放同步监视器 | 即便执行,也不会释放同步监视器 |
结束阻塞的方式 | 到达指定时间,自动结束 或被notify()唤醒,结束阻塞 | 到达指定时间自动结束阻塞 |
2.7单例模式(线程安全)
2.7.1 手写一个单例模式(Singleton),还要是安全的
饿汉式;
安全的懒汉式;
内部类;
2.7.2 手写一个懒汉式的单例模式&解决其线程问题,并说明思路?
类似问题:
手写一个懒汉式的单例模式
饿汉式;
安全的懒汉式;
内部类;