文章目录
- java 并发篇
- 1. 进程和线程的区别
- 2. 如何开启线程
- 2.1 继承(extends)Thread 重写(@Overried)run方法
- 2.2 实现(Implement)Runnable 接口 实现(@Override)run方法
- 2.3 实现(Implement)Callable接口,实现(@Override)call方法,通过FutureTask创建一个线程,获取线程返回值
- 2.4 通过线程池开启
- 2.5 为什么有这么多种线程开启方式
- 3 怎么保证线程安全
- 3.1 为什么需要?
- 3.2 怎么保证?
- 3.3 加锁的方式
- 4 Volatile 和 Synchronized 有什么区别?Volatile能不能保证线程安全?DCL(Double check Lock)单例为什么要加Volatile?
- 5. JAVA线程锁机制是怎么的?偏向锁,轻量级锁,重量级锁有什么区别?锁机制是怎么升级的?
- 6. 谈谈你对AQS的理解。以及AQS是如何实现可重入锁的?
- 7. 有A,B,C三个线程。如何保证三个线程同步执行?如何保证三个线程在并发下依次执行?如何保证三个线程有序交错执行?
- 8. 如何将一个int数组快速进行排序
java 并发篇
1. 进程和线程的区别
进程(Process):
进程是程序的一次执行实例,是系统进行资源分配和调度的基本单位。它包含了程序运行所需的所有资源,如内存空间、文件句柄、设备资源等。进程之间是相对独立的,每个进程都有自己独立的地址空间。
线程(Thread):
线程是进程中的一个执行单元,是操作系统能够进行运算调度的最小单位。一个进程可以包含多个线程,这些线程共享进程的资源(如内存空间),但每个线程有自己的执行栈和程序计数器,可以独立执行任务。
2. 如何开启线程
2.1 继承(extends)Thread 重写(@Overried)run方法
public class MyThread extends Thread{@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println("继承Therad");}}
}public static void main(String[] args){MyThread thread = new MyThread();thread.start;
//.start 启动线程 在start方法里会去调用run方法
//这里如果直接调用run方法,那就不会开启新的线程,只是普通方法的调用
}
2.2 实现(Implement)Runnable 接口 实现(@Override)run方法
public class Runnable01 implements Runnable{@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println("实现Runnable01");}}
}public static void main(String[] args){//实现Runnable接口Thread thread2 = new Thread(new Runnable01());thread2.start();
}
2.3 实现(Implement)Callable接口,实现(@Override)call方法,通过FutureTask创建一个线程,获取线程返回值
public class Callabled01 implements Callable {@Overridepublic String call() throws Exception {return "Callabled01";}
}public static void main(String[] args){//如果需要拿到线程执行过程的放回值 采用事项Callable接口FutureTask<String> futureTask = new FutureTask<>(new Callabled01());Thread thread4 = new Thread(futureTask);thread4.start();String s = null;try {s = futureTask.get();} catch (InterruptedException e) {} catch (ExecutionException e) {throw new RuntimeException(e);}System.out.println("拿到了"+s);
}
2.4 通过线程池开启
。。。。待补充
2.5 为什么有这么多种线程开启方式
基于java 单继承 可多实现的特性进行设计,根据业务需求自行选择,如果你这个类还要实现其他接口的功能,那么就选择实现(Implement)Runnable 接口
如果有获取线程返回值的需求,可以实现(Implement)Callable接口 实现call方法 的方式
3 怎么保证线程安全
3.1 为什么需要?
当你有多个线程对同一个资源进行操作时,就需要保证线程的安全。
3.2 怎么保证?
加锁,假设我有一段代码(资源)很多线程来抢,就在对这段代码加锁,哪个线程拿到锁了,就可以执行这段代码
3.3 加锁的方式
-
JVM提供的 Synchronized 关键字 (jdk1.5之前为重量级锁 1.5之后 转化为轻量)
-
jdk 提供的各种锁 Lock(公平锁 非公平锁 可重入锁)
4 Volatile 和 Synchronized 有什么区别?Volatile能不能保证线程安全?DCL(Double check Lock)单例为什么要加Volatile?
- Synchronized 关键字用来加锁,Volatile 只是保持线程之间变量的可见性。通常适用于一个线程写,多个线程读的情况
- 不能,Volatile 只能保证线程可见性,不能保证原子性
保证原子可见性,下图为一个主线程,还有两个子线程,子线程的flag是从主线程同步过来的,要使不同子线程之间flag改变是同步的是已知的,就要使用Volatile
3. Volatile 防止指令重排。在DCL中,防止高并发下的指令重排导致的线程安全问题。
public class SingleDemo01 {//单例模式--懒汉式public static volatile SingleDemo01 instance;private SingleDemo01(){};public static SingleDemo01 getInstance(){if (null == instance){synchronized (SingleDemo01.class){if (null == instance){instance = new SingleDemo01();}}}return instance;}
}
重排序,指的是计算机优化的计算过程,把本来 1 2 3的流程,进行1 3 2这样执行,单线程的情况下不会收到影响,但是多线程情况下,会有问题,volatile 可以禁止重排序的出现,让cpu严格按照 1 2 3 这样执行。
5. JAVA线程锁机制是怎么的?偏向锁,轻量级锁,重量级锁有什么区别?锁机制是怎么升级的?
- java的锁其实就是对象的MarkWord中记录的一个锁状态。无锁,偏向锁,轻量级锁,重量级锁代表不同的锁状态。
- 从无锁 --> 偏向锁 --> 轻量级锁 --> 重量级锁 代表锁的升级(偏向锁以及轻量级锁都是JVM级别 重量级锁是操作系统级别)
- 锁升级的过程
6. 谈谈你对AQS的理解。以及AQS是如何实现可重入锁的?
AQS 是 Java 并发包(java.util.concurrent)的核心框架之一,用于构建锁和其他同步器的基础组件。它通过一个整型状态变量 state 和一个 FIFO (先进先出)的双向队列(CLH 队列变种)来管理线程的同步状态。
- AQS是java线程同步的框架。是JDK中很多锁工具的核心实现框架。
- 在AQS中维护了一个信号state和一个线程组成的双向链表队列。其中,这个线程队列就是用来给线程排队的。而不同的同步器对 state 的语义有不同的定义,
例如:
(1)在 ReentrantLock 中,state 表示锁的重入次数。
(2)在 Semaphore 中,state 表示剩余的许可证数量。
7. 有A,B,C三个线程。如何保证三个线程同步执行?如何保证三个线程在并发下依次执行?如何保证三个线程有序交错执行?
- CountDownLatch
- CylicBarrier
- Semaphore
8. 如何将一个int数组快速进行排序
- 快速排序算发
- Fork-Jion 框架
- 快速排序算个发+fork-join