java并发编程阻塞队列

java

在前面我们接触的队列都是非阻塞队列,比如PriorityQueue、LinkedList(LinkedList是双向链表,它实现了Dequeue接口)。

  使用非阻塞队列的时候有一个很大问题就是:它不会对当前线程产生阻塞,那么在面对类似消费者-生产者的模型时,就必须额外地实现同步策略以及线程间唤醒策略,这个实现起来就非常麻烦。但是有了阻塞队列就不一样了,它会对当前线程产生阻塞,比如一个线程从一个空的阻塞队列中取元素,此时线程会被阻塞直到阻塞队列中有了元素。当队列中有元素后,被阻塞的线程会自动被唤醒(不需要我们编写代码去唤醒)。这样提供了极大的方便性。

使用非阻塞队列的时候有一个很大问题就是:它不会对当前线程产生阻塞,那么在面对类似消费者-生产者的模型时,就必须额外地实现同步策略以及线程间唤醒策略,这个实现起来就非常麻烦。但是有了阻塞队列就不一样了,它会对当前线程产生阻塞,比如一个线程从一个空的阻塞队列中取元素,此时线程会被阻塞直到阻塞队列中有了元素。当队列中有元素后,被阻塞的线程会自动被唤醒(不需要我们编写代码去唤醒)。这样提供了极大的方便性。

使用非阻塞队列的时候有一个很大问题就是:它不会对当前线程产生阻塞,那么在面对类似消费者-生产者的模型时,就必须额外地实现同步策略以及线程间唤醒策略,这个实现起来就非常麻烦。但是有了阻塞队列就不一样了,它会对当前线程产生阻塞,比如一个线程从一个空的阻塞队列中取元素,此时线程会被阻塞直到阻塞队列中有了元素。当队列中有元素后,被阻塞的线程会自动被唤醒(不需要我们编写代码去唤醒)。这样提供了极大的方便性。

使用非阻塞队列的时候有一个很大问题就是:它不会对当前线程产生阻塞,那么在面对类似消费者-生产者的模型时,就必须额外地实现同步策略以及线程间唤醒策略,这个实现起来就非常麻烦。但是有了阻塞队列就不一样了,它会对当前线程产生阻塞,比如一个线程从一个空的阻塞队列中取元素,此时线程会被阻塞直到阻塞队列中有了元素。当队列中有元素后,被阻塞的线程会自动被唤醒(不需要我们编写代码去唤醒)。这样提供了极大的方便性。

使用非阻塞队列的时候有一个很大问题就是:它不会对当前线程产生阻塞,那么在面对类似消费者-生产者的模型时,就必须额外地实现同步策略以及线程间唤醒策略,这个实现起来就非常麻烦。但是有了阻塞队列就不一样了,它会对当前线程产生阻塞,比如一个线程从一个空的阻塞队列中取元素,此时线程会被阻塞直到阻塞队列中有了元素。当队列中有元素后,被阻塞的线程会自动被唤醒(不需要我们编写代码去唤醒)。这样提供了极大的方便性。

四.示例和使用场景

  下面先使用Object.wait()和Object.notify()、非阻塞队列实现生产者-消费者模式:

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

public class Test {

    private int queueSize = 10;

    private PriorityQueue<Integer> queue = new PriorityQueue<Integer>(queueSize);

     

    public static void main(String[] args)  {

        Test test = new Test();

        Producer producer = test.new Producer();

        Consumer consumer = test.new Consumer();

         

        producer.start();

        consumer.start();

    }

     

    class Consumer extends Thread{

         

        @Override

        public void run() {

            consume();

        }

         

        private void consume() {

            while(true){

                synchronized (queue) {

                    while(queue.size() == 0){

                        try {

                            System.out.println("队列空,等待数据");

                            queue.wait();

                        catch (InterruptedException e) {

                            e.printStackTrace();

                            queue.notify();

                        }

                    }

                    queue.poll();          //每次移走队首元素

                    queue.notify();

                    System.out.println("从队列取走一个元素,队列剩余"+queue.size()+"个元素");

                }

            }

        }

    }

     

    class Producer extends Thread{

         

        @Override

        public void run() {

            produce();

        }

         

        private void produce() {

            while(true){

                synchronized (queue) {

                    while(queue.size() == queueSize){

                        try {

                            System.out.println("队列满,等待有空余空间");

                            queue.wait();

                        catch (InterruptedException e) {

                            e.printStackTrace();

                            queue.notify();

                        }

                    }

                    queue.offer(1);        //每次插入一个元素

                    queue.notify();

                    System.out.println("向队列取中插入一个元素,队列剩余空间:"+(queueSize-queue.size()));

                }

            }

        }

    }

}

   这个是经典的生产者-消费者模式,通过阻塞队列和Object.wait()和Object.notify()实现,wait()和notify()主要用来实现线程间通信。

  具体的线程间通信方式(wait和notify的使用)在后续问章中会讲述到。

  下面是使用阻塞队列实现的生产者-消费者模式:

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

public class Test {

    private int queueSize = 10;

    private ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(queueSize);

     

    public static void main(String[] args)  {

        Test test = new Test();

        Producer producer = test.new Producer();

        Consumer consumer = test.new Consumer();

         

        producer.start();

        consumer.start();

    }

     

    class Consumer extends Thread{

         

        @Override

        public void run() {

            consume();

        }

         

        private void consume() {

            while(true){

                try {

                    queue.take();

                    System.out.println("从队列取走一个元素,队列剩余"+queue.size()+"个元素");

                catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }

        }

    }

     

    class Producer extends Thread{

         

        @Override

        public void run() {

            produce();

        }

         

        private void produce() {

            while(true){

                try {

                    queue.put(1);

                    System.out.println("向队列取中插入一个元素,队列剩余空间:"+(queueSize-queue.size()));

                catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }

        }

    }

}

   有没有发现,使用阻塞队列代码要简单得多,不需要再单独考虑同步和线程间通信的问题。

  在并发编程中,一般推荐使用阻塞队列,这样实现可以尽量地避免程序出现意外的错误。

  阻塞队列使用最经典的场景就是socket客户端数据的读取和解析,读取数据的线程不断将数据放入队列,然后解析线程不断从队列取数据解析。还有其他类似的场景,只要符合生产者-消费者模型的都可以使用阻塞队列。

以上是 java并发编程阻塞队列 的全部内容, 来源链接: utcz.com/z/390566.html

回到顶部