java 多線程重溫

之前犯懶一直沒有開過博客,然後一直學一直丟一直在重新學。。。重新學習的過程中找資源是很艱難的,所以今天開通了博客,希望能每天進步一點點,每天更快樂一點點。

-------------------------------------------------------------------------------------------------------------------------------------------------

原博客地址:http://www.cnblogs.com/rollenholt/archive/2011/08/28/2156357.html

2011-08-28 20:08 by Rollen Holt, 363546 閱讀, 89 評論, 收藏編輯

以前沒有寫筆記的習慣,現在慢慢的發現及時總結是多麼的重要了,呵呵。雖然才大二,但是也快要畢業了,要加油了。


                                                                              java中的多線程

在java中要想實現多線程,有兩種手段,一種是繼續Thread類,另外一種是實現Runable接口。

對於直接繼承Thread的類來說,代碼大致框架是:

1
2
3
4
5
6
7
8
9
10
11
12
class 類名 extends Thread{
方法1;
方法2
public void run(){
// other code…
}
屬性1
屬性2
 
}

先看一個簡單的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
 * @author Rollen-Holt 繼承Thread類,直接調用run方法
 * */
class hello extends Thread {
 
    public hello() {
 
    }
 
    public hello(String name) {
        this.name = name;
    }
 
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(name + "運行     " + i);
        }
    }
 
    public static void main(String[] args) {
        hello h1=new hello("A");
        hello h2=new hello("B");
        h1.run();
        h2.run();
    }
 
    private String name;
}

【運行結果】:

A運行     0

A運行     1

A運行     2

A運行     3

A運行     4

B運行     0

B運行     1

B運行     2

B運行     3

B運行     4

我們會發現這些都是順序執行的,說明我們的調用方法不對,應該調用的是start()方法。

當我們把上面的主函數修改爲如下所示的時候:

1
2
3
4
5
6
public static void main(String[] args) {
        hello h1=new hello("A");
        hello h2=new hello("B");
        h1.start();
        h2.start();
    }

然後運行程序,輸出的可能的結果如下:

A運行     0

B運行     0

B運行     1

B運行     2

B運行     3

B運行     4

A運行     1

A運行     2

A運行     3

A運行     4

因爲需要用到CPU的資源,所以每次的運行結果基本是都不一樣的,呵呵。

注意:雖然我們在這裏調用的是start()方法,但是實際上調用的還是run()方法的主體。

那麼:爲什麼我們不能直接調用run()方法呢?

我的理解是:線程的運行需要本地操作系統的支持。

如果你查看start的源代碼的時候,會發現:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public synchronized void start() {
        /**
     * This method is not invoked for the main method thread or "system"
     * group threads created/set up by the VM. Any new functionality added
     * to this method in the future may have to also be added to the VM.
     *
     * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0 || this != me)
            throw new IllegalThreadStateException();
        group.add(this);
        start0();
        if (stopBeforeStart) {
        stop0(throwableFromStop);
    }
}
private native void start0();

注意我用紅色加粗的那一條語句,說明此處調用的是start0()。並且這個這個方法用了native關鍵字,次關鍵字表示調用本地操作系統的函數。因爲多線程的實現需要本地操作系統的支持。

但是start方法重複調用的話,會出現java.lang.IllegalThreadStateException異常。

通過實現Runnable接口:

 

大致框架是:

1
2
3
4
5
6
7
8
9
10
11
12
class 類名 implements Runnable{
方法1;
方法2
public void run(){
// other code…
}
屬性1
屬性2
 
}

 

來先看一個小例子吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
 * @author Rollen-Holt 實現Runnable接口
 * */
class hello implements Runnable {
 
    public hello() {
 
    }
 
    public hello(String name) {
        this.name = name;
    }
 
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(name + "運行     " + i);
        }
    }
 
    public static void main(String[] args) {
        hello h1=new hello("線程A");
        Thread demo= new Thread(h1);
        hello h2=new hello("線程B");
        Thread demo1=new Thread(h2);
        demo.start();
        demo1.start();
    }
 
    private String name;
}

【可能的運行結果】:

線程A運行     0

線程B運行     0

線程B運行     1

線程B運行     2

線程B運行     3

線程B運行     4

線程A運行     1

線程A運行     2

線程A運行     3

線程A運行     4

 

關於選擇繼承Thread還是實現Runnable接口?

