最后
Java架构学习技术内容包含有:Spring,Dubbo,MyBatis, RPC, 源码分析,高并发、高性能、分布式,性能优化,微服务 高级架构开发等等。
还有Java核心知识点+全套架构师学习资料和视频+一线大厂面试宝典+面试简历模板可以领取+阿里美团网易腾讯小米爱奇艺快手哔哩哔哩面试题+Spring源码合集+Java架构实战电子书+2021年最新大厂面试题。
注意:当多个线程执行一个方法时,该方法内部的局部变量并不是临界资源,因为这些局部变量是在每个线程的私有栈中,因此不具有共享性质,不会导致线程安全问题。
可见性
多线程访问同一个变量时,如果有一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。这是因为为了保证多个CPU之间的高速缓存是一致的,操作系统会有一个缓存一致性协议,volatile就是通过OS的缓存一致性协议策略来保证了共享变量在多个线程之间的可见性。
public class ThreadDemo2 {
private static boolean flag = false;
public void thread_1(){
flag = true;
System.out.println(“线程1已对flag做出改变”);
}
public void thread_2(){
while (!flag){
}
System.out.println(“线程2->flag已被修改,成功打断循环”);
}
public static void main(String[] args) {
ThreadDemo2 threadDemo2 = new ThreadDemo2();
Thread thread2 = new Thread(()->{
threadDemo2.thread_2();
});
Thread thread1= new Thread(()->{
threadDemo2.thread_1();
});
thread2.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread1.start();
}
}
执行结果
线程1已对flag做出改变
代码无论执行多少次,线程2的输出语句都不会被打印。为flag添加volatile修饰后执行,线程2执行的语句被打印
执行结果
线程1已对flag做出改变
线程2->flag已被修改,成功打断循环
局限:volatile只是保证共享变量的可见性,无法保证其原子性。多个线程并发时,执行共享变量i的i++操作<==> i = i + 1,这是分两步执行,并不是一个原子性操作。根据缓存一致性协议,多个线程读取i并对i进行改变时,其中一个线程抢先独占i进行修改,会通知其他CPU我已经对i进行修改,把你们高速缓存的值设为无效并重新读取,在并发情况下是可能出现数据丢失的情况的。
public class ThreadDemo3 {
private volatile static int count = 0;
public static void main(String[] args) {
for (int i = 0; i < 10; ++i){
Thread thread = new Thread(()->{
for (int j = 0; j < 1000; ++j){
count++;
}
});
thread.start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(“count执行的结果为->” + count);
}
}
执行结果
count执行的结果为->9561
注意:这个结果是不固定的,有时10000,有时少于10000。
原子性
就像恋人一样同生共死,表现在多线程代码中程序一旦开始执行,就不会被其他线程干扰要嘛一起成功,要嘛一起失败,一个操作不可被中断。在上文的例子中,为什么执行结果不一定等于10000,就是因为在count++是多个操作,1.读取count值,2.对count进行加1操作,3.计算的结果再赋值给count。这几个操作无法构成原子操作的,在一个线程读取完count值时,另一个线程也读取他并给它赋值,根据缓存一致性协议通知其他线程把本次读取的值置为无效,所以本次循环操作是无效的,我们看到的值不一定等于10000,如何进行更正---->synchronized关键字
public class ThreadDemo3 {
private volatile static int count = 0;
private static Object object = new Object();
public static void main(String[] args) {
for (int i = 0; i < 10; ++i){
Thread thread = new Thread(()->{
for (int j = 0; j < 1000; ++j){
synchronized (object){
count++;
}
}
});
thread.start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(“count执行的结果为->” + count);
}
}
执行结果
count执行的结果为->10000
加锁后,线程在争夺执行权就必须获取到锁,当前线程就不会被其他线程所干扰,保证了count++的原子性,至于synchronized为什么能保证原子性,篇幅有限,下一篇在介绍。
有序性
jmm内存模型允许编译器和CPU在单线程执行结果不变的情况下,会对代码进行指令重排(遵守规则的前提下)。但在多线程的情况下却会影响到并发执行的正确性。
public class ThreadDemo4 {
private static int x = 0,y = 0;
最后
按照上面的过程,4个月的时间刚刚好。当然Java的体系是很庞大的,还有很多更高级的技能需要掌握,但不要着急,这些完全可以放到以后工作中边用别学。
学习编程就是一个由混沌到有序的过程,所以你在学习过程中,如果一时碰到理解不了的知识点,大可不必沮丧,更不要气馁,这都是正常的不能再正常的事情了,不过是“人同此心,心同此理”的暂时而已。
“道路是曲折的,前途是光明的!”
已。
“道路是曲折的,前途是光明的!”
[外链图片转存中…(img-kprYI7yX-1714858860624)]
[外链图片转存中…(img-LesAIHBk-1714858860624)]