java 面试 性能_Java性能调优面试-4

1、Volatile可见性底层实现原理

代码分析:

通过循环开启10个线程,每个线程执行1000次increase()。

t.join()表示主线程等待子线程的终止。也就是说主线程的代码块中,如果碰到了t.join()方法,此时主线程需要等待(阻塞),等待子线程结束了(Waits for this thread to die.),才能继续执行t.join()之后的代码块。

即通过循环表示每调一次t.join()表示主线程等该子线程结束,循环结束后则所有子线程执行完成,则主线程继续想下执行。

public class VolatileAtomicTest {

public static volatile int num = 0;

public static void increase(){

num++;

}

public static void main(String[] args) throws InterruptedException {

Thread[] threads = new Thread[10];

for(int i=0;i

threads[i] = new Thread(new Runnable() {

@Override

public void run() {

for (int i=0;i<1000;i++){

increase();

}

}

});

threads[i].start();

}

//主线程需要等待(阻塞)所有子线程执行完

for (Thread t : threads){

t.join();

}

System.out.println(num);//1000*100=10000

}

}

结果中num<=10000。

结果分析:

num++不保证原子性:是因为+1的操作有丢失。

结果<10000的原因:当线程1计算完num++执行assign得num=1,此时线程1准备将num=1回写回主内存。在线程1写到总线之前,线程2也计算完num++执行assign得num=1。线程1开始经过总线,而线程2通过CPU总线嗅探会将工作内存值失效,此时导致线程2的计算结果失效。当线程1更新回主内存时num=0>1。此时主内存值为1。

正常线程1将数据同步回主内存,线程2开始读主内存执行计算逻辑,则num=1>2。

6cd557ca20389bce7b4dcec79d11a6b5.png

2、Volatile有序性

2.1、没有添加volatile

public class VolatileSerialTest {

static int x=0,y=0;

public static void main(String[] args) throws InterruptedException {

Set resultSet = new HashSet<>();

Map resultMap = new HashMap<>();

for (int i = 0; i < 1000000; i++) {

x=0;y=0;

resultMap.clear();

Thread one = new Thread(new Runnable() {

@Override

public void run() {

int a = y;

x = 1;

resultMap.put("a", a);

}

});

Thread other = new Thread(new Runnable() {

@Override

public void run() {

int b = x;

y = 1;

resultMap.put("b", b);

}

});

one.start();

other.start();

one.join();

other.join();

resultSet.add("a="+resultMap.get("a")+","+"b="+resultMap.get("b"));

System.out.println(resultSet);

}

}

}

结果:[a=1,b=0, a=0,b=1,a=0,b=0, a=1,b=1]

2.2、添加volatile

public class VolatileSerialTest {

static volatile int x=0,y=0;

public static void main(String[] args) throws InterruptedException {

Set resultSet = new HashSet<>();

Map resultMap = new HashMap<>();

for (int i = 0; i < 1000000; i++) {

x=0;y=0;

resultMap.clear();

Thread one = new Thread(new Runnable() {

@Override

public void run() {

int a = y;

x = 1;

resultMap.put("a", a);

}

});

Thread other = new Thread(new Runnable() {

@Override

public void run() {

int b = x;

y = 1;

resultMap.put("b", b);

}

});

one.start();

other.start();

one.join();

other.join();

resultSet.add("a="+resultMap.get("a")+","+"b="+resultMap.get("b"));

System.out.println(resultSet);

}

}

}

结果:[a=1,b=0, a=0,b=1,a=0,b=0]

2.3、原因分析

程序存在指令重排,导致下面四行代码执行的先后顺序不确定,都有可能。

int a = y;

x = 1;

int b = x;

y = 1;

存在volatile时,volatile修饰了x和y,而lock指令修饰的代码自带内存屏障的功能,可以理解为凡是有内存屏障修饰的指令,cpu是没有办法把它前面的代码优化排到它的后面执行。即x=1和y=1前面都是有lock修饰的,cpu不能把x=1前面额所有代码拿到x=1后面执行,也不能把y=1前面的所有代码拿到y=1后面执行。

cpu对代码重排序的原因:cpu内部认为重排序后比排序前代码执行效率更高,往往cpu重排序会导致bug产生。

2、t.join()和t.wait()

join()的使用场景

在很多情况下,主线程创建并启动子线程,如果子线程中要进行大量的耗时运算,主线程将可能早于子线程结束。如果主线程需要知道子线程的执行结果时,就需要等待子线程执行结束了。

主线程可以sleep(xx),但这样的xx时间不好确定,因为子线程的执行时间不确定,join()方法比较合适这个场景。

join()方法:

join()是Thread类的一个方法。根据jdk文档的定义:

public final void join()throws InterruptedException: Waits for this thread to die.

join()方法的作用,是等待这个线程结束;

即是主线程等待子线程的终止。也就是说主线程的代码块中,如果碰到了t.join()方法,此时主线程需要等待(阻塞),等待子线程结束了(Waits for this thread to die.),才能继续执行t.join()之后的代码块。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值