Java多线程和高并发学习笔记5

Java多线程之间的通信,最常见的就是生产者-消费者模式,通过一个状态码来控制什么时候去生产和消费,下面我们就来写一个简单的例子,来看看这种最常见的线程之间的通信方式

package thread3;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 对同一个数字,两个线程,分别+1,-1,交替执行5次
 */
public class Customer {
     

    static class ShareData {
     
        private int number = 0;
        private final Lock lock = new ReentrantLock();
        private final Condition condition = lock.newCondition();

        public void increment() {
     
            try {
     
                lock.lock();
                // 首先判断是否需要生产
                while (number != 0) {
     
                    // 等待,不能生产
                    condition.await();
                }

                // 生产
                number++;
                System.out.println(Thread.currentThread().getName() + '\t' + number);

                // 通知唤醒
                condition.signalAll();
            } catch (InterruptedException e) {
     
                e.printStackTrace();
            } finally {
     
                lock.unlock();
            }
        }

        public void decrement() {
     
            try {
     
                lock.lock();
                // 首先判断是否需要生产
                while (number == 0) {
     
                    // 等待,不能生产
                    condition.await();
                }

                // 生产
                number--;
                System.out.println(Thread.currentThread().getName() + '\t' + number);

                // 通知唤醒
                condition.signalAll();
            } catch (InterruptedException e) {
     
                e.printStackTrace();
            } finally {
     
                lock.unlock();
            }
        }
    }


    public static void main(String[] args) {
     
        ShareData shareData = new ShareData();
        new Thread(() -> {
     
            for (int i = 0; i < 5; i++) {
     
                shareData.increment();
            }
        }, "a").start();

        new Thread(() -> {
     
            for (int i = 0; i < 5; i++) {
     
                shareData.decrement();
            }
        }, "b").start();
    }
}

上面这段代码很好理解,首先有两个线程,a和b,分别执行5次,对ShareData对象中的number值进行加和减,我们实现的方式也很简单,首先给加和减两个方法添加了一个可重入锁,如果number值为0,那么我们就对number值进行加1,否则就对number值进行减1,这个地方加了锁以后,就可以保证同时间只有一个线程可以对number操作,而不会出现多个线程同时操作同一个对象,导致最终的值不一致的情况

运行结果:
Java多线程和高并发学习笔记5_第1张图片

这里顺便提一个我遇到的一个面试题:
sychronized和Lock有什么区别?用Lock有什么好处?请举例~
使用方式:
synchronized (new Object()) {}
new ReentrantLock();
1、sychronized是一个关键字,Lock是jdk1.5以后提出的一个类
2、sychronized不用手动释放,Lock需要unlock,而且最好是在finally里面执行
3、sychronized不能中断,除非抛异常或者运行完成,Lock可以中断(设置超时方法:tryLock(long timeout,TimeUnit unit),lockInterruptibly()代码块中,调用interrupt()方法可以中断)
4、sychronized默认非公平锁,Lock默认也是非公平锁,但是可以设置为公平锁
5、ReentrantLock可以用来实现分组唤醒需要唤醒的线程们,可以精确唤醒,而不是像sychronized要么随机唤醒一个线程要么唤醒全部线程

关于上面答案的第5点,我们可以举个例子来证明一下,ReentrantLock可以精准的唤醒我们需要唤醒的线程

package thread3;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 多线程之间按顺序调用,实现A->B->C三个线程启动,要求如下:
 * A打印5次,B打印10次,C打印15次
 * 紧接着
 * A打印5次,B打印10次,C打印15次
 */

class ShareData {
     
    private int number = 1; // A:1 B:2 C:3
    private final Lock lock = new ReentrantLock();
    private final Condition condition1 = lock.newCondition();
    private final Condition condition2 = lock.newCondition();
    private final Condition condition3 = lock.newCondition();

    public void print5() {
     
        lock.lock();
        try {
     
            while (number != 1) {
     
                condition1.await();
            }

            for (int i = 0; i < 5; i++) {
     
                System.out.println(Thread.currentThread().getName() + "\t" + i);
            }

            number = 2;
            condition2.signal();
        } catch (InterruptedException e) {
     
            e.printStackTrace();
        } finally {
     
            lock.unlock();
        }
    }

