首先打印1-100數字如果用一個單線程實現那麼只要一個for循環即可,那麼如果要用兩個線程打印出來呢?(一個線程打印奇數,一個線程打印偶數)於是大家會想到可以通過加鎖實現,但是這樣的效率是不是不高?這裏我用一個變量來控制兩個線程的輸出
public class ThreadTest {
volatile int flag=0;
public void runThread() throws InterruptedException{
Thread t1=new Thread(new Thread1());
Thread t2=new Thread(new Thread2());
t1.start();
t2.start();
}
public class Thread1 implements Runnable{
public void run() {
int i=0;
while(i<=99){
if(flag==0)
{
System.out.println("t1="+i+"flag="+flag);
i+=2;
flag=1;
}
}
}
}
public class Thread2 implements Runnable{
public void run() {
int i=1;
while(i<=99){
if(flag==1)
{
System.out.println("t2="+i+"flag="+flag);
i+=2;
flag=0;
}
}
}
}
}
那麼如果要實現三個線程輪流打印1-100的數字呢?是不是也可以用上面的方法實現呢?代碼如下
public class ThreadTest {
private int i=0;
private Thread thread1,thread2,thread3;
private int flag=0;
public void runThread() throws InterruptedException{
thread1=new Thread(new Thread1());
thread2=new Thread(new Thread2());
thread3=new Thread(new Thread3());
thread1.start();
thread2.start();
thread3.start();
}
public class Thread1 implements Runnable{
public void run() {
while(i<=100){
if(flag==0) {
System.out.println("t1="+i);
i++;
flag=1;
}
}
}
}
public class Thread2 implements Runnable{
public void run() {
while(i<=100){
if(flag==1) {
System.out.println("t2="+i);
i++;
flag=2;
}
}
}
}
public class Thread3 implements Runnable{
public void run() {
while(i<=100){
if(flag==2) {
System.out.println("t3="+i);
i++;
flag=0;
}
}
}
}
}
運行結果
發現三個線程只打印了一次就停止不輸出了,是什麼原因呢?
可以用jdk自帶的jstack來看看線程的狀態,在windows系統中可以打開cmd然後進入jdk所在目錄,然後執行Jsp,能查看到各線程id,然後執行jstack -F pid就可以看的狀態了
可以看到幾個Thread state是BLOCKED,就是阻塞了,什麼原因呢?
尷尬發現flag變量和i變量前面忘記加volatile,導致flag和i被線程讀取修改時,其他線程不可見,所以才導致上面的問題出現。
在JVM中每個線程讀取變量到cache中時相互都是不可見的,也就是java五大內存區中的程序計數器區域對於每個線程都是獨立的不共享的,只有堆內存區和方法區是對所有線程都是共享的。當線程1讀取了flag和i的值,並對其進行修改的時候,線程2併發運行,並不知道flag和i值已經改變,導致多線程數據不一致的情況,所以加了volatile後,當線程讀取變量進行修改後會“通知”其它線程這個值已經進行了修改。
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadTest {
private volatile int i=0;
private Thread thread1,thread2,thread3;
private volatile int flag=0;
public void runThread() throws InterruptedException{
thread1=new Thread(new Thread1());
thread2=new Thread(new Thread2());
thread3=new Thread(new Thread3());
thread1.start();
thread2.start();
thread3.start();
}
public class Thread1 implements Runnable{
public void run() {
while(i<100){
if(flag==0) {
System.out.println("t1="+i);
i++;
flag=1;
}
}
}
}
public class Thread2 implements Runnable{
public void run() {
while(i<100){
if(flag==1){
System.out.println("t2="+i);
i++;
flag=2;
}
}
}
}
public class Thread3 implements Runnable{
public void run() {
while(i<100){
if(flag==2){
System.out.println("t3="+i);
i++;
flag=0;
}
}
}
}
}
運行結果
-----未完-----