多線程程式設計主要學的就是"互斥"和"可見","互斥"指的就是就是同步,而"可見"想必很多人還沒有了解.
在方法或者變量已經同步的情況下,還會出現什麼問題嗎?
舉個例子:
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();
}
}
運作結果:
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);
}
}
結果:
其實在這個過程中,我遇到了一些有趣的事情,可見性這東西,其實還有三個東西可以實作:
第一個-->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);
}
}
結果:
第二個是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);
}
}
結果:
第三個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);
}
}
結果: