其他
2 万字长文详解 10 大多线程面试题|原力计划
作者 | ZZZhonngger
责编 | 伍杏玲
出品 | CSDN博客
保证可见性 不能保证原子性 禁止指令重排序
线程解锁前,必须把共享变量的值刷新回主内存 线程加锁前,必须读取主内存的最新值到自己的工作内存 加锁解锁是同一把锁
/**
* @Author Zhongger
* @Description 验证Volatile的可见性
* @Date 2020.3.4
*/
public class VolatileDemo {
public static void main(String[] args) {
MyData myData = new MyData();
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"\t come in");
try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); }
myData.addOne();
System.out.println(Thread.currentThread().getName()+"\t updated a="+myData.a);
},"A").start();
//第二个线程为main线程
while (myData.a==0){
//如果线程间的可见性不能保证,那么此循环回成为死循环
}
//如果执行到以下语句,证明volatile可以保证线程间的可见性
System.out.println(Thread.currentThread().getName()+"\t come here");
}
}
class MyData {
//int a = 0;
volatile int a = 0;
void addOne() {
this.a += 1;
}
}
public static void main(String[] args) {
test2();
}
public static void test2(){
MyData data = new MyData();
for (int i = 0; i < 20; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
data.addOne();
}
}).start();
}
// 默认有 main 线程和 gc 线程
while (Thread.activeCount() > 2) {
Thread.yield();
}
System.out.println(data.a);
}
}
class MyData {
//int a = 0;
volatile int a = 0;
void addOne() {
this.a += 1;
}
}
class Test{
volatile int n=0;
public void add(){
n++;
}
}
编译成字节码文件是这样的:
public void addAtomic(){
atomicInteger.incrementAndGet();//相当于n++
}
保证特定操作的执行顺序 保证某些变量的内存可见性(利用该特性实现 volatile 的内存可见性)
工作内存与主内存同步延迟现象导致可见性问题:可以使用 synchronzied 或 volatile 关键字解决,它们可以使用一个线程修改后的变量立即对其他线程可见
对于指令重排导致可见性问题和有序性问题,可以利用 volatile 关键字解决,因为 volatile 的另一个作用就是禁止指令重排序优化
class SingleTon {
private static volatile SingleTon singleTon;
private SingleTon() {
}
//解决线程安全问题,同时解决懒加载问题,也保证了效率
public static synchronized SingleTon getSingleTon() {
if (singleTon == null) {
//同步代码效率较低
synchronized (SingleTon.class) {
if (singleTon == null) {
singleTon = new SingleTon();
}
}
}
return singleTon;
}
}
instance(memory); // 2.初始化对象
instance = memory; // 3.设置instance指向刚分配的内存地址,此时instance != null
instance = memory; // 3.设置instance指向刚分配的内存地址,此时instance != null,但对象还没有初始化完成
instance(memory); // 2.初始化对象
import java.util.concurrent.atomic.AtomicInteger;
/**
* @Author Zhongger
* @Description CAS算法——比较和交换
* @Date 2020.3.5
*/
public class CASDemo {
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(5);
//三个线程获取主内存中的值,并当主内存中的值为5时,替换为2020
for (int i = 0; i < 3; i++) {
System.out.println(atomicInteger.compareAndSet(5, 2020)+"\t current data:"+atomicInteger.get());
}
}
}
* Atomically increments by one the current value.
*
* @return the previous value
*/
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
private static final long serialVersionUID = 6214790243416807050L;
// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
// 获取下面 value 的地址偏移量
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
private volatile int value;
// ......
}
valueOffset为该对象的引用地址
expected为期望修改的值
val为修改的数值
public final int getAndAddInt(Object obj, long valueOffset, long expected, int val) {
int temp;
do {
temp = this.getIntVolatile(obj, valueOffset); // 获取当前对象在其地址上的快照值
} while (!this.compareAndSwap(obj, valueOffset, temp, temp + val)); // 如果此时 temp 没有被修改,把其值修改成temp+val,就能退出循环;否则重新获取,这个循环的过程,就相当于自旋锁。
return temp;
}
import java.util.concurrent.atomic.AtomicReference;
/**
* @Author Zhongger
* @Description 原子引用演示
* @Date 2020.3.7
*/
public class AtomicReferenceDemo {
public static void main(String[] args) {
AtomicReference<User> userAtomicReference = new AtomicReference<>();
User user1 = new User("A",21);
User user2 = new User("B", 26);
userAtomicReference.set(user1);//将主物理内存中的值设置为A
System.out.println(userAtomicReference.compareAndSet(user1,user2)+"\t"+userAtomicReference.get().toString());//CAS算法
System.out.println(userAtomicReference.compareAndSet(user1,user2)+"\t"+userAtomicReference.get().toString());//经过上一行代码的CAS算法,主物理内存中的值是B而不是A,返回false
}
}
class User{
private String username;
private int age;
public User(String username, int age) {
this.username = username;
this.age = age;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", age=" + age +
'}';
}
}
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
/**
* @Author Zhongger
* @Description ABA问题
* @Date 2020.3.7
*/
public class ABAProblem {
private static AtomicReference<Integer> atomicReference=new AtomicReference<>(100);
public static void main(String[] args) {
new Thread(()->{
atomicReference.compareAndSet(100,127);//由于Integer的范围,expect和update的值都应该在-128~127之间
System.out.println("100->101:"+atomicReference.get());
atomicReference.compareAndSet(127,100);//ABA操作,将100改成127,然后将127又改回100
System.out.println("101->100:"+atomicReference.get());
},"Thread1").start();
new Thread(()->{
try {//确保Thread1完成一次ABA操作
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
atomicReference.compareAndSet(100,2020);
//读取到主存中值仍然为100,执行更新操作,其实中途主存的值发生了100->127->100的变化
System.out.println("最终结果"+atomicReference.get());//返回2020
},"Thread2").start();
}
}
import java.util.concurrent.atomic.AtomicStampedReference;
/**
* @Author Zhongger
* @Description 使用AtomicStampedReference来解决ABA问题
* @Date 2020.3.7
*/
public class ABASolution {
private static AtomicStampedReference<Integer> atomicStampedReference=new AtomicStampedReference<>(100,1);
//主内存中初始值为100,版本号为1
public static void main(String[] args) {
new Thread(()->{
int stamp = atomicStampedReference.getStamp();//当前版本号
System.out.println(Thread.currentThread().getName()+"\t 第1次的版本号"+stamp);
try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }//等待Thread2也拿到相同的版本号
atomicStampedReference.compareAndSet(100,127,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);//更新一次,版本号加1
System.out.println(Thread.currentThread().getName()+"\t 第2次的版本号"+atomicStampedReference.getStamp());
atomicStampedReference.compareAndSet(127,100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);//更新一次,版本号加1
System.out.println(Thread.currentThread().getName()+"\t 第3次的版本号"+atomicStampedReference.getStamp());
},"Thread1").start();
new Thread(()->{
int stamp = atomicStampedReference.getStamp();//当前版本号
System.out.println(Thread.currentThread().getName()+"\t 第1次的版本号"+stamp);
try { Thread.sleep(4000); } catch (InterruptedException e) { e.printStackTrace(); }//等待Thread1完成一次ABA操作
boolean result = atomicStampedReference.compareAndSet(100, 2020, stamp, stamp + 1);
int newStamp = atomicStampedReference.getStamp();//最新版本号
System.out.println(Thread.currentThread().getName()+"\t修改成功否"+result+"当前实际版本号"+newStamp);
System.out.println(Thread.currentThread().getName()+"\t当前最新值"+atomicStampedReference.getReference());
},"Thread2").start();
}
}
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < 100; i++) {
new Thread(() -> {
list.add(random.nextInt(10));
System.out.println(list);
}).start();
}
}
}
new Vector();
Collections.synchronizedList(new ArrayList<>());
new CopyOnWriteArrayList<>();
/**
* @Author Zhongger
* @Description 可重入锁演示
* @Date 2020.3.8
*/
public class ReentrantLockDemo {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
phone.sendSMS();
},"A").start();
new Thread(()->{
phone.sendSMS();
},"B").start();
}
}
//资源类
class Phone{
public synchronized void sendSMS(){
System.out.println(Thread.currentThread().getName() + "\t 执行了sendSMS方法");
sendEmail();
//sendSMS()方法调用了加了锁的sendEmail方法,如果Thread.currentThread().getName()是一致的
//说明synchronized是可重入锁
}
public synchronized void sendEmail(){
System.out.println(Thread.currentThread().getName() + "\t 执行了sendEmail方法");
}
}
boolean isLocked = false;
Thread lockedBy = null;
int lockedCount = 0;
public synchronized void lock() throws InterruptedException {
Thread thread = Thread.currentThread();
while (isLocked && lockedBy != thread) {
wait();
}
isLocked = true;
lockedCount++;
lockedBy = thread;
}
public synchronized void unlock() {
if (Thread.currentThread() == lockedBy) {
lockedCount--;
if (lockedCount == 0) {
isLocked = false;
notify();
}
}
}
}
ReentrantLock lock = new ReentrantLock();
public void print() throws InterruptedException{
lock.lock();
doAdd();
lock.unlock();
}
private void doAdd() throws InterruptedException {
lock.lock();
// do something
System.out.println("ReentrantLock");
lock.unlock();
}
public static void main(String[] args) throws InterruptedException {
Count count = new Count();
count.print();
}
}
private boolean isLocked = false;
public synchronized void lock() throws InterruptedException {
while (isLocked) {
wait();
}
isLocked = true;
}
public synchronized void unlock() {
isLocked = false;
notify();
}
}
NotReentrantLock lock = new NotReentrantLock();
public void print() throws InterruptedException{
lock.lock();
doAdd();
lock.unlock();
}
private void doAdd() throws InterruptedException {
lock.lock();
// do something
lock.unlock();
}
public static void main(String[] args) throws InterruptedException {
Count count = new Count();
count.print();
}
}
private AtomicReference<Thread> atomicReference = new AtomicReference<>();
private void lock () {
System.out.println(Thread.currentThread() + " coming...");
while (!atomicReference.compareAndSet(null, Thread.currentThread())) {
// loop
}
}
private void unlock() {
Thread thread = Thread.currentThread();
atomicReference.compareAndSet(thread, null);
System.out.println(thread + " unlock...");
}
public static void main(String[] args) throws InterruptedException {
SpinLock spinLock = new SpinLock();
new Thread(() -> {
spinLock.lock();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("hahaha");
spinLock.unlock();
}).start();
Thread.sleep(1);
new Thread(() -> {
spinLock.lock();
System.out.println("hehehe");
spinLock.unlock();
}).start();
}
}
try {
Integer val = synchronousQueue.take();
System.out.println(val);
Integer val2 = synchronousQueue.take();
System.out.println(val2);
Integer val3 = synchronousQueue.take();
System.out.println(val3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
public static void main(String[] args) {
String lockA = "lockA";
String lockB = "lockB";
DeadLockDemo deadLockDemo = new DeadLockDemo();
Executor executor = Executors.newFixedThreadPool(2);
executor.execute(() -> deadLockDemo.method(lockA, lockB));
executor.execute(() -> deadLockDemo.method(lockB, lockA));
}
public void method(String lock1, String lock2) {
synchronized (lock1) {
System.out.println(Thread.currentThread().getName() + "--获取到:" + lock1 + "; 尝试获取:" + lock2);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock2) {
System.out.println("获取到两把锁!");
}
}
}
}
32376 com.intellij.idea.Main
28521 com.cuzz.thread.DeadLockDemo
27836 org.jetbrains.kotlin.daemon.KotlinCompileDaemon
28591 sun.tools.jps.Jps
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.191-b12 mixed mode):
"Attach Listener" #13 daemon prio=9 os_prio=0 tid=0x00007f7acc001000 nid=0x702a waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
// ...
Found one Java-level deadlock:
=============================
"pool-1-thread-2":
waiting to lock monitor 0x00007f7ad4006478 (object 0x00000000d71f60b0, a java.lang.String),
which is held by "pool-1-thread-1"
"pool-1-thread-1":
waiting to lock monitor 0x00007f7ad4003be8 (object 0x00000000d71f60e8, a java.lang.String),
which is held by "pool-1-thread-2"
Java stack information for the threads listed above:
===================================================
"pool-1-thread-2":
at com.cuzz.thread.DeadLockDemo.method(DeadLockDemo.java:34)
- waiting to lock <0x00000000d71f60b0> (a java.lang.String)
- locked <0x00000000d71f60e8> (a java.lang.String)
at com.cuzz.thread.DeadLockDemo.lambda$main$1(DeadLockDemo.java:21)
at com.cuzz.thread.DeadLockDemo$$Lambda$2/2074407503.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
"pool-1-thread-1":
at com.cuzz.thread.DeadLockDemo.method(DeadLockDemo.java:34)
- waiting to lock <0x00000000d71f60e8> (a java.lang.String)
- locked <0x00000000d71f60b0> (a java.lang.String)
at com.cuzz.thread.DeadLockDemo.lambda$main$0(DeadLockDemo.java:20)
at com.cuzz.thread.DeadLockDemo$$Lambda$1/558638686.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Found 1 deadlock.