一。线程的可见性volatile(不是太常用,因为他只能解决线程可见和阻止指令排序,并不能解决多线程的并发问题)
volatile:(1)保证变量的修改让所有线程可见
(2)阻止指令排序
这个程序有3个结果,0,42,没有进入循环直接结束
-
42:正常跑
-
0:yeild以后直接继续跑run的线程了
-
没有进入循环直接结束:这个就比肩奇怪了,除非这句话先运行了,否则是不会这样的啊。
事实上就是这句话线运行了,可是为什么会这样呢,cpu实际有一个指令排序的问题,几个指令在一起在逻辑允许的情况下不是按顺序运行的,比如运行慢的语句就后执行。
volatile关键字就可以解决这个问题,解决程序的可见性,加上了volatile可以确定其位置的语句在此处执行(意思就是它上面的一堆语句还是不安书序执行,但是一定是上面群执行完了再执行他,然后再执行下边的语句)
下边这个例子是从另外一个层面讲的,
上下两个线程都是无限循环的,正常情况下,2线程修改了bchange后一线程也就停止了,但是实际运行后直接卡死了,因为bchange一直没改变,
为什么呢,因为cpu有内存和缓存,运行时是放到缓存里里运行的,而第二个线程修改的值是直接放进内存的,一线程的缓存里一直有数据,所以他根本不去看内存,只看缓存,即使2线程改变了值,一线程也是看不到的。
这时就可以用volatile,让他必须从内存里读数据。
二,CAS操作
compare and set 先比较后修改
一般的并发问题都还cas操作,一线程比较后修改了值,这时二线程也来比较修改值,但它可能修改的是之前的值,所以才需要锁,或者原子性
三,线程封闭threadlocal
首先介绍一种封闭——栈封闭,比如方法内部的声明修改都是不会溢出的,因为有方法栈啊,方法用完了就释放了,别人也用不了(细致了解的自己百度)
threadlocal线程绑定,
当你想操作一组数据,又不受其他线程的干扰,就用它,他会给每一个线程绑定一个自己的副本,自己用自己的,不会打架
使用:把threadLocal对象放进threadlocal.set(),这样就制作了一个副本,再用get()拿出来,进行后续的操作。
注意:我爱主函数里只new了一个threadLocal,然后有两个线程同时使用它(改了数字并println),如果按平时的话,现成话打架的,但是把threadLocal对象放进threadlocal,set()之后就不会打架了,实际就是生成了不同的副本,各操作各的,互不干扰。
//这就是一个普通的bean来演示
public class TheadText {
private int num;
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
}
public class test {
public static void main(String[] args){
ThreadLocal<TheadText> threadLocal=new ThreadLocal(); //new一个ThreadLocal
TheadText theadText=new TheadText();
//把threadLocal对象放进threadlocal.set(),这样就制作了一个副本,再用get()拿出来,进行后续的操作。
new Thread(){
@Override
public void run(){
for (;;){
threadLocal.set(theadText);
TheadText theadText1=threadLocal.get();
theadText1.setNum(111);
System.out.println(Thread.currentThread().getName()+":"+theadText1.getNum());
Thread.yield();
}
}
}.start();
new Thread(){
@Override
public void run(){
for (;;){
threadLocal.set(theadText);
TheadText theadText2=threadLocal.get();
theadText2.setNum(222);
System.out.println(Thread.currentThread().getName()+":"+theadText2.getNum());
Thread.yield();
}
}
}.start();
}
}
结果;并没有打架