一)多线程
1.多线程的存在帮助我们加快了数据的处理的效率,减少数据处理的时间,但是如果多线程使用不当不但不会对效率进行加快,反而成为整个程序的弊端,多线程的开销是要大于单线程的,多线程之间就涉及到了线程之间调度,通信,创建销毁,同步等问题。
JDK1.5之间使用Synchroinzed关键字进行线程的同步,之后有了Volatile关键字。在JDK1.5之后java添加了强大了 java.util.concurrent 提供了对线程的控制对应方法
主要根据下面的几个方面对java JUC进行说明
二)Volatile关键字的说明
1.内存可见性问题
什么是内存可见性
下面我们根据一个小程序来说明内存可见性的问题
1.V类为Runnable的实现类实现run方法,有成员属性flag
2.线程体将flag值变为true
3.TestVolatile main方法内写入死循环循环读取flag的值如果为true的话就退出循环
public class TestVolatile {
public static void main(String[] args) {
V v=new V();
new Thread(v).start();
while (true) {
if(v.getFlag()) {
System.out.println("================");
break;
}
}
}
}
class V implements Runnable{
private boolean flag=false;
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
flag=true;
System.out.println("flag="+flag);
}
public boolean getFlag() {
return flag;
}
}
结果:程序没有执行结束
解析:
1.java虚拟机在进行线程创建的时候会为每一个线程都会分配一个缓存区,提高线程的效率
2.共享数据存储的位置是在主存中,当两个线程都去进行操作flag这个数据共享数据的时候就产生了问题
3.原因:
此时的两个线程的作用一个是读一个是写(run写main读)
1.线程修改数据的第一步是先将主存中的数据读取到自己的缓存区中,也就是说线程对共享数据的操作是在自己的缓存区内执行的,执行结束后在将修改后的数据写回主存
2.如果run线程在没有将修改好的数据写会主存中main线程抢到了cpu资源开始执行死循环,由于while true循环调用的是相对于底层的代码执行的效率是非常的高,高到main线程无法在去主存中读取flag的值,所以main线程读取的flag的值是false
3.这就是内存可见性的问题,当多个线程操作共享数据的时候多个线程操作共享数据不可见
问题解决:
1.使用同步锁(效率低)
2.使用Volatile关键字
Volatile关键字是一个轻量级的同步组件,保证多个线程在操作共享数据的时候是可见的,使用了叫内存栅栏的方式实现内存可见性的问题解决,保证线程缓存区内的数据及时刷新到主存中(你可以理解为Volatile关键字修饰的变量的操作是在主存内完成的),并且Volatile关键字修饰的变量不具备重排序的功能
3.相较于Synchroinzed关键字Volatile关键字保证线程的安全不具备:
1)不具备互斥性
Synchroinzed是保证线程安全的在一个线程拿到同步锁时其他线程无法对共享数据进行操作,而Volatile则不具备这种功能,它只保证数据在主存中刷新而不保证数据的操作是一个线程的操作。
2)不具备原子性
原子性:不可分割的意思
也就是无法保证数据的安全性,也就是存在线程安全问题