1. 程式人生 > >Java執行緒入門一

Java執行緒入門一

1. 使用多執行緒 繼承Thread類

public class MyThread extends Thread {
	
    @Override
    public void run() {
        System.out.println("MyThread.run");
    }
}
public class Demo01 {

    public static void main(String[] args) {

        MyThread t = new MyThread();
        t.start();
        //多次呼叫 start()方法會出現 如下異常
//t.start();java.lang.IllegalThreadStateException System.out.println("Demo01.main"); } }

輸出結果如下:執行緒執行的隨機性
Demo01.main
MyThread.run

說明執行緒呼叫的隨機性:

public class MyThread extends Thread {

    @Override
    public void run() {
        try {

            for (int i=0;i<10;i++){
                int
time = (int) (Math.random()*1000); Thread.sleep(time); System.out.println("run:"+Thread.currentThread().getName()); } }catch (InterruptedException e){ e.printStackTrace(); } } } public class Demo01 { public static void main
(String[] args) { try { MyThread t = new MyThread(); t.setName("myThread"); t.start(); for (int i=0;i<10;i++){ int time = (int) (Math.random()*1000); Thread.sleep(time); System.out.println("run:"+Thread.currentThread().getName()); } }catch (InterruptedException e){ e.printStackTrace(); } System.out.println("Demo01.main"); } }

結果如下:
run:myThread
run:myThread
run:main
run:main
run:main
run:myThread
run:main
run:myThread
run:main
run:main
run:main
run:main
run:myThread
run:main
run:main
Demo01.main
run:myThread
run:myThread
run:myThread
run:myThread
run:myThread

說明:執行start()的順序不代表執行緒啟動的順序,程式碼如下:

public class MyThread extends Thread {
    private int i;

    public MyThread(int i) {
        this.i = i;
    }

    @Override
    public void run() {
        System.out.println(i);
    }
}
public class Demo01 {

    public static void main(String[] args) {

        new MyThread(1).start();
        new MyThread(2).start();
        new MyThread(3).start();
        new MyThread(4).start();
        new MyThread(5).start();
        new MyThread(6).start();
        new MyThread(7).start();
        new MyThread(8).start();
        new MyThread(9).start();
        new MyThread(10).start();
        new MyThread(11).start();

        System.out.println("Demo01.main");
    }
}

執行結果為:
Demo01.main
1
2
4
3
5
7
8
9
11
6
10

2. 實現Runnable介面略去

3. 例項變數和執行緒安全

  • 例項變數不共享的情況
public class MyThread extends Thread {
		private int count = 5;

		public MyThread(String name) {
			this.setName(name);
		}

		@Override
		public void run() {

			while (count > 0) {
				count--;
				System.out.println("由 " + this.currentThread().getName() + "計算,count=" + count);

			}
		}
	}
	public class Demo01 {

		public static void main(String[] args) {

			new MyThread("A").start();
			new MyThread("B").start();
			new MyThread("C").start();


			System.out.println("Demo01.main");
		}
	}

執行結果為:
Demo01.main
由 A計算,count=4
由 A計算,count=3
由 A計算,count=2
由 A計算,count=1
由 A計算,count=0
由 B計算,count=4
由 B計算,count=3
由 B計算,count=2
由 B計算,count=1
由 B計算,count=0
由 C計算,count=4
由 C計算,count=3
由 C計算,count=2
由 C計算,count=1
由 C計算,count=0

這裡一共建立了三個執行緒,每個執行緒都有自己各自的count變數,自己減少自己的count變數值
這樣的情況就是變數不共享。

如果想實現三個執行緒對同一個count變數進行減法操作,如果實現?

  • 共享資料的情況
public class MyThread extends Thread {
		private int count = 5;

		@Override
		public void run() {

			count--;
			System.out.println("由 " + this.currentThread().getName() + "計算,count=" + count);

		}
	}
	public class Demo01 {
		public static void main(String[] args) {
			MyThread t = new MyThread();
			new Thread(t,"A").start();
			new Thread(t,"B").start();
			new Thread(t,"C").start();
			new Thread(t,"D").start();
			new Thread(t,"E").start();
			System.out.println("Demo01.main");
		}
	}
	

執行結果如下:
Demo01.main
由 A計算,count=4
由 B計算,count=3
由 D計算,count=1
由 E計算,count=0
由 C計算,count=2

結果和預期不一樣,這就是"非執行緒安全"的問題

"非執行緒安全"的問題: 主要是指多個執行緒對同一個物件中的同一個例項變數進行操作時
會出現某值被更改,值不同步的情況,進而影響執行流程。

在某些JVM中,i–的操作分成如下3步:
a. 取得原有的i值
b. 計算i-1
c. 對i進行賦值

在這3個步驟中,如果多個執行緒同時訪問,那麼一定會出現"非執行緒安全"的問題

修改為如下程式碼即可:

public class MyThread extends Thread {
	private int count = 5;

	@Override
	synchronized public void run() {

		count--;
		System.out.println("由 " + this.currentThread().getName() + "計算,count=" + count);

	}
}

模擬生成環境的非執行緒安全問題:

public class LoginServlet {

    private static String usernameRef;
    private static String passwordRef;

    public static void doPOst(String username,String password){
        try {
            usernameRef = username;

            if(username.equals("a")){
                Thread.sleep(5000);
            }
            passwordRef = password;

            System.out.println("username:"+usernameRef+" password:"+passwordRef);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}
public class ALogin extends Thread{

    @Override
    public void run() {
        LoginServlet.doPOst("a","aa");
    }
}
public class BLogin extends Thread {

    @Override
    public void run() {

        LoginServlet.doPOst("b","bb");
    }
}
public class Demo01 {

    public static void main(String[] args) {

        ALogin a = new ALogin();
        a.start();


        BLogin b = new BLogin();
        b.start();

        System.out.println("Demo01.main");
    }
}

輸出結果為:
Demo01.main
username:b password:bb
username:b password:aa

解決問題:使用synchronized 關鍵字

synchronized public static void doPOst(String username,String password){
	//同上,略去
}

4. i-- 和 System.out.println 的異常

細化以下println()和i++聯合使用時"有可能"出現的另外一種異常情況。
程式碼如下:

public class MyThread extends Thread {
    private int i = 5;

    @Override
    public void run() {

        //i-- 放在了 println方法中執行
        System.out.println("i= "+(i--) +" threadName="+ MyThread.currentThread().getName());
    }
}
public class Demo01 {

    public static void main(String[] args) {

        MyThread run = new MyThread();

        for (int i=0;i<5;i++){
            new Thread(run).start();
        }
    }
}

輸出結果如下:
i= 5 threadName=Thread-2
i= 4 threadName=Thread-3
i= 3 threadName=Thread-4
i= 5 threadName=Thread-1
i= 2 threadName=Thread-5

為什麼呢?雖然println方法時同步的,但是i–操作是在進入println()方法之前發生的,
所以還是存在非執行緒安全問題的概率。為了防止發生這個問題,還是繼續使用同步方法。

完!