    public void print10() {
     
        lock.lock();
        try {
     
            while (number != 2) {
     
                condition2.await();
            }

            for (int i = 0; i < 10; i++) {
     
                System.out.println(Thread.currentThread().getName() + "\t" + i);
            }

            number = 3;
            condition3.signal();
        } catch (InterruptedException e) {
     
            e.printStackTrace();
        } finally {
     
            lock.unlock();
        }
    }

    public void print15() {
     
        lock.lock();
        try {
     
            while (number != 3) {
     
                condition3.await();
            }

            for (int i = 0; i < 15; i++) {
     
                System.out.println(Thread.currentThread().getName() + "\t" + i);
            }

            number = 1;
            condition1.signal();
        } catch (InterruptedException e) {
     
            e.printStackTrace();
        } finally {
     
            lock.unlock();
        }
    }
}

public class LockDemo {
     
    public static void main(String[] args) {
     
        ShareData shareData = new ShareData();
        new Thread(() -> {
     
            for (int i = 0; i < 10; i++) {
     
                shareData.print5();
            }
        }, "a").start();

        new Thread(() -> {
     
            for (int i = 0; i < 10; i++) {
     
                shareData.print10();
            }
        }, "b").start();

        new Thread(() -> {
     
            for (int i = 0; i < 10; i++) {
     
                shareData.print15();
            }
        }, "c").start();
    }
}

Java多线程和高并发学习笔记5_第2张图片
这里没有截图完,但是可以看出来,是按照我们想要的顺序去执行的

下面我们再用阻塞队列再来改造一下:

package thread3;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

class MyResource {
     
    private boolean FLAG = true; // 如果为true的时候,则默认生产和消费
    private final AtomicInteger atomicInteger = new AtomicInteger();

    BlockingQueue<String> blockingQueue;

    public MyResource(BlockingQueue<String> blockingQueue) {
     
        this.blockingQueue = blockingQueue;
        System.out.println(blockingQueue.getClass().getName());
    }

    public void myProd() {
     
        String data;
        boolean returnVal;
        while (FLAG) {
     
            data = String.valueOf(atomicInteger.incrementAndGet());
            try {
     
                returnVal = blockingQueue.offer(data, 2L, TimeUnit.SECONDS);
                if (returnVal) {
     
                    System.out.println(Thread.currentThread().getName() + "\t 插入队列" + data + "成功");
                } else {
     
                    System.out.println(Thread.currentThread().getName() + "\t 插入队列" + data + "失败");
                }

                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
     
                e.printStackTrace();
            }
        }

        System.out.println(Thread.currentThread().getName() + "\t 当前生产者停止生产,flag为false");
    }

    public void myCustomer() {
     
        String data;
        while (FLAG) {
     
            try {
     
                data = blockingQueue.poll(2L, TimeUnit.SECONDS);
                if (data == null || "".equalsIgnoreCase(data)) {
     
                    FLAG = false;
                    System.out.println(Thread.currentThread().getName() + "\t" + "超过2秒没有可以消费的物品,消费退出");
                    return;
                }

                System.out.println(Thread.currentThread().getName() + "\t" + data + "消费队列成功");

                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
     
                e.printStackTrace();
            }
        }

        System.out.println(Thread.currentThread().getName() + "\t 当前消费者停止消费,flag为false");
    }

    public void stop() {
     
        this.FLAG = false;
    }
}

public class BlockQueueDemo {
     
    public static void main(String[] args) {
     
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(10);
        MyResource myResource = new MyResource(blockingQueue);

        new Thread(() -> {
     
            System.out.println(Thread.currentThread().getName() + "\t生产线程开始启动");
            myResource.myProd();
        }, "prod").start();

        new Thread(() -> {
     
            System.out.println(Thread.currentThread().getName() + "\t消费线程开始启动");
            myResource.myCustomer();
        }, "customer").start();

        try {
     
            TimeUnit.SECONDS.sleep(5);
            System.out.println("5秒时间到,停止生产和消费");
            myResource.stop();
        } catch (InterruptedException e) {
     
            e.printStackTrace();
        }
    }
}

结果:
Java多线程和高并发学习笔记5_第3张图片

你可能感兴趣的:(多线程和高并发)