天天看點

java并發程式設計--實作可見性的四種可行方案

多線程程式設計主要學的就是"互斥"和"可見","互斥"指的就是就是同步,而"可見"想必很多人還沒有了解.

在方法或者變量已經同步的情況下,還會出現什麼問題嗎?

舉個例子:

MyVolatile.java:

package cn.mxl.test.wr;


public class MyVolatile {
	private  int a=0;

	public   void write() throws InterruptedException {
		a++;
		System.out.println("目前線程:"+Thread.currentThread().getName());
		System.out.println(a);
	}
	
	public   void read() throws InterruptedException {
		while(a==0) {
//			Thread.sleep(1);
//			System.out.println(a);
		}
		System.out.println("目前線程:"+Thread.currentThread().getName());
		System.out.println(a);
	}
}
           

MyRead.java:

package cn.mxl.test.wr;


public class MyRead implements Runnable{
	private MyVolatile task;
	public MyRead(MyVolatile task) {
		// TODO Auto-generated constructor stub
		this.task=task;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			task.read();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}
           

MyWrite.java:

package cn.mxl.test.wr;


public class MyWrite implements Runnable{
	private MyVolatile task;
	public MyWrite(MyVolatile task) {
		// TODO Auto-generated constructor stub
		this.task=task;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			task.write();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	

}
           

Run.java:

package cn.mxl.test.wr;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;

public class Run {
	public static void main(String[] args) throws Exception, ExecutionException {
		MyVolatile task=new MyVolatile();
		MyWrite mt1=new MyWrite(task);
		MyRead mt2=new MyRead(task);
		
		Thread tW=new Thread(mt1,"Write");
		Thread tR=new Thread(mt2,"Read");
		
		tR.start();
		
		Thread.sleep(2000);
		
		tW.start();
	}
}
           

運作結果:

java并發程式設計--實作可見性的四種可行方案

Read線程一直處于a==0中;

為什麼呢:

因為當Read線程 先開始線程的時候,加載主記憶體中的資料放到自己的私有記憶體中,在程式執行過程中為了提高效率,它就隻讀自己私有記憶體中的資料,不會再去讀主記憶體中的共享資料,在這個過程中,主記憶體對于該線程來說,隻寫不讀,也就是說将自己修改的變量值存放到主記憶體中,但是自己不去讀主記憶體的資料;是以說,在Read線程執行到while語句一直循環的時候,即使Write線程修改a==1值了,Read線程還是之前a==0的值,一直跳不出循環;

解決辦法-->可見性,讓Read線程讀a變量的時候去主記憶體(共享資料)中讀取:

volatiel關鍵字的使用:

MyVolatile.java:

package cn.mxl.test.wr;


public class MyVolatile {
	private volatile  int a=0;

	public   void write() throws InterruptedException {
		a++;
		System.out.println("目前線程:"+Thread.currentThread().getName());
		System.out.println(a);
	}
	
	public   void read() throws InterruptedException {
		while(a==0) {
//			Thread.sleep(1);
//			System.out.println(a);
		}
		System.out.println("目前線程:"+Thread.currentThread().getName());
		System.out.println(a);
	}
}
           

結果:

java并發程式設計--實作可見性的四種可行方案

其實在這個過程中,我遇到了一些有趣的事情,可見性這東西,其實還有三個東西可以實作:

第一個-->Thread.sleep():

MyVolatile.java(取消sleep的注解):

package cn.mxl.test.wr;


public class MyVolatile {
	private   int a=0;

	public   void write() throws InterruptedException {
		a++;
		System.out.println("目前線程:"+Thread.currentThread().getName());
		System.out.println(a);
	}
	
	public   void read() throws InterruptedException {
		while(a==0) {
			Thread.sleep(1);
//			System.out.println(a);
		}
		System.out.println("目前線程:"+Thread.currentThread().getName());
		System.out.println(a);
	}
}
           

 結果:

java并發程式設計--實作可見性的四種可行方案

第二個是system.out.println():

MyVolatile.java:

package cn.mxl.test.wr;


public class MyVolatile {
	private   int a=0;

	public   void write() throws InterruptedException {
		a++;
		System.out.println("目前線程:"+Thread.currentThread().getName());
		System.out.println(a);
	}
	
	public   void read() throws InterruptedException {
		while(a==0) {
//			Thread.sleep(1);
			System.out.println(a);
		}
		System.out.println("目前線程:"+Thread.currentThread().getName());
		System.out.println(a);
	}
}
           

 結果:

java并發程式設計--實作可見性的四種可行方案

 第三個atomic:

MyVolatile.java:

package cn.mxl.test.wr.atomic;

import java.util.concurrent.atomic.AtomicInteger;

public class MyVolatile {
	private   AtomicInteger a=new AtomicInteger();

	public   void write() throws InterruptedException {
		a.incrementAndGet();
		System.out.println("目前線程:"+Thread.currentThread().getName());
		System.out.println(a);
	}
	
	public   void read() throws InterruptedException {
		while(a.get()==0) {
//			Thread.sleep(1);
//			System.out.println(a);
		}
		System.out.println("目前線程:"+Thread.currentThread().getName());
		System.out.println(a);
	}
}
           

結果:

java并發程式設計--實作可見性的四種可行方案

繼續閱讀