天天看點

Java多線程程式設計(volatile)

當涉及到多繼承時,實作Runnable接口而不是繼承Thread類,很有必要。

package com.java.mul;

import com.java.mul.extthread.Alogin;
import com.java.mul.extthread.BLogin;

public class multest {

	public static void main(String[] args) throws InterruptedException {
		// TODO Auto-generated method stub
		PrintString printStringService = new PrintString();
		new Thread(printStringService).start();
		System.out.println("stop="+ Thread.currentThread().getName());
		printStringService.setContinuePrint(false);

	}
}


package com.java.mul;

public class PrintString implements Runnable{
	private boolean isContinuePrint = true;
	public boolean isContinuePrint() {
		return isContinuePrint;
	}
	public void setContinuePrint(boolean isContinuePrint) {
		this.isContinuePrint = isContinuePrint;
	}
	public void printStringMethod() {
		try {
			while(isContinuePrint == true) {
				System.out.println("threadName=" + Thread.currentThread().getName());
				Thread.sleep(1000);
			}
			
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		printStringMethod();
	}
	
}
           

以上執行個體代碼的格式,一旦運作在-server伺服器模式中,64bit的JVM上時,就會出現死循環,解決辦法是用volatile關鍵字。

volatile關鍵字的作用是強制從公共堆棧中取得變量的值,而不是從線程私有資料棧中取得變量的值。

package com.java.mul;

import com.java.mul.extthread.Alogin;
import com.java.mul.extthread.BLogin;

public class multest {

	public static void main(String[] args) throws InterruptedException {
		// TODO Auto-generated method stub
		RunThread thread = new RunThread();
		thread.start();
		Thread.sleep(1000);
		thread.setRunning(false);
		System.out.println("value false");

	}
}

package com.java.mul;

public class RunThread extends Thread{
	private boolean isRunning = true;
	public boolean isRunning() {
		return isRunning;
	}
	public void setRunning(boolean isRunning) {
		this.isRunning = isRunning;
	}
	@Override
	public void run() {
		System.out.println("enter");
		while(isRunning == true) {
			
		}
		System.out.println("stop");
	}
}
           

在啟動RunThread線程時,變量private boolean isRunning = true;存在于公共堆棧及線程的私有堆棧中。在JVM被設定為-server模式時,為了線程運作的效率,線程一直在私有堆棧中取得isRunning的值是true。而代碼thread.setRunning(false);雖然被執行,更新的卻是公共堆棧中的isRunning變量值false,是以一直就是死循環的狀态。解決這樣的問題要用volatile關鍵字,使得線程通路isRunning變量時,強制性從公共堆棧中進行取值。

更改如下

package com.java.mul;

import com.java.mul.extthread.Alogin;
import com.java.mul.extthread.BLogin;

public class multest {

	public static void main(String[] args) throws InterruptedException {
		// TODO Auto-generated method stub
		RunThread thread = new RunThread();
		thread.start();
		Thread.sleep(1000);
		thread.setRunning(false);
		System.out.println("value false");

	}
}

package com.java.mul;

public class RunThread extends Thread{
	volatile private boolean isRunning = true;
	public boolean isRunning() {
		return isRunning;
	}
	public void setRunning(boolean isRunning) {
		this.isRunning = isRunning;
	}
	@Override
	public void run() {
		System.out.println("enter");
		while(isRunning == true) {
			
		}
		System.out.println("stop");
	}
}
           

volatile變量最緻命的缺點是不支援原子性。(隻能用來修飾變量)解決的是可見性問題,而不是原子性問題。

除了可以用synchronized實作原子性,還可以用AtomicInteger

package com.java.mul;

import com.java.mul.extthread.Alogin;
import com.java.mul.extthread.BLogin;

public class multest {

	public static void main(String[] args) throws InterruptedException {
		// TODO Auto-generated method stub
		RunThread countService = new RunThread();
		Thread t1 = new Thread(countService);
		t1.start();
		Thread t2 = new Thread(countService);
		t2.start();
		Thread t3 = new Thread(countService);
		t3.start();
		Thread t4 = new Thread(countService);
		t4.start();
		
		


	}
}

package com.java.mul;

import java.util.concurrent.atomic.AtomicInteger;

public class RunThread extends Thread{
	private AtomicInteger count = new AtomicInteger(0);


	@Override
	public void run() {
		for(int i = 0;i<10000;i++) {
			System.out.println(count.incrementAndGet());
		}
	}
}
           

synchronized的可見性:

package com.java.mul;

public class Service {
	private boolean isCon = true;
	public void runMethod() {
		while(isCon==true) {
			
		}
		System.out.print("stop");
	}
	public void stopM() {
		isCon = false;
	}

}

package com.java.mul;

public class MyThreadA extends Thread{
	private Service service;
	public MyThreadA(Service service) {
		super();
		this.service = service;
	}
	
	@Override
	public void run() {
		service.runMethod();
	}
}

package com.java.mul;

public class MyThreadB extends Thread{
	private Service service;
	public MyThreadB(Service service) {
		super();
		this.service = service;
	}
	
	@Override
	public void run() {
		service.stopM();
	}
}

package com.java.mul;

import com.java.mul.extthread.Alogin;
import com.java.mul.extthread.BLogin;

public class multest {

	public static void main(String[] args) throws InterruptedException {
		// TODO Auto-generated method stub
		Service service = new Service();
		MyThreadA a = new MyThreadA(service);
		a.start();
		Thread.sleep(1000);
		MyThreadB b = new MyThreadB(service);
		b.start();
		System.out.println("stop send");
		
		


	}
}
           

這個代碼,MyThreadB并沒有将isCon變為false,因為各線程之間,資料沒有可視性。是以可以加關鍵字。

package com.java.mul;

public class Service {
	private boolean isCon = true;
	private Object o = new Object();
	public void runMethod() {
		while(isCon==true) {
			synchronized(o) {
				
			}
		}
		System.out.print("stop");
	}
	public void stopM() {
		isCon = false;
	}

}
           

synchronized有兩個特征:互斥性和可見性。不僅可以解決一個線程看到對象處于不一緻的狀态,還可以保證進入同步方法或者同步代碼塊的每個線程,都看到由同一個鎖保護之前所有的修飾效果。