提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
一、java如何开启线程?怎么保证线程安全?
- 线程和进程的区别:进程是操作系统进行资源分配的最小单元、线程是操作系统进行任务分配的最小单元,线程属于进程
- 如何开启线程:常用的两种方式 继承Thread类,重写run方法、实现Runnable接口,实现run方法,两种实现方法是由于java的单继承多实现的机制,避免继承了Thread就不能继承其他的类,没有扩展性,所以常用的实现Runnable;另外还有一种方法是 实现Callable接口,实现call方法,这个方式是由返回值,如果需要放回线程中的数据时,可以使用此种方法,通过FutureTask创建一个线程,获取到线程返回值;最后就是通过线程池来创建线程(线程池后面单独介绍)。
- 如何保证线程安全?
核心思想就是 加锁 :JVM提供的锁,也就是Synchronized关键字、还有就是JDK提供各种锁Lock
二、Volatile和Synchronized有什么区别?Volatile能不能保证线程安全?DCL(Double Check Lock)单例为什么要加Volatile?
- Volatile和Synchronized有什么区别
volatile的使用可查看 java并发篇之volatile关键字
Synchronized关键字,用来加锁。Volatile只是保证变量的线程可见性。通常适用于一个变量在一个线程写,多个线程读的场景。 - Volatile能不能保证线程安全?
不能,Volatile关键字只能保存线程可见性,不能保证原子性 - DCL(Double Check Lock)单例为什么要加Volatile?
首先看以下代码
public class SingDemo {
private static SingDemo singDemo = new SingDemo();
// 私有化
private SingDemo(){};
public static SingDemo getInstance(){
return singDemo;
}
}
这是一个简单的单例模式,在项目启动时就将对象实例化,但是有时想要在用的时候在加载对象,可以将代码改成
public class SingDemo {
private static SingDemo singDemo;
// 私有化
private SingDemo(){};
public static SingDemo getInstance(){
if(null == singDemo){
singDemo = new SingDemo();
}
return singDemo;
}
}
初始化时不new对象,而是在用的时候判断后初始化对象,但是这样的代码就会有线程不安全的问题,同时两个线程访问时,可能会创建两次,这个时候可以加锁
public class SingDemo {
private static SingDemo singDemo;
// 私有化
private SingDemo(){};
public static SingDemo getInstance(){
if(null == singDemo){
synchronized (SingDemo.class){
if(null == singDemo){
singDemo = new SingDemo();
}
}
}
return singDemo;
}
}
当线程1进来判断为空,继续进入实例化,这时不管来多少线程都会被锁住,当线程1创建成功之后,后续线程解锁后会再次进行一次判断,这样就不会重复创建了,这种就叫做DCL(Double Check Lock)双重检查。
回归正题,Volatile在DCL中防止高并发的情况下防止指令重排造成的指令重排。
如下图,创建一个 Integer i = 8 这样一个包装类,CPU在底层会分为3步执行,
1、在堆内存中分配控件,不进行初始化
2、对象初始化,设值为8
3、栈建立指针与堆内存的关系
在单线程中不会存在问题,如果在多线程高并发的情况下,可能会出现顺序错误,就叫指令重排,这是就需要Volatile,保证指令的正确顺序
三、java线程锁机制是怎样的?偏向锁、轻量级锁、重量级锁有什么区别?
public class JolDemo {
public static void main(String[] args) {
Object o = new Object();
System.out.println(ClassLayout.parseInstance(o).toPrintable());
synchronized (o){
System.out.println(ClassLayout.parseInstance(o).toPrintable());
}
}
}
上面代码可以将未加锁和加锁之后的对象在JVM内存对象中的布局给打印出来
java 的锁只是在对象的Markword中记录一个锁状态,无锁、偏向锁、轻量级锁、重量级锁对应不同的锁状态
四、有A,B,C三个线程,如何保证三个线程同时执行?如何在并发情况下保证三个线程依次执行?如何保证三个线程有序交错进行?
原文地址 CountDownLatch、CylicBarrier、Semaphore的区别
面试题是结合B站楼兰老师的面试课总结而来,可直接看楼兰老师视频 楼兰老师面试课程