Java實現多線程輪流打印1-100的數字

首先打印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;
				}
			}
		}
		
	}
}

運行結果

 

-----未完-----

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章