其實Thread也是實現Runnable接口的

1
2
3
4
5
6
7
8
class Thread implements Runnable {
    //…
public void run() {
        if (target != null) {
             target.run();
        }
        }
}

其實Thread中的run方法調用的是Runnable接口的run方法。不知道大家發現沒有,Thread和Runnable都實現了run方法,這種操作模式其實就是代理模式。關於代理模式,我曾經寫過一個小例子呵呵,大家有興趣的話可以看一下:http://www.cnblogs.com/rollenholt/archive/2011/08/18/2144847.html

Thread和Runnable的區別:

如果一個類繼承Thread,則不適合資源共享。但是如果實現了Runable接口的話,則很容易的實現資源共享。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
 * @author Rollen-Holt 繼承Thread類,不能資源共享
 * */
class hello extends Thread {
    public void run() {
        for (int i = 0; i < 7; i++) {
            if (count > 0) {
                System.out.println("count= " + count--);
            }
        }
    }
 
    public static void main(String[] args) {
        hello h1 = new hello();
        hello h2 = new hello();
        hello h3 = new hello();
        h1.start();
        h2.start();
        h3.start();
    }
 
    private int count = 5;
}

 

【運行結果】:

count= 5

count= 4

count= 3

count= 2

count= 1

count= 5

count= 4

count= 3

count= 2

count= 1

count= 5

count= 4

count= 3

count= 2

count= 1

大家可以想象,如果這個是一個買票系統的話,如果count表示的是車票的數量的話,說明並沒有實現資源的共享。

我們換爲Runnable接口

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class MyThread implements Runnable{
 
    private int ticket = 5;  //5張票
 
    public void run() {
        for (int i=0; i<=20; i++) {
            if (this.ticket > 0) {
                System.out.println(Thread.currentThread().getName()+ "正在賣票"+this.ticket--);
            }
        }
    }
}
public class lzwCode {
     
    public static void main(String [] args) {
        MyThread my = new MyThread();
        new Thread(my, "1號窗口").start();
        new Thread(my, "2號窗口").start();
        new Thread(my, "3號窗口").start();
    }
}

 

  

 

 

【運行結果】:

count= 5

count= 4

count= 3

count= 2

count= 1

 

總結一下吧:

實現Runnable接口比繼承Thread類所具有的優勢:

1):適合多個相同的程序代碼的線程去處理同一個資源

2):可以避免java中的單繼承的限制

3):增加程序的健壯性,代碼可以被多個線程共享,代碼和數據獨立。

 

所以,本人建議大家勁量實現接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
 * @author Rollen-Holt
 * 取得線程的名稱
 * */
class hello implements Runnable {
    public void run() {
        for (int i = 0; i < 3; i++) {
            System.out.println(Thread.currentThread().getName());
        }
    }
 
    public static void main(String[] args) {
        hello he = new hello();
        new Thread(he,"A").start();
        new Thread(he,"B").start();
        new Thread(he).start();
    }
}

【運行結果】:

A

A

A

B

B

B

Thread-0

Thread-0

Thread-0

說明如果我們沒有指定名字的話,系統自動提供名字。

提醒一下大家:main方法其實也是一個線程。在java中所以的線程都是同時啓動的,至於什麼時候,哪個先執行,完全看誰先得到CPU的資源。

 

java中,每次程序運行至少啓動2個線程。一個是main線程,一個是垃圾收集線程。因爲每當使用java命令執行一個類的時候,實際上都會啓動一個JVM,每一個jVM實際在就是在操作系統中啓動了一個進程。

判斷線程是否啓動

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
 * @author Rollen-Holt 判斷線程是否啓動
 * */
class hello implements Runnable {
    public void run() {
        for (int i = 0; i < 3; i++) {
            System.out.println(Thread.currentThread().getName());
        }
    }
 
    public static void main(String[] args) {
        hello he = new hello();
        Thread demo = new Thread(he);
        System.out.println("線程啓動之前---》" + demo.isAlive());
        demo.start();
        System.out.println("線程啓動之後---》" + demo.isAlive());
    }
}

【運行結果】

線程啓動之前---》false

線程啓動之後---》true

Thread-0

Thread-0

Thread-0

主線程也有可能在子線程結束之前結束。並且子線程不受影響,不會因爲主線程的結束而結束。

 

