volatile应用
程序员文章站
2022-05-04 10:09:40
...
volatile概念:volatile关键字的作用是使变量在多个线程间可见.
在java中,每一个县城都会有一块工作内存区,其中存放着所有线程共享的主内存中的变量值的拷贝.当线程执行时,他在自己的工作内存区中操作这些变量.为了存取一个共享的变量,一个线程通常先获取锁定病区清除他的内存工作区,把这些共享变量从所有的现成的共享内存区中正确的装入到他自己所在的工作内存区中,当线程解锁时保证该工作内存区中变量的值写回到共享内存中.
![这里写图片描述](https://img-blog.csdn.net/20180107102331451?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2VpeGluXzM5NTQ5NjU2/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
public class RunThread extends Thread{
private boolean isRunning = true;
private void setRunning(boolean isRunning){
this.isRunning = isRunning;
}
public void run(){
System.out.println("进入run方法..");
int i = 0;
while(isRunning == true){
//..
}
System.out.println("线程停止");
}
public static void main(String[] args) throws InterruptedException {
RunThread rt = new RunThread();
rt.start();
Thread.sleep(1000);
rt.setRunning(false);
System.out.println("isRunning的值已经被设置了false");
}
}
结果,变量改了但是线程不停止
产生问题原因:jdk的问题
下图是jdk1.5之后加入的新特性,其他线程会将主线程变量拷贝一份到自己的内存区,为了提高效率
内存分析图如下
变量前加入volatile关键字:private volatile boolean isRunning = true;
public class RunThread extends Thread{
private volatile boolean isRunning = true;
private void setRunning(boolean isRunning){
this.isRunning = isRunning;
}
public void run(){
System.out.println("进入run方法..");
int i = 0;
while(isRunning == true){
//..
}
System.out.println("线程停止");
}
public static void main(String[] args) throws InterruptedException {
RunThread rt = new RunThread();
rt.start();
Thread.sleep(1000);
rt.setRunning(false);
System.out.println("isRunning的值已经被设置了false");
}
}
volatile不具备原子性(仅是多线程之间的可见,AtomicInteger具备原子特性)
不具备原子性案例
public class VolatileNoAtomic extends Thread{
private static volatile int count;
private static void addCount(){
for (int i = 0; i < 1000; i++) {
count++ ;
}
System.out.println(count);
}
public void run(){
addCount();
}
public static void main(String[] args) {
VolatileNoAtomic[] arr = new VolatileNoAtomic[100];
for (int i = 0; i < 10; i++) {
arr[i] = new VolatileNoAtomic();
}
for (int i = 0; i < 10; i++) {
arr[i].start();
}
}
}
打印结果不会到10000,因为打印的同时第二个线程继续操作值导致.
解决办法用jdk里面AtomicInteger类 代码如下
public class VolatileNoAtomic extends Thread{
//private static volatile int count;
private static AtomicInteger count = new AtomicInteger(0);
private static void addCount(){
for (int i = 0; i < 1000; i++) {
//count++ ;
count.incrementAndGet();
}
System.out.println(count);
}
public void run(){
addCount();
}
public static void main(String[] args) {
VolatileNoAtomic[] arr = new VolatileNoAtomic[100];
for (int i = 0; i < 10; i++) {
arr[i] = new VolatileNoAtomic();
}
for (int i = 0; i < 10; i++) {
arr[i].start();
}
}
}
AtomicInteger类中方法,重复再一个方法中出现不具备原子性需要加锁,
下面的代码multiAdd()方法是否加synchronized结果都是1000,为题在其他线程对count进行操作,只是
“count.addAndGet(1);
count.addAndGet(2);
count.addAndGet(3);
count.addAndGet(4); ”
这四行代码不具备原子性
public class AtomicUse {
private static AtomicInteger count = new AtomicInteger(0);
//多个addAndGet在一个方法内是非原子性的,需要加synchronized进行修饰,保证4个addAndGet整体原子性
/**synchronized*/
public synchronized int multiAdd(){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
count.addAndGet(1);
count.addAndGet(2);
count.addAndGet(3);
count.addAndGet(4); //+10
return count.get();
}
public static void main(String[] args) {
final AtomicUse au = new AtomicUse();
List<Thread> ts = new ArrayList<Thread>();
for (int i = 0; i < 100; i++) {
ts.add(new Thread(new Runnable() {
@Override
public void run() {
System.out.println(au.multiAdd());
}
}));
}
for(Thread t : ts){
t.start();
}
}
}
上面代码如果不加synchronized打印如下