當涉及到多繼承時,實作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有兩個特征:互斥性和可見性。不僅可以解決一個線程看到對象處于不一緻的狀态,還可以保證進入同步方法或者同步代碼塊的每個線程,都看到由同一個鎖保護之前所有的修飾效果。