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()方法之前發生的,
所以還是存在非執行緒安全問題的概率。為了防止發生這個問題,還是繼續使用同步方法。
完!