Java 线程与进程(1)(基础知识及用法(问答形式))
1 Java 中有几种新起线程的方式?run和start的区别? 方式一:自定义线程继承Thread
方式二:实现Runnable接口
public static void main(String[] args) {
// 方式一
NewThread1 newThread1 = new NewThread1();
newThread1.start();
// 方式二
NewThread2 newThread2 = new NewThread2();
new Thread(newThread2).start();
}public static class NewThread1 extends Thread{
@Override
public void run() {
super.run();
System.out.println("new thread by extends Thread");
}
}public static class NewThread2 implements Runnable{
@Override
public void run() {
System.out.println("new thread by implements Runnable");
}
}
run是函数调用 和线程没有任何关系;
start会走底层,走系统层 最终调度到 run函数,这才是线程。
2 怎么让Java线程安全停止工作? stop()还是interrupt()的选择:
- stop() : 暴力方式,不要用(过时), 如下载一部分终止危险,且线程机制中有来不及释放的碎片
- interrupt():协作方式,可以安全停止(以下有Thread和Runnable两种方式的中断处理代码)
public class EndThread {
public static void main(String[] args) throws InterruptedException {
MyThread myThread = new MyThread();
myThread.start();
MyRunnable myRunnable = new MyRunnable();
Thread runableThread = new Thread(myRunnable);
Thread.sleep(1000);
runableThread.interrupt();
myThread.interrupt();
// 发出中断信号 但不会自己使线程停止 需线程中通过isInterrupted()做判断处理
}public static class MyThread extends Thread{
@Override
public void run() {
super.run();
String name = Thread.currentThread().getName();
while (!isInterrupted()) {
System.out.println(name + " ==== is run state" + isInterrupted());
}
}
}public static class MyRunnable implements Runnable{
@Override
public void run() {
String name = Thread.currentThread().getName();
// 获取当前线程的中断信息状态
while (!Thread.currentThread().isInterrupted()) {
System.out.println(name + " ==== is run state" + Thread.currentThread().isInterrupted());
}
}
}
}
3 多线程中的并行和并发的理解 并行:类比几个车道就可以有几辆车并行行驶
并发:和时间有关系,计算吞吐量,类比车流量
4 线程常用的方法和线程状态(图),各个方法使用场景及流程
文章图片
4.1 sleep与wait的区别
sleep 是休眠,等休眠时间一过,才有执行权的资格(无条件可以休眠)
wait 是等待,需要人家来唤醒,唤醒后,才有执行权的资格(某些原因与条件需要等待一下)
注意:只是又有资格了,并不代表马上就会被执行,什么时候又执行起来,取决于操作系统调度
另外,sleep在 catch异常时会被 InterruptedException e 清除中断标记
4.2 如何控制线程的顺序——join控制
public class JoinThreadTest {
public static void main(String[] args) throws InterruptedException{
JoinThread joinThreadA = new JoinThread("A");
JoinThread joinThreadB = new JoinThread("B");
joinThreadA.start();
//放弃当前线程的执行,并返回对应的线程的执行,joinThreadA执行完了,main线程才有执行的机会
joinThreadA.join();
joinThreadB.start();
}public static class JoinThread extends Thread{
public JoinThread(String name){
super(name);
}
@Override
public void run() {
super.run();
for(int i=0;
i<100;
i++){
System.out.println(this.getName() + ":" + i);
}
}
}
}
4.3 如何让出当前线程执行权—yield(几乎不用)
用法与上面的 join 一样,只不过执行效果是等其他线程执行完了最后才执行设置 yield 的线程。
4.4 关于守护线程----场景?:
// 主线程执行完毕后守护线程也跟着一起结束
public class DaemonThread {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread() {
@Override
public void run() {
for (int i = 0;
i < 50;
i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + "---" + i);
}
}
};
t.setDaemon(true);
// 设置了守护线程
t.start();
// 谁调用的 main, main管了我 我就守护main// 主线程,是为了 等 Thread t 10秒钟
Thread.sleep(10000);
// 走到这里,代表主线程结束,主线程结束不管t线程有没有结束都必须结束,因为t线程是守护线程,守护了main
}
}
5 对锁的使用和区分(类锁/对象锁/显示锁)—死锁 5.1 类锁------隐式锁
public class GPSEngine {
private static GPSEngine gpsEngine;
public GPSEngine getGpsEngine(){
if (gpsEngine == null){
gpsEngine = new GPSEngine();
}
return gpsEngine;
}// 持有GPSEngine.class的类锁
public static synchronized GPSEngine getGpsEngine1(){
if (gpsEngine == null){
// 其他任何线程不能进来,效率低
gpsEngine = new GPSEngine();
}
return gpsEngine;
}// 标准单例模式------DCL
public static synchronized GPSEngine getInstance(){
if (gpsEngine == null){
// 持有类锁
synchronized (GPSEngine.class){
if (gpsEngine == null){
gpsEngine = new GPSEngine();
}
}
}
return gpsEngine;
}
}
5.2 对象锁------隐式锁
public class SynTest {
private long count =0;
private Object obj = new Object();
// 作为一个锁 对象锁obj public long getCount() {
return count;
} public void setCount(long count) {
this.count = count;
} public void incCount(){
synchronized (obj){ // 使用一把锁------对象锁
count++;
}
} // synchronized == 类锁
public synchronized void incCount2(){
count++;
}
// this == 类锁
public void incCount3(){
synchronized (this){
count++;
}
} // 线程
private static class Count extends Thread{
private SynTest simplOper;
public Count(SynTest simplOper) {
this.simplOper = simplOper;
}@Override
public void run() {
for(int i=0;
i<10000;
i++){
simplOper.incCount();
// count = count+10000
}
}
} public static void main(String[] args) throws InterruptedException {
SynTest simplOper = new SynTest();
// 启动两个线程
Count count1 = new Count(simplOper);
Count count2 = new Count(simplOper);
count1.start();
count2.start();
Thread.sleep(50);
System.out.println(simplOper.count);
//20000
}
}
5.3 显示锁 Lock/ReentrantLock
// 声明一个显示锁之可重入锁new 可重入锁------ 非公平锁
private Lock lock = new ReentrantLock();
public void incr(){
// 使用 显示锁 的规范
lock.lock();
try{
count++;
} finally {// 打死都要执行最后一定会执行
lock.unlock();
}
} // 可重入锁 意思就是递归调用自己,锁可以释放出来
// synchronized == 天生就是 可重入锁
// 如果是非重入锁 ,就会自己把自己锁死
public synchronized void incr2(){
count++;
incr2();
}
6 生产者消费者案例(产生问题—解决方案) 6.1 初始版代码
public class CommunicationDemo {
public static void main(String[] args) {
// 创建资源对象
Res res = new Res();
// 创建生产者任务
ProduceRunnable produceRunnable = new ProduceRunnable(res);
// 创建消费者任务
ConsumeRunnable consumeRunnable = new ConsumeRunnable(res);
// 启动生产者任务
new Thread(produceRunnable).start();
// 启动消费者任务
new Thread(consumeRunnable).start();
}
}// 生产者任务
class ProduceRunnable implements Runnable {
private Res res;
ProduceRunnable(Res res) {
this.res = res;
}@Override
public void run() {//执行线程任务
for (int i = 0;
i < 20;
i++) {
res.put("面包");
}
}
}// 消费者任务
class ConsumeRunnable implements Runnable {
private Res res;
ConsumeRunnable(Res res) {
this.res = res;
}@Override
public void run() {//执行线程任务
for (int i = 0;
i < 20;
i++) {
res.out();
}
}
}class Res {
private String name;
private int id;
public void put(String name) { // 生产一个面包
id += 1;
this.name = name + " 商品编号:" + id;
System.out.println(Thread.currentThread().getName() + "生产者 生产了:" + this.name);
}public void out() {// 消费
id -= 1;
System.out.println(Thread.currentThread().getName() +">>>>>>>>>>>>>>>>>>>>>>>>>>>>> 消费者 消费了:" + this.name);
}
}
6.2 内置锁解决安全问题------先全部生产再消费
// 对操作共享数据的地方加入同步锁的方式来解决安全问题
public synchronized void put(String name) {
id += 1;
System.out.println(Thread.currentThread().getName() + "生产者 生产了:" + this.id);
}public synchronized void out() {
System.out.println(Thread.currentThread().getName() +">>>>>>>>>>>>>>>>>>>>>>>>>>>>> 消费者 消费了:" + this.id);
id -= 1;
}
6.3 实现生产一个消费一个------ wait/notify等待唤醒机制
class Res2 {
private String name;
private int id;
private boolean flag;
// 定义标记 默认第一次为falsepublic synchronized void put(String name) { // 生产一个面包
if (!flag) {
id += 1;
System.out.println(Thread.currentThread().getName() + "生产者 生产了:" + this.id);
flag = true;
// 修改标记//唤醒 wait();
冻结的线程,如果没有就是空唤醒,Java是支持的
notify();
// 注意:?? wait();
notify();
这些必须要有同步锁包裹着//当前自己线程 冻结,释放CPU执行资格,释放CPU执行权,CPU就会去执行其他线程了
try {
wait();
// 生产好一个,休息下
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}public synchronized void out() {// 消费
// 消费之前判断标记
if (flag) {
System.out.println(Thread.currentThread().getName() +">>>>>>>>>>>>>>>>>>>>>>>>>>>>> 消费者 消费了:" + this.id);
flag = false;
//修改标记
notify();
try {wait();
// 消费完休息下
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
7 ThreadLocal——隔离线程(待完善) 设置的值只对当前设置的线程有用,类似于副本,不会全局修改,Handler中有用到
ThreadLocalMap: 性能最高
8 并发基础补充知识点(待完善) 8.1 线程的生命周期
8.2 第三种创建方式的实质
8.3 死锁的条件及解决方案
8.4 活锁
9 CAS(Compare And Swap) 9.1 CAS含义及原理
原子操作:全部完成或全部未做,不可再分,如synchronized
含义:比较并交换
原理:循环指令直到成功
9.2 悲观锁和乐观锁
悲观锁:上下文切换 一次切换3-5ms 效率低
乐观锁:一次指令0.6ns
9.3 CAS问题
ABA问题:期间被换了但保持原样(本质已变)——加个版本戳解决
开销问题
只能保证一个共享变量的原子操作
9.4 原子操作类的使用(凡是以Atomic开头的)
更新基本类型类
更新数组类
更新引用类型
10 队列和阻塞队列 10.1 含义
队列:先进先出
阻塞队列:BlockingQueue接口
10.2 常见阻塞队列
有界
ArrayBlockingQueue:
LinkedBlockingQueue:
无界
PriorityBlockingQueue:
DelayBlockingQueue:
LinkedTranceferQueue:
其他
SychrononsQueue:不存储元素的阻塞队列
LinkedBlockingDeque:
11 线程池 11.1 什么是线程池?为什么要用线程池?
缩短任务的总执行时间
11.2 ThreadPoolExcutor 线程池
各个参数的含义:
corePoolSize:核心线程数
maxnumPoolSize:最大线程数
keepAliveTime:空闲线程存活时间
unit:存活时间单位
workQueue:阻塞队列
threadFactory:
handler:拒绝策略,四种
拒绝策略名称 | 说明 |
---|---|
DiscardOldestPolicy | 排在最前面最老的的丢弃 |
CallerRunsPolicy | 你行你来做,谁往线程池提交任务谁来做 |
DiscardPolicy | 最新提交的任务直接丢弃 |
AbortPolicy | 抛出异常,默认策略 |
核心线程==》阻塞队列==》最大线程数==》拒绝策略
提交任务
submit
关闭线程池
shutdown:中断未在执行的线程
shutdownNow:尝试关闭所有线程,但不一定会成功
【Java 线程与进程(1)(基础知识及用法(问答形式))】11.3 合理配置线程池
任务特性
CPU密集型:CPU在不断计算的——配置线程数不能超过CPU核心数
Runtime.getRuntime().availableProcessors()
IO密集型:与网络进行通讯,有读写磁盘操作的——机器CPU核心数*2
混合型:
12 面试题汇总
推荐阅读
- JAVA(抽象类与接口的区别&重载与重写&内存泄漏)
- Docker应用:容器间通信与Mariadb数据库主从复制
- 《真与假的困惑》???|《真与假的困惑》??? ——致良知是一种伟大的力量
- 第326天
- Shell-Bash变量与运算符
- 事件代理
- 逻辑回归的理解与python示例
- Guava|Guava RateLimiter与限流算法
- Java|Java OpenCV图像处理之SIFT角点检测详解
- 我和你之前距离