線程的強制執行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
     * @author Rollen-Holt 線程的強制執行
     * */
    class hello implements Runnable {
        public void run() {
            for (int i = 0; i < 3; i++) {
                System.out.println(Thread.currentThread().getName());
            }
        }
     
        public static void main(String[] args) {
            hello he = new hello();
            Thread demo = new Thread(he,"線程");
            demo.start();
            for(int i=0;i<50;++i){
                if(i>10){
                    try{
                        demo.join();  //強制執行demo
                    }catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("main 線程執行-->"+i);
            }
        }
    }

【運行的結果】:

main 線程執行-->0

main 線程執行-->1

main 線程執行-->2

main 線程執行-->3

main 線程執行-->4

main 線程執行-->5

main 線程執行-->6

main 線程執行-->7

main 線程執行-->8

main 線程執行-->9

main 線程執行-->10

線程

線程

線程

main 線程執行-->11

main 線程執行-->12

main 線程執行-->13

...

 

線程的休眠:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
 * @author Rollen-Holt 線程的休眠
 * */
class hello implements Runnable {
    public void run() {
        for (int i = 0; i < 3; i++) {
            try {
                Thread.sleep(2000);
            catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + i);
        }
    }
 
    public static void main(String[] args) {
        hello he = new hello();
        Thread demo = new Thread(he, "線程");
        demo.start();
    }
}

【運行結果】:(結果每隔2s輸出一個)

線程0

線程1

線程2

 

線程的中斷:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
 * @author Rollen-Holt 線程的中斷
 * */
class hello implements Runnable {
    public void run() {
        System.out.println("執行run方法");
        try {
            Thread.sleep(10000);
            System.out.println("線程完成休眠");
        catch (Exception e) {
            System.out.println("休眠被打斷");
            return;  //返回到程序的調用處
        }
        System.out.println("線程正常終止");
    }
 
    public static void main(String[] args) {
        hello he = new hello();
        Thread demo = new Thread(he, "線程");
        demo.start();
        try{
            Thread.sleep(2000);
        }catch (Exception e) {
            e.printStackTrace();
        }
        demo.interrupt(); //2s後中斷線程
    }
}

【運行結果】:

執行run方法

休眠被打斷

 

java程序中,只要前臺有一個線程在運行,整個java程序進程不會小時,所以此時可以設置一個後臺線程,這樣即使java進程小時了,此後臺線程依然能夠繼續運行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
 * @author Rollen-Holt 後臺線程
 * */
class hello implements Runnable {
    public void run() {
        while (true) {
            System.out.println(Thread.currentThread().getName() + "在運行");
        }
    }
 
    public static void main(String[] args) {
        hello he = new hello();
        Thread demo = new Thread(he, "線程");
        demo.setDaemon(true);
        demo.start();
    }
}

雖然有一個死循環,但是程序還是可以執行完的。因爲在死循環中的線程操作已經設置爲後臺運行了。

線程的優先級:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
 * @author Rollen-Holt 線程的優先級
 * */
class hello implements Runnable {
    public void run() {
        for(int i=0;i<5;++i){
            System.out.println(Thread.currentThread().getName()+"運行"+i);
        }
    }
 
    public static void main(String[] args) {
        Thread h1=new Thread(new hello(),"A");
        Thread h2=new Thread(new hello(),"B");
        Thread h3=new Thread(new hello(),"C");
        h1.setPriority(8);
        h2.setPriority(2);
        h3.setPriority(6);
        h1.start();
        h2.start();
        h3.start();
         
    }
}

 

【運行結果】:

A運行0

A運行1

A運行2

A運行3

A運行4

B運行0

C運行0

C運行1

C運行2

C運行3

C運行4

B運行1

B運行2

B運行3

B運行4

。但是請讀者不要誤以爲優先級越高就先執行。誰先執行還是取決於誰先去的CPU的資源、

 

另外,主線程的優先級是5.

線程的禮讓。

在線程操作中,也可以使用yield()方法,將一個線程的操作暫時交給其他線程執行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
 * @author Rollen-Holt 線程的優先級
 * */
class hello implements Runnable {
    public void run() {
        for(int i=0;i<5;++i){
            System.out.println(Thread.currentThread().getName()+"運行"+i);
            if(i==3){
                System.out.println("線程的禮讓");
                Thread.currentThread().yield();
            }
        }
    }
 
    public static void main(String[] args) {
        Thread h1=new Thread(new hello(),"A");
        Thread h2=new Thread(new hello(),"B");
        h1.start();
        h2.start();
         
    }
}

A運行0

A運行1

A運行2

A運行3

線程的禮讓

A運行4

B運行0

B運行1

B運行2

B運行3

線程的禮讓

B運行4

 

 

同步和死鎖:

【問題引出】:比如說對於買票系統,有下面的代碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
 * @author Rollen-Holt
 * */
class hello implements Runnable {
    public void run() {
        for(int i=0;i<10;++i){
            if(count>0){
                try{
                    Thread.sleep(1000);
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
                System.out.println(count--);
            }
        }
    }
 
    public static void main(String[] args) {
        hello he=new hello();
        Thread h1=new Thread(he);
        Thread h2=new Thread(he);
        Thread h3=new Thread(he);
        h1.start();
        h2.start();
        h3.start();
    }
    private int count=5;
}

【運行結果】:

5

4

3

2

1

0

-1

這裏出現了-1,顯然這個是錯的。,應該票數不能爲負值。

如果想解決這種問題,就需要使用同步。所謂同步就是在統一時間段中只有有一個線程運行,

其他的線程必須等到這個線程結束之後才能繼續執行。

【使用線程同步解決問題】

採用同步的話,可以使用同步代碼塊和同步方法兩種來完成。

 

【同步代碼塊】:

語法格式:

synchronized(同步對象){

 //需要同步的代碼

}

但是一般都把當前對象this作爲同步對象。

比如對於上面的買票的問題,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
 * @author Rollen-Holt
 * */
class hello implements Runnable {
    public void run() {
        for(int i=0;i<10;++i){
            synchronized (this) {
                if(count>0){
                    try{
                        Thread.sleep(1000);
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                    System.out.println(count--);
                }
            }
        }
    }
 
    public static void main(String[] args) {
        hello he=new hello();
        Thread h1=new Thread(he);
        Thread h2=new Thread(he);
        Thread h3=new Thread(he);
        h1.start();
        h2.start();
        h3.start();
    }
    private int count=5;
}

【運行結果】:(每一秒輸出一個結果)

5

4

3

2

1

【同步方法】

也可以採用同步方法。

語法格式爲synchronized 方法返回類型方法名(參數列表){

    // 其他代碼

}

現在,我們採用同步方法解決上面的問題。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
 * @author Rollen-Holt
 * */
class hello implements Runnable {
    public void run() {
        for (int i = 0; i < 10; ++i) {
            sale();
        }
    }
 
    public synchronized void sale() {
        if (count > 0) {
            try {
                Thread.sleep(1000);
            catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(count--);
        }
    }
 
    public static void main(String[] args) {
        hello he = new hello();
        Thread h1 = new Thread(he);
        Thread h2 = new Thread(he);
        Thread h3 = new Thread(he);
        h1.start();
        h2.start();
        h3.start();
    }
 
    private int count = 5;
}

【運行結果】(每秒輸出一個)

5

4

3

2

1

提醒一下,當多個線程共享一個資源的時候需要進行同步,但是過多的同步可能導致死鎖。

此處列舉經典的生產者和消費者問題。

【生產者和消費者問題】

先看一段有問題的代碼。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
class Info {
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public int getAge() {
        return age;
    }
 
    public void setAge(int age) {
        this.age = age;
    }
 
    private String name = "Rollen";
    private int age = 20;
}
 
/**
 * 生產者
 * */
class Producer implements Runnable{
    private Info info=null;
    Producer(Info info){
        this.info=info;
    }
     
    public void run(){
        boolean flag=false;
        for(int i=0;i<25;++i){
            if(flag){
                this.info.setName("Rollen");
                try{
                    Thread.sleep(100);
                }catch (Exception e) {
                    e.printStackTrace();
                }
                this.info.setAge(20);
                flag=false;
            }else{
                this.info.setName("chunGe");
                try{
                    Thread.sleep(100);
                }catch (Exception e) {
                    e.printStackTrace();
                }
                this.info.setAge(100);
                flag=true;
            }
        }
    }
}
/**
 * 消費者類
 * */
class Consumer implements Runnable{
    private Info info=null;
    public Consumer(Info info){
        this.info=info;
    }
     
    public void run(){
        for(int i=0;i<25;++i){
            try{
                Thread.sleep(100);
            }catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(this.info.getName()+"<---->"+this.info.getAge());
        }
    }
}
 
/**
 * 測試類
 * */
class hello{
    public static void main(String[] args) {
        Info info=new Info();
        Producer pro=new Producer(info);
        Consumer con=new Consumer(info);
        new Thread(pro).start();
        new Thread(con).start();
    }
}

【運行結果】:

Rollen<---->100

chunGe<---->20

chunGe<---->100

Rollen<---->100

chunGe<---->20

Rollen<---->100

Rollen<---->100

Rollen<---->100

chunGe<---->20

chunGe<---->20

chunGe<---->20

Rollen<---->100

chunGe<---->20

Rollen<---->100

chunGe<---->20

Rollen<---->100

chunGe<---->20

Rollen<---->100

chunGe<---->20

Rollen<---->100

chunGe<---->20

Rollen<---->100

chunGe<---->20

Rollen<---->100

chunGe<---->20

大家可以從結果中看到,名字和年齡並沒有對於。

 

那麼如何解決呢?

1)加入同步

2)加入等待和喚醒

先來看看加入同步會是如何。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
class Info {
     
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public int getAge() {
        return age;
    }
 
    public void setAge(int age) {
        this.age = age;
    }
 
    public synchronized void set(String name, int age){
        this.name=name;
        try{
            Thread.sleep(100);
        }catch (Exception e) {
            e.printStackTrace();
        }
        this.age=age;
    }
     
    public synchronized void get(){
        try{
            Thread.sleep(100);
        }catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(this.getName()+"<===>"+this.getAge());
    }
    private String name = "Rollen";
    private int age = 20;
}
 
/**
 * 生產者
 * */
class Producer implements Runnable {
    private Info info = null;
 
    Producer(Info info) {
        this.info = info;
    }
 
    public void run() {
        boolean flag = false;
        for (int i = 0; i < 25; ++i) {
            if (flag) {
                 
                this.info.set("Rollen"20);
                flag = false;
            else {
                this.info.set("ChunGe"100);
                flag = true;
            }
        }
    }
}
 
/**
 * 消費者類
 * */
class Consumer implements Runnable {
    private Info info = null;
 
    public Consumer(Info info) {
        this.info = info;
    }
 
    public void run() {
        for (int i = 0; i < 25; ++i) {
            try {
                Thread.sleep(100);
            catch (Exception e) {
                e.printStackTrace();
            }
            this.info.get();
        }
    }
}
 
/**
 * 測試類
 * */
class hello {
    public static void main(String[] args) {
        Info info = new Info();
        Producer pro = new Producer(info);
        Consumer con = new Consumer(info);
        new Thread(pro).start();
        new Thread(con).start();
    }
}

【運行結果】:

Rollen<===>20

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

Rollen<===>20

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

ChunGe<===>100

從運行結果來看,錯亂的問題解決了,現在是Rollen 對應20,ChunGe對於100

,但是還是出現了重複讀取的問題,也肯定有重複覆蓋的問題。如果想解決這個問題,就需要使用Object類幫忙了、

,我們可以使用其中的等待和喚醒操作。

要完成上面的功能,我們只需要修改Info類飢渴,在其中加上標誌位,並且通過判斷標誌位完成等待和喚醒的操作,代碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
class Info {
     
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public int getAge() {
        return age;
    }
 
    public void setAge(int age) {
        this.age = age;
    }
 
    public synchronized void set(String name, int age){
        if(!flag){
            try{
                super.wait();
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
        this.name=name;
        try{
            Thread.sleep(100);
        }catch (Exception e) {
            e.printStackTrace();
        }
        this.age=age;
        flag=false;
        super.notify();
    }
     
    public synchronized void get(){
        if(flag){
            try{
                super.wait();
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
         
        try{
            Thread.sleep(100);
        }catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(this.getName()+"<===>"+this.getAge());
        flag=true;
        super.notify();
    }
    private String name = "Rollen";
    private int age = 20;
    private boolean flag=false;
}
 
/**
 * 生產者
 * */
class Producer implements Runnable {
    private Info info = null;
 
    Producer(Info info) {
        this.info = info;
    }
 
    public void run() {
        boolean flag = false;
        for (int i = 0; i < 25; ++i) {
            if (flag) {
                 
                this.info.set("Rollen"20);
                flag = false;
            else {
                this.info.set("ChunGe"100);
                flag = true;
            }
        }
    }
}
 
/**
 * 消費者類
 * */
class Consumer implements Runnable {
    private Info info = null;
 
    public Consumer(Info info) {
        this.info = info;
    }
 
    public void run() {
        for (int i = 0; i < 25; ++i) {
            try {
                Thread.sleep(100);
            catch (Exception e) {
                e.printStackTrace();
            }
            this.info.get();
        }
    }
}
 
/**
 * 測試類
 * */
class hello {
    public static void main(String[] args) {
        Info info = new Info();
        Producer pro = new Producer(info);
        Consumer con = new Consumer(info);
        new Thread(pro).start();
        new Thread(con).start();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
【程序運行結果】:
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
ChunGe<===>100
Rollen<===>20
先在看結果就可以知道,之前的問題完全解決。

《完》

PS(寫在後面):

本人深知學的太差,所以希望大家能多多指點。另外,關於多線程其實有很多的知識,由於目前我也就知道的不太多,寫了一些常用的。雖然在操作系統這門課上學了很多的線程和進程,比如銀行家算法等等的,以後有時間在補充,大家有什麼好資料可以留個言,大家一起分享一下,謝謝了。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


============================================================================== 
我喜歡程序員,他們單純、固執、容易體會到成就感;面對壓力,能夠挑燈夜戰不眠不休;面對困難,能夠迎難而上挑戰自我。他
們也會感到困惑與傍徨,但每個程序員的心中都有一個比爾蓋茨或是喬布斯的夢想“用智慧開創屬於自己的事業”。我想說的是,其
實我是一個程序員

============================================================================== 
77
0
(請您對文章做出評價)
« 上一篇:棋盤覆蓋--遞歸分治java實現
» 下一篇:java中類的生命週期
ADD YOUR COMMENT
  1. #51樓 白開水加糖  2013-09-28 21:25
    @yyg1982
    會不會是單核CPU執行的時候不行? 我覺得樓主寫的麼錯啊
  2. #52樓 shuyy_com  2013-10-28 13:26
    寫得很好,謝謝,辛苦了
  3. #53樓 沙漠中的大奔  2013-11-02 22:07
    樓主 爲什麼不學java了呢?
  4. #54樓[樓主] Rollen Holt  2013-11-04 10:26
    @沙漠中的大奔
    工作的關係,哈哈
  5. #55樓 你的名字我的姓氏  2013-11-28 16:21
    不錯,講的很細
  6. #56樓 道上的追逐  2013-12-26 09:36
    不錯很詳細,適合我們這些新手
  7. #57樓 會呼吸的魚  2013-12-27 11:40
    很好,lz辛苦了
  8. #58樓 fkblog.org  2014-01-13 09:50
    不錯,我也推薦個Java多線程入門講解:http://www.w3cschool.cc/java/ava-multithreading.html
  9. #59樓 reiben  2014-01-28 16:41
    public class lzwCode {
         
        public static void main(String [] args) {
            MyThread my = new MyThread();
            new Thread(my, "1號窗口").start();
            new Thread(my, "2號窗口").start();
            new Thread(my, "3號窗口").start();
        }
    }

    這個分配了3個線程對象,但運行的都是同一個線程MyThread,內存中就一份COUNT=5.

    class hello extends Thread {
    public void run() {
    for (int i = 0; i < 7; i++) {
    if (count > 0) {
    System.out.println("count= " + count--);
    }
    }
    }

    public static void main(String[] args) {
    hello h1 = new hello();
    hello h2 = new hello();
    hello h3 = new hello();
    h1.start();
    h2.start();
    h3.start();
    }

    private int count = 5;
    }

    這個新建了3個線程,內存中有三份COUNT=5.

    跑出來的結果自然不一樣了。是可以共享的。
  10. #60樓 reiben  2014-01-28 16:43
    class hello extends Thread {
    public void run() {
    for (int i = 0; i < 7; i++) {
    if (count > 0) {
    System.out.println("count= " + count--);
    }
    }
    }

    public static void main(String[] args) {
    hello h1 = new hello();
    hello h2 = new hello();
    hello h3 = new hello();
    h1.start();
    h2.start();
    h3.start();
    }

    private int count = 5;
    }


    private int count = 5;
    改成
    private static int count = 5;試試
  11. #61樓 Vitt不再孤單  2014-01-30 00:14
    每次看到你們這樣的筆記就會想,當初自己不學測控多好····媽蛋····
  12. #62樓 BIGBIRD大鳥  2014-02-10 22:41
    寫的真好! 很詳細了!
  13. #63樓 無翼神  2014-02-13 20:03
    寫的不錯,例子也不錯
  14. #64樓 simpman  2014-02-27 15:48
    大家可以想象,如果這個是一個買票系統的話,如果count表示的是車票的數量的話,說明並沒有實現資源的共享。
    我們換爲Runnable接口

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    class MyThread implements Runnable{

    private int ticket = 5; //5張票

    public void run() {
    for (int i=0; i<=20; i++) {
    if (this.ticket > 0) {
    System.out.println(Thread.currentThread().getName()+ "正在賣票"+this.ticket--);
    }
    }
    }
    }
    public class lzwCode {

    public static void main(String [] args) {
    MyThread my = new MyThread();
    new Thread(my, "1號窗口").start();
    new Thread(my, "2號窗口").start();
    new Thread(my, "3號窗口").start();
    }
    }

    這個也會出現兩個窗口賣同一張票的情況。應該用synchronized給代碼加鎖吧。
  15. #65樓 Bactryki  2014-04-18 11:29
    真棒!
  16. #66樓 beyondchina  2014-04-21 13:46
    mark. 寫得很不錯哦。
  17. #67樓 小咚  2014-04-22 17:24
    public synchronized void set(String name, int age){
    if(!flag){
    try{
    super.wait();
    }catch (Exception e) {
    e.printStackTrace();
    }
    }
    this.name=name;
    try{
    Thread.sleep(100);
    }catch (Exception e) {
    e.printStackTrace();
    }
    this.age=age;
    flag=false;
    super.notify();
    }

    public synchronized void get(){
    if(flag){
    try{
    super.wait();
    }catch (Exception e) {
    e.printStackTrace();
    }
    }

    try{
    Thread.sleep(100);
    }catch (Exception e) {
    e.printStackTrace();
    }
    System.out.println(this.getName()+"<===>"+this.getAge());
    flag=true;
    super.notify();
    }
    private String name = "Rollen";
    private int age = 20;
    private boolean flag=false;
    }

    這個生產者和消費者的判斷應該反了把
    生產應該是set 應該是!flag 沒有就創建 然後激活get 有就等待消費
    消費應該是get 應該是flag 有就消費 然後激活set 沒有就等待生產
  18. #68樓 拉姆塞  2014-04-23 09:14
    學習了,謝謝
  19. #69樓 youngjoy  2014-05-17 00:15
    寫的太好。學習。膜拜。
  20. #70樓 水瀾。影雪  2014-05-19 10:30
    很好,非常適合我們新手~
  21. #71樓 xmu_lomo  2014-05-21 08:44
    樓主在線程的強制執行那段是怎麼樣得到那樣的結果的...我的至少得在run中執行5次及5次以上纔可能在main線程10以後join, 0-0樓主的輸出結果概率略小呀,...
  22. #72樓 Me.thinking  2014-05-21 16:12
    good
  23. #73樓 秦寅  2014-05-29 17:39
    樓主在說明多線程兩種實現方式資源共享的時候,demo賣票是錯誤的。
    class hello extends Thread {
    public void run() {
    for (int i = 0; i < 7; i++) {
    if (count > 0) {
    System.out.println("count= " + count--);
    }
    }
    }

    public static void main(String[] args) {
    hello h1 = new hello();
    hello h2 = new hello();
    hello h3 = new hello();
    h1.start();
    h2.start();
    h3.start();
    }

    private int count = 5;
    }
    這個繼承Thread實現的方法,在main方法中其實是創建了是三個線程,所以運行的時候輸出的是15條記錄。但是如下實例:

    private int ticket = 5; //5張票

    public void run() {
    for (int i=0; i<=20; i++) {
    if (this.ticket > 0) {
    System.out.println(Thread.currentThread().getName()+ "正在賣票"+this.ticket--);
    }
    }
    }
    }
    public class lzwCode {

    public static void main(String [] args) {
    MyThread my = new MyThread();
    new Thread(my, "1號窗口").start();
    new Thread(my, "2號窗口").start();
    new Thread(my, "3號窗口").start();
    }
    }
    這個是通過實現Runnable接口實現資源共享中,只是創建了一個線程。在如下是我自己寫的實例,同樣可以實現資源共享的目的:
    public class Hello extends Thread{

    public Hello() {

    }

    public Hello(String name) {
    this.name = name;
    }

    private int ticket=10;
    public void run() {
    for (int i = 0; i < 5; i++) {
    if(ticket>0){
    System.out.println(Thread.currentThread().getName()+ "--" +this.ticket--);
    }
    }
    }

    public static void main(String[] args) {
    Hello h3 = new Hello();
    new Thread(h3, "窗口1").start();
    new Thread(h3, "窗口2").start();
    new Thread(h3, "窗口3").start();
    }

    private String name;

    }
  24. #74樓 oddsworld  2014-07-24 15:51
    此篇文章有多處錯誤,比如說到Thread,和Runnable的區別說的資源共享,“實現Runnable接口比繼承Thread類所具有的優勢:

    1):適合多個相同的程序代碼的線程去處理同一個資源” 文中沒有使用靜態變量,沒有資源共享的提現,
    另有一處說到 join用法, 說到“ demo.join(); //強制執行demo”也是錯誤的,join是等待該 demo線程結束才跳轉另外線程。
    暫時看到這些,不過文章總體還是有借鑑價值。
  25. #75樓 huamaoenjoy  2014-08-07 15:49
    你這個代碼怎麼插入的?格式很好看呀?求教一下
  26. #76樓[樓主] Rollen Holt  2014-08-07 18:54
    @huamaoenjoy
    博客園自帶的代碼高亮啊。另外推薦使用博客園最近支持的Markdown。格式個人感覺更好看
  27. #77樓 WilsonPeng  2014-09-02 10:11
    本文有些地方真不敢苟同。好多錯誤,確實不想往下看了。1:樓主觀點說Runnable接口方便共享資源。這裏不能說錯,但舉的例子實在是錯誤。2:樓主觀點:主線程結束,子線程可能不會結束。先暫不說這個是對是錯,但舉的例子實在不能說明論證這個觀點。還有處代碼的註釋:demo.join() //強制執行demo ,看到這個註釋,實在不想繼續往下看了。
  28. #78樓 WilsonPeng  2014-09-02 10:15
    希望樓主要麼把博文修改下,要麼刪了。不做任何處理的話,真有點誤導。言辭有點不當,但是實話。
  29. #79樓[樓主] Rollen Holt  2014-09-02 10:35
    @WilsonPeng
    本文章已經不維護.原文如果有錯,我也懶得改了,也懶的刪.
  30. #80樓 快鳥  2014-11-20 16:06
    之所以不使用run()來調度線程,是因爲調用run()只是簡單的方法調用,而不是重新啓動一個線程。

    synchronized(this){......}是不能達到同步效果的,除非在單例模式下。
    簡單的在方法前面加synchronized也不能達到預期的同步效果。

    這兩種都是由於線程持有各自的同步鎖,而不能達到同步效果。

    要實現預期的同步效果,必須保證所有線程的同步鎖共享且唯一。
  31. #81樓 泡沫__opt  2014-11-30 17:16
    好長啊……但真詳細~~
  32. #82樓 紅桃J  2014-12-05 19:49
    謝謝樓主!從你這兒學習了!
  33. #83樓 挨踢程序猿  2015-01-16 14:10
    樓主威武,寫得非常好。
  34. #84樓 smallstone  2015-02-03 17:47
    錯誤有點多。
  35. #85樓 jet23  2015-02-13 16:45
    Sometimes we use 'start()' based on the first example and it still performed in sequence. 

    Why?

    I guess the reason is that the steps in the first thread just took so short millsecond. So that even before the second thread starting the operations were completed. Is that correct?
  36. #86樓 yanhuadesan  2015-02-27 21:22
    厲害,樓主大二的時候就已經總結的這麼好了
  37. #87樓 星域  2015-04-24 11:40
    樓主寫的跟我看的一本書叫Java開發實戰經典裏的一模一樣-_-#.
  38. #88樓 AVAT  2015-05-26 21:18
    @秦寅
    這個正式我想說的,例子本身是錯的,會誤導別人
  39. #89樓 菜七  2015-07-08 11:46
    第一個實現Runnable接口共享資源的能通過嗎?我自己測的不能通過,有重複數據

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