三篇搞定Java高并发笔记【第二篇】

本文Java高并发的内容将从三个阶段记录,参考资料【Java并发编程详解】:

  • 多线程基础
  • Java内存模型(高并发设计模式)
  • Java并发包JUC
  • Java并发包源码AQS

线程通信

线程通信与网络间的通信不一样,线程通信又称进程内通信,多个线程实现互斥访问共享资源时会互相发送信号或等待信号。

wait和notify

我们都知道wait是等待的意思,notify是通知、通告的意思。但是wait和notify并不是thread独有的方法,而是object中的方法,也就是说JDK中的每一个类都拥有这两个方法。那么这两个方法是怎么运作的呢,怎样的场景适合运用这两个方法呢?
同步阻塞消息场景
我们假设用户在网上商城购买用品,每个用户向服务器发送请求购买,但是此时只有一个服务器,并且只能同步处理用户的请求。如下图:

上面这种是同步阻塞消息处理,意思就是每个请求发送到服务器,服务器再去分配线程去处理请求。一个服务器当前只能处理一个请求,其他请求访问时会自动阻塞。

异步非阻塞消息处理场景

同步阻塞消息处理的方式是吞吐量低,很难提高业务的并发量。如果换成上面的异步处理方式,将请求进入队列依次异步处理,这就类似于操作系统的进程,并发的处理这些消息请求。这样用户不用等结果处理之后才返回,从而提高了系统的吞吐量和并发量,但异步处理同样也存在缺陷,用户等待结果返回时需要再次调用接口进行查询结果集,浪费了时间。

那么对于线程的阻塞和唤醒我们可以通过什么关键字去实现呢?

wait和notify方法详解

  • 1 wait方法必须拥有该对象的monitor,也就是必须在同步方法中使用。

    • 1.1 monitor的解释
      Java虚拟机为每个对象和class字节码都设置了监听器monitor,用于检测并发代码的重入,同时在object类还提供了wait和notify对线程进程控制。
      monitor可以类比为一个房间,房间中有比较宝贵的数据,那么我每次只能要求一个线程进行房间访问重要的数据。线程进入房间访问到该对象的重要的数据,称为持有monitor,访问完毕退出房间的为释放monitor
      在上一节中讲到,其实monitor就可以当成对象锁,因为monitor是对象锁的实现。 我们知道,sychronized关键字可以获取锁,那么就等于获取到了对象的monitor,持有了monitor就可以访问共享数据了。
    new Thread(new Runnable() {  
            @Override  
            public void  run() {  
                try {  
                    synchronized (obj) {  
                        obj.wait();
                    }  
                } catch (Exception e) {  
                    e.printStackTrace();  
                }  
            }  
        }).start();  
    

    比如我们上面这段代码,在同步synchronized中使用对象的wait方法,为什么这样写呢,wait方法前提就是持有对象的monitor,也就是说synchronized帮助线程获取到了obj对象的monitor(锁),那么其他线程访问obj时就会进入等待!
    wait方法的工作原理就是释放monitor的所有权,此时线程再次请求对象的monitor时就会进入阻塞状态(此时释放的monitor被其他线程获取),所以这就是为什么wait方法会使线程进入阻塞状态!

  • 当前线程执行了该对象的wait方法后,将放弃对该monitor的所有权并且进入与对象关联的wait set队列中,其他线程将会继续争抢该对象的monitor。

    执行wait方法,线程释放了对象的monitor所有权,线程进入阻塞状态,而wait set就类似于阻塞队列,让当前线程加入wait set。

进入阻塞状态之后,怎样才能苏醒呢?

  • 可以通过interrupt中断阻塞
  • 可以通过notify唤醒线程,但必须前提持有对象的monitor,并且是同一对象
private final Object MUTEX = new Object();
private synchronized void testWait(){
try{
MUTEX.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}

private synchronized void testNotify(){
MUTEX.notify();
}

如果执行了wait 也就是对象阻塞了,没有唤醒,此时其他线程会不会获得monitor

线程池原理及自定义线程

自JDK1.5起,utils包提供了ExutorService线程池的实现,主要目的是为了重复利用线程,提高效率。从前面可以得知Thread是一个重量级的资源,创建、启动 和销毁都需要消耗大量的系统资源。那么线程池究竟是怎样的一个结构呢?

其实线程池就相当于一个池子,里面装满了线程,当任务执行的时候就会有线程去执行,如果线程不够就会扩充新的线程到池子里,但是线程的数量是有限的,就像水池一样,是由深度的,只能容纳固定的水量。

为了能够异步的提交任务和缓存未处理的任务,会有一个任务队列。当线程添加足够时,会有一个拒绝策略

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值