手写生产者与消费者

使用synchroinzed与ReentrantLock完成手写生产者与消费者

狂神说Java学习笔记

生产者和消费者 synchroinzed 版

/**
 * Created by Xumx on 2022/6/14 20:25
 *
 *         生产者和消费者 synchroinzed 版
 *
 * 题目:现在两个线程,可以操作初始值为0的一个变量
 *  实现一个线程对该变量 + 1,一个线程对该变量 -1
 *  实现交替10次
 *
 *  诀窍:
 *  1. 高内聚低耦合的前提下,线程操作资源类
 *  2. 判断 、干活、通知
 */

public class Test1 {

    public static void main(String[] args) {
        Data data = new Data();

        new Thread(()->{
            for (int i = 1; i <= 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();

        new Thread(()->{
            for (int i = 1; i <= 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
    }

}

class Data{
    private  int number = 0;

    public synchronized void increment() throws InterruptedException {
        while (number!=0){
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"\t"+number);
        this.notifyAll();
    }

    public synchronized void decrement() throws InterruptedException {
        while (number==0){
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"\t"+number);
        this.notifyAll();
    }

}

问题升级:4个线程,两个加,两个减

/**
 * Created by Xumx on 2022/6/14 20:34
 *
 *      synchroinzed 版
 * 题目:现在四个线程,可以操作初始值为0的一个变量
 * 实现两个线程对该变量 + 1,两个线程对该变量 -1
 * 实现交替10次
 *
 * 诀窍:
 * 1. 高内聚低耦合的前提下,线程操作资源类
 * 2. 判断 、干活、通知
 * 3. 多线程交互中,必须要防止多线程的虚假唤醒,也即(判断不能用if,只能用while)
 */

public class Test2 {
    public static void main(String[] args) {
        final Data data = new Data();

        new Thread(()->{
            for (int i = 1; i <= 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();

        new Thread(()->{
            for (int i = 1; i <= 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();

        new Thread(()->{
            for (int i = 1; i <= 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();

        new Thread(()->{
            for (int i = 1; i <= 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"D").start();
    }

}

class Data{
    private  int number = 0;

    public synchronized void increment() throws InterruptedException {
        while (number!=0){
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"\t"+number);
        this.notifyAll();
    }

    public synchronized void decrement() throws InterruptedException {
        while (number==0){
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"\t"+number);
        this.notifyAll();
    }

}

ReentrantLock版生产者和消费者写法

/**
 * Created by Xumx on 2022/6/14 20:48
 *
 *      ReentrantLock版生产者和消费者写法
 * 题目:现在四个线程,可以操作初始值为0的一个变量
 * 实现两个线程对该变量 + 1,两个线程对该变量 -1
 * 实现交替10次
 * <p>
 * 诀窍:
 * 1. 高内聚低耦合的前提下,线程操作资源类
 * 2. 判断 、干活、通知
 * 3. 多线程交互中,必须要防止多线程的虚假唤醒,也即(判断不能用if,只能用while)
 */
public class Test3 {
    public static void main(String[] args) {
        Data data = new Data();

        new Thread(()->{
            for (int i = 1; i <= 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();

        new Thread(()->{
            for (int i = 1; i <= 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();

        new Thread(()->{
            for (int i = 1; i <= 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();

        new Thread(()->{
            for (int i = 1; i <= 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"D").start();
    }

}

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

    public void increment() throws InterruptedException {
        lock.lock();
        try {
            while (number!=0){
                condition.await();
            }
            number++;
            System.out.println(Thread.currentThread().getName()+"\t"+number);
            condition.signalAll();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }

    public void decrement() throws InterruptedException {
        lock.lock();

        try {
            while (number==0){
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName()+"\t"+number);
            condition.signalAll();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }

}

精确通知顺序访问

/**
 * Created by Xumx on 2022/6/14 20:57
 /**
 * 题目:多线程之间按顺序调用,实现 A->B->C
 * 三个线程启动,要求如下:
 * AA 打印5次,BB 打印10次。CC打印15次,依次循环
 *
 * 重点:标志位
 */

public class Test4 {
    public static void main(String[] args) {
        Resources resources = new Resources();
        new Thread(()->{
            for (int i = 1; i <=10; i++) {
                resources.print5();
            }
        },"AA").start();
        new Thread(()->{
            for (int i = 1; i <=10; i++) {
                resources.print10();
            }
        },"BB").start();
        new Thread(()->{
            for (int i = 1; i <=10; i++) {
                resources.print15();
            }
        },"CC").start();
    }
}
class Resources{ // 资源类
    private int number = 1; // 1A 2B 3C
    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();
    public void print5(){
        lock.lock();
        try {
            // 判断
            while (number!=1){
                condition1.await();
            }
            // 干活
            for (int i = 1; i <= 5; i++) {
                System.out.println(Thread.currentThread().getName()+'\t'+i);
            }
            // 通知,指定的干活!
            number = 2;
            condition2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void print10(){
        lock.lock();
        try {
            // 判断
            while (number!=2){
                condition2.await();
            }
            // 干活
            for (int i = 1; i <= 10; i++) {
                System.out.println(Thread.currentThread().getName()+'\t'+i);
            }
            // 通知,指定的干活!
            number = 3;
            condition3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void print15(){
        lock.lock();
        try {
            // 判断
            while (number!=3){
                condition3.await();
            }
            // 干活
            for (int i = 1; i <= 15; i++) {
                System.out.println(Thread.currentThread().getName()+'\t'+i);
            }
            // 通知,指定的干活!
            number = 1;
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}


手写单例模式

饿汉式

/*
*       饿汉式
*
*   在Hungry类中,如果定义了四个byte数组,当代码一运行,这四个数组就被初始化,并且放入内存了,如
    果长时间没有用到getInstance方法,不需要Hungry类的对象,这不是一种浪费吗?我希望的是 只有用
    到了 getInstance方法,才会去初始化单例类,才会加载单例类中的数据。所以就有了 第二种单例模
    式:懒汉式。
* */
public class Hungry {

    private Hungry(){}

    private final static Hungry hungry = new Hungry();

    public static Hungry getInstance(){
        return hungry;
    }

}

DCL懒汉式

/*
*       懒汉模式
*       lazyMan = new LazyMan();
        不是原子性操作,至少会经过三个步骤:
            1. 分配对象内存空间
            2. 执行构造方法初始化对象
            3. 设置instance指向刚分配的内存地址,此时instance !=null;
        由于指令重排,导致A线程执行 lazyMan = new LazyMan();的时候,可能先执行了第三步(还没执行第
        二步),此时线程B又进来了,发现lazyMan已经不为空了,直接返回了lazyMan,并且后面使用了返回
        的lazyMan,由于线程A还没有执行第二步,导致此时lazyMan还不完整,可能会有一些意想不到的错
        误,所以增加一个volatile关键字来避免指令重排。
            private volatile static LazyMan lazyMan;
 * */

public class LazyMan {

    private LazyMan(){}

    public static volatile LazyMan lazyMan = null;

    public static LazyMan getLazyMan(){
        if (lazyMan==null){
            synchronized (LazyMan.class){
                if (lazyMan==null){
                    lazyMan = new LazyMan();
                }
            }
        }
        return lazyMan;
    }

	//演示 反射可以破解当前方式的单例
    public static void main(String[] args) throws Exception {
        LazyMan lazyMan = LazyMan.getLazyMan();

        Constructor<LazyMan> constructor = LazyMan.class.getDeclaredConstructor(null);
        constructor.setAccessible(true);
        LazyMan lazyMan2 = constructor.newInstance();

        System.out.println(lazyMan);
        System.out.println(lazyMan2);
        System.out.println(lazyMan==lazyMan2);   // 所以反射是一个比较霸道的东西,无视private修饰的构造方法,可以直接在外面newInstance,破坏我们辛辛苦苦写的单例模式

    }

}

静态内部类实现饿汉式

/*
*       还有这种方式是第一种饿汉式的改进版本,同样也是在类中定义static变量的对象,并且直接初始化,不
        过是移到了静态内部类中,十分巧妙。既保证了线程的安全性,同时又满足了懒加载。
* */

public class LazyMan2 {

    private LazyMan2(){}

    public static LazyMan2 getLazyMan(){
        return InnerClass.lazyMan;
    }

    private static class InnerClass{
        public static final LazyMan2 lazyMan = new LazyMan2();

    }


    //演示 反射可以破解当前方式的单例
    public static void main(String[] args) throws Exception {
        LazyMan2 lazyMan = LazyMan2.getLazyMan();

        Constructor<LazyMan2> constructor = LazyMan2.class.getDeclaredConstructor(null);
        constructor.setAccessible(true);
        LazyMan2 lazyMan2 = constructor.newInstance();

        System.out.println(lazyMan);
        System.out.println(lazyMan2);
        System.out.println(lazyMan==lazyMan2);   // 所以反射是一个比较霸道的东西,无视private修饰的构造方法,可以直接在外面newInstance,破坏我们辛辛苦苦写的单例模式

    }

}

枚举实现饿汉式

public enum LazyMan3 {

    INSTANCE;

    public LazyMan3 getInstance(){
        return INSTANCE;
    }


    public static void main(String[] args) throws Exception {

        LazyMan3 instance1 = LazyMan3.INSTANCE;
        LazyMan3 instance2 = LazyMan3.INSTANCE;

        System.out.println(instance1);
        System.out.println(instance2);

        Constructor<LazyMan3> constructor = LazyMan3.class.getDeclaredConstructor(String.class,int.class);
        constructor.setAccessible(true);
        LazyMan3 instance3 = constructor.newInstance();  //Cannot reflectively create enum objects

        System.out.println(instance3);

    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

晒太阳的黑宝

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值