好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Java多线程:生产者与消费者案例

前言

想象一下生活中哪些是和线程沾边的?饭店炒菜就是一个很好的例子

首先客人要吃菜,前提是厨师要炒好,也就是说,厨师不炒好的话客人是没有饭菜的。这时候,厨师就是一个线程,客人拿菜就是另一个线程。

工具

jdk13,IDEA2019.1.4

知识点

Thread、Runnable、synchronized、面向对象知识(继承、封装、接口、方法重写)、条件判断以及线程的一些其他知识点

设计思路

首先要有两个线程,也就是说要两个类,分别是Producer(生产者)和Consumer(消费者)。

由于我们是模拟厨师与客人之间的互动,也就是说还需要一个类来封装信息:Message(类)。

然后,避免线程之间发生数据混乱的情况,肯定还需要使用synchronized来进行线程同步。

具体步骤

首先我们来一份没有用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

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

public class Message {

     private String title;

     private String content;

     Message(){

     };

     public Message(String title, String content) {

         this .title = title;

         this .content = content;

     }

     public String getTitle() {

         return title;

     }

     public void setTitle(String title) {

         this .title = title;

     }

     public String getContent() {

         return content;

     }

     public void setContent(String content) {

         this .content = content;

     }

}

/*

* 定义生产者类Producer

* */

class Producer implements Runnable{

     private Message msg=null;

     public Producer(Message msg) {

         this.msg = msg;

     }

     @Override

     public void run() {

         for (int i=0;i<=50;i++){

             if (i%2==0){

                 this.msg.setTitle("喜欢夜雨吗?");

                 try {

                     Thread.sleep(100);

                 }catch (InterruptedException e){

                     System.out.println(e);

                 }

                 this.msg.setContent("是的呢!");

             }else {

                 this.msg.setTitle("还不关注我吗?");

                 try {

                     Thread.sleep(100);

                 }catch (InterruptedException e){

                     System.out.println(e);

                 }

                 this.msg.setContent("好的呢!");

             }

         }

     }

}

/*

* 定义消费者类Consumer

* */

class Consumer implements Runnable{

     private Message msg= null ;

     public Consumer(Message msg) {

         this .msg = msg;

     }

     @Override

     public void run() {

         for ( int i= 0 ;i<= 50 ;i++){

             try {

                 Thread.sleep( 100 );

             } catch (InterruptedException e){

                 System.out.println(e);

             }

             System.out.println( this .msg.getTitle()+ "--->" + this .msg.getContent());

         }

     }

}

class TestDemo{

     public static void main(String[] args) {

         Message msg= new Message();

         new Thread( new Producer(msg)).start();

         new Thread( new Consumer(msg)).start();

     }

}

看看运行结果:

看仔细咯,发生了数据错乱啊,Title与Content没有一一对应欸。咋办?

能咋办,改代码呗。

发生数据混乱的原因完全是因为,生产者线程还没生产的时候,消费者就已经消费了(至于消费的啥我也不知道,我也不敢问啊)。所以造成了数据混乱,不过我们上面说了啊,要使用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

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

class TestDemo{

     public static void main(String[] args) {

         Message msg= new Message();

         new Thread( new Producer(msg)).start();

         new Thread( new Consumer(msg)).start();

     }

}

class Message{

     private String title,content;

     public synchronized void set(String title,String content){

         this .title=title;

         this .content=content;

     }

     public synchronized void get(){

         try {

             Thread.sleep( 1000 );

         } catch (InterruptedException e){

             System.out.println(e);

         }

         System.out.println( this .title+ "-->" + this .content);

     }

     public String getContent() {

         return content;

     }

     public void setContent(String content) {

         this .content = content;

     }

     public String getTitle() {

         return title;

     }

     public void setTitle(String title) {

         this .title = title;

     }

}

class Producer implements Runnable{

     private Message msg= null ;

     Producer(Message msg){

         this .msg=msg;

     }

     @Override

     public void run() {

         for ( int i= 0 ;i<= 50 ;i++){

             if (i% 2 == 0 ){

                 this .msg.set( "喜欢夜雨吗?" , "是的呢!" );

             } else {

                 this .msg.set( "还不关注吗?" , "好的呢!" );

             }

         }

     }

}

class Consumer implements Runnable{

     private Message msg= null ;

     Consumer(Message msg){

         this .msg=msg;

     }

     @Override

     public void run() {

         for ( int i= 0 ;i<= 50 ;i++){

             this .msg.get();

         }

     }

}

我又重新封装了一些方法,然后运行的时候,wc,数据倒是不混乱了,但是呢,数据重复了一大堆。

为啥会出现这个问题呢?最后想了一下,会出现这种问题的,就是因为线程的执行顺序的问题。我们想要实现的效果是生产者线程执行了之后,让生产者线程等待,而后让消费者线程执行,等待消费者线程执行完成之后就又让生产者线程继续执行。后来我又查了查官方文档,发现Object类中专门有三个方法是与线程相关的:

方法 描述
public final void wait() throws InterruptedException 线程的等待
public final void notify() 唤醒第一个等待线程
public final void notifyAll()  

当我看到这些方法的时候,心里愣了一下,这不就是我们想要的方法吗,用wait()方法来让生产者线程等待,然后运行消费者线程,等消费者线程执行完了之后又让生产者线程去执行。这其中我们用true和false来表示运行开始和运行暂停。

最后我们来看看完成品的代码:

?

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

class TestDemo{

     public static void main(String[] args) {

         Message msg= new Message();

         new Thread( new Producer(msg)).start();

         new Thread( new Consumer(msg)).start();

     }

}

class Message extends Exception{

     private String title,content;

     private boolean flag= true ;

     // true表示正在生产,不要来取走,因为没由让消费者区走的

     // false表示可以取走,但是不能生产

     public synchronized void set(String title,String content){

         if ( this .flag== false ){

             try {

                 super .wait();

             } catch (InterruptedException e){

                 System.out.println(e);

             }

         }

         this .title=title;

         try {

             Thread.sleep( 60 );

         } catch (InterruptedException e){

             System.out.println(e);

         }

         this .content=content;

         this .flag= true ; // 生产完成,修改标志位

         super .notify(); // 唤醒等待线程

     }

     public synchronized void get(){

         if (flag== true ) { // 已经生产好了,等待取走

             try {

                 super .wait();

             } catch (InterruptedException e){

                 System.out.println(e);

             }

         }

         try {

             Thread.sleep( 60 );

         } catch (InterruptedException e){

             System.out.println(e);

         }

         System.out.println( this .title+ "-->" + this .content);

         this .flag= true ;

         super .notify();

     }

     public String getContent() {

         return content;

     }

     public void setContent(String content) {

         this .content = content;

     }

     public String getTitle() {

         return title;

     }

     public void setTitle(String title) {

         this .title = title;

     }

}

class Producer implements Runnable{

     private Message msg= null ;

     Producer(Message msg){

         this .msg=msg;

     }

     @Override

     public void run() {

         for ( int i= 0 ;i<= 50 ;i++){

             if (i% 2 == 0 ){

                 this .msg.set( "喜欢夜雨吗?" , "是的呢!" );

             } else {

                 this .msg.set( "还不关注吗?" , "好的呢!" );

             }

         }

     }

}

class Consumer implements Runnable{

     private Message msg= null ;

     Consumer(Message msg){

         this .msg=msg;

     }

     @Override

     public void run() {

         for ( int i= 0 ;i<= 50 ;i++){

             this .msg.get();

         }

     }

}

运行结果我就不贴了,你们自己去测试一下吧…

总结

这个案例完美的呈现了线程以及面向对象知识的综合运用,具有很强的实际操作性

本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注的更多内容!

原文链接:https://blog.csdn.net/weixin_43581288/article/details/104784227

查看更多关于Java多线程:生产者与消费者案例的详细内容...

  阅读:17次