线程之间的相互通信

等待和通知机制

可以通过锁对象调用Object类的方法:

1.wait() 让当前线程等待,直到被通知。

2.wait(long) 让线程等待,直到被通知或时间结束。

3.notify() 随机唤醒一个线程。

4.notifyAll() 唤醒所有线程。

注意:如果不是锁对象调用上面的方法会出现异常IllegalMonitorStateException。

简单的一个等待和通知案例

线程的交替输出,线程1负责输出A,线程2负责输出B,两个线程同时执行打印出ABABAB....

/**
 * 线程交替输出
 */
public class ExchangeDemo1 {
    public static synchronized void test1(){
        while (true){
            ExchangeDemo1.class.notify();
            System.out.println("A");
            try {
                ExchangeDemo1.class.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public static synchronized void test2(){
        while (true){
            ExchangeDemo1.class.notify();
            System.out.println("B");
            try {
                ExchangeDemo1.class.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


    public static void main(String[] args) {
        new Thread(()->test1()).start();
        new Thread(()->test2()).start();
    }
}

生产者消费者模式

是一种设计模式,用于解决两个点(线程、进程、服务器)之间数据通信的协调问题。

生产数据的点叫生产者,使用数据的点叫消费者,生产者和消费者可能存在速度不一致的情况。生产者速度过快,消费者消费速度慢,会浪费大量资源;反过来,消费者速度快,生产者速度慢,消费者浪费时间取等待。

实现步骤:

1.设置缓冲区,设定临界值。

2.生产者生产数据,存入缓冲区,如果缓冲区达到临界值,生产者就等待;否则就通知消费者进行消费。

3.消费者从缓冲区取出数据,如果缓冲区空了,消费者等待;否则就通知生产者继续生产。

作用:

1.解耦,生产者和消费者之间加入缓冲区,不需要直接调用。
2. 忙闲不均,协调生产者和消费者之间的速度。
3.节约资源,减少生产者资源的浪费,和消费者等待的时间。

编写一个简单的案例,模拟包子铺和顾客来买包子的案例

/**
 * 包子的实体类
 */
public class Baozi {
    private int id;

    public Baozi(int id) {
        this.id = id;
    }

    public Baozi() {

    }

    @Override
    public String toString() {
        return "--包子id" + id;
    }
}

 第一种方式:使用synchronized来实现


/**
 * 使用synchronized实现生产者和消费者模式包子铺1.0
 */
public class BaoziStore {
    //临界值
    public static final int MAX_SIZE = 50;
    //缓冲区
    private List<Baozi> list = new ArrayList<>();

    //生产包子
    public synchronized void makeBaozi(){
        //如果缓冲区满了,老板就等待顾客来买
        if(list.size()==MAX_SIZE){
            System.out.println("包子满了老板"+Thread.currentThread().getName()+"等待顾客来买");
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }else{
            //通知消费者
            this.notifyAll();
        }
        if(list.size()<MAX_SIZE){
            //如果缓冲区没有满,就生产包子
            Baozi baozi = new Baozi(list.size());
            list.add(baozi);
            System.out.println("老板" +Thread.currentThread().getName()+ "做了包子"+baozi+",总共有"+list.size()+"个包子");
        }
    }


    //卖包子
    public synchronized void takeBaozi(){
        //判断如果包子铺没有包子,顾客就等待
        if(list.size()==0){
            System.out.println("包子空了顾客"+Thread.currentThread().getName()+"一直等");
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }else{
            //通知生产者
            this.notifyAll();
        }
        if(list.size()>0){
            //如果包子铺有包子,就拿一个包子
            Baozi baozi = list.remove(0);
            System.out.println("顾客"+Thread.currentThread().getName()+"拿走了一个包子"+baozi+",还剩下"+list.size()+"个包子");
        }
    }

    public static void main(String[] args) {
        BaoziStore baoziStore = new BaoziStore();
        new Thread(()->{
            for (int i = 0; i < 100; i++) {
                baoziStore.makeBaozi();
            }
        }).start();
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                for (int j = 0; j < 10; j++) {
                    baoziStore.takeBaozi();
                }
            }).start();
        }
    }

}

第二种方式:使用同步锁的方式来实现

Lock的等待和通知

Condition 类可以提供锁对象类似的方法

创建:

Condition condition = lock.newCondition();

等待和通知:

await() 让线程等待

signal() 通知线程

signalAll() 通知所有线程


/**
 * 使用同步锁实现生产者和消费者模式包子铺2.0
 */
public class BaoziStore2 {
    //创建锁对象
    private Lock lock = new ReentrantLock();
    //获取Condition对象
    private Condition condition = lock.newCondition();
    //临界值
    public static final int MAX_SIZE = 50;
    //缓冲区
    private  List<Baozi> list = new ArrayList<>();

    //生产包子
    public  void makeBaozi(){
        try {
            lock.lock();
            //如果缓冲区满了
            if (list.size() == MAX_SIZE) {
                System.out.println("包子满了老板" + Thread.currentThread().getName() + "--等待顾客来买");
                try {
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {
                //通知消费者
               condition.signalAll();
            }
            if (list.size() < MAX_SIZE) {
                //创建包子对象,存入缓存区
                Baozi baozi = new Baozi(list.size());
                list.add(baozi);
                System.out.println("老板" + Thread.currentThread().getName() + "做了包子" + baozi + ",总共有" + list.size() + "个包子");
            }
        }finally {
            lock.unlock();
        }
    }


    //卖包子
    public void takeBaozi(){
        try {
            lock.lock();
            //判断如果包子铺没有包子,顾客等待
            if (list.size() == 0) {
                System.out.println("包子空了顾客" + Thread.currentThread().getName() + "一直等");
                try {
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {
                //通知老板
               condition.signalAll();
            }
            if (list.size() > 0) {
                //从缓存区拿一个包子
                Baozi baozi = list.remove(0);
                System.out.println("顾客" + Thread.currentThread().getName() + "拿走了包子" + baozi + ",还剩下" + list.size() + "个包子");
            }
        }finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        BaoziStore2 baoziStore2 = new BaoziStore2();
        new Thread(()->{
            for (int i = 0; i < 100; i++) {
                baoziStore2.makeBaozi();
            }
        }).start();
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                for (int j = 0; j < 10; j++) {
                    baoziStore2.takeBaozi();
                }
            }).start();
        }
    }
}

阻塞队列

阻塞队列是一种特殊的集合,会自动阻塞对队列添加或删除数据的线程,也可以自动通知。

BlockingQueue接口

put(T) 添加数据到队尾,到临界值阻塞。

T take 从队头取出并删除数据,到空时阻塞。

常用实现类:

ArrayBlockingQueue 数组结构的阻塞队列

LinkedBlockingQueue 链表结构的阻塞队列

SynchronousQueue 同步机制的队列

使用阻塞队列来编写上述的包子铺案例

/**
 * 阻塞队列的包子铺3.0
 */
public class BaoziStore3 {
    //临界值
    public static final int MAX_SIZE = 30;
    //缓冲区
    private BlockingQueue<Baozi> list = new ArrayBlockingQueue<>(MAX_SIZE);

    //生产包子
    public  void makeBaozi(){
        try {
           //创建包子对象,存入缓存区
            Baozi baozi = new Baozi(list.size());
            list.put(baozi);
            System.out.println("老板" + Thread.currentThread().getName() + "做了包子" + baozi + ",总共有" + list.size() + "个包子");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


    //卖包子
    public void takeBaozi(){
            //从缓存区拿出一个包子
        try {
             Baozi baozi = list.take();
            System.out.println("顾客" + Thread.currentThread().getName() + "拿走包子" + baozi + ",还剩下" + list.size() + "个包子");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        BaoziStore3 baoziStore3 = new BaoziStore3();
        new Thread(()->{
            for (int i = 0; i < 50; i++) {
                baoziStore3.makeBaozi();
            }
        }).start();
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                for (int j = 0; j < 10; j++) {
                    baoziStore3.takeBaozi();
                }
            }).start();
        }
    }
}

ThreadLocal

线程局部变量,也是解决线程同步问题的一种方式。

锁机制 多个线程排队访问一个资源 以时间换空间。

ThreadLocal 多个线程并发访问自己独立的资源 以空间换时间。

ThreadLocal的原理

每个线程内部有Map集合ThreadLocalMap,获得数据时候返回当前线程的ThreadLocalMap,Map中有大量键值对Entry,键是当前的ThreadLocal对象,值是保存的数据。

 public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
    
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

 简单编写一个使用ThreadLocal案例

/**
 * 线程局部变量
 */
public class ThreadLocalDemo {
    static volatile int value = 1;
    public static void main(String[] args) {
        //线程的本地变量
        ThreadLocal<Integer> local = new ThreadLocal<Integer>(){
            //设置初始值
            @Override
            protected Integer initialValue() {
                return 1;
            }
        };
        for (int j = 0; j < 3; j++) {
            new Thread(()->{
                for (int i = 0; i < 10; i++) {
                    value++;
                    local.set(local.get()+1);
                }
                System.out.println(Thread.currentThread().getName()+"value="+value);
                System.out.println(Thread.currentThread().getName()+"local="+local.get());
            }).start();
        }
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"value的值"+value);
        System.out.println(Thread.currentThread().getName()+"local的值"+local.get());
    }
}

补充:强软弱虚引用的区别?

强引用: 一般情况下创建的对象

Person person = new Person()

如果对象有引用指向,就不会被垃圾收集,没有引用指向就可能被垃圾收集。

person = null;

软引用: SoftReference

如果即将出现OOM,gc会优先回收软引用,比强引用更容易被回收。

主要用于缓存

弱引用:WeakReference

只要出现gc,都会进行回收。

可以缓存临时数据

虚引用: PhantomReference

形同虚设,不能作为对象的引用使用,主要用于跟踪垃圾收集器的活动。

强引用 > 软引用 > 弱引用 > 虚引用

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值