Java 并发编程在各主流框架中的应用

Java 内存操作协议

1:java内存定义了8中完成主内存和工作内存的变量访问【顺序不是从上向下】

read:把一个变量的值从主内存传输到工作内存,以便随后的load方法
load把从主内存中读取的变量值转入工作内存的变量副本中
use:把工作内存中一个变量的值传递给java虚拟机执行引擎
assign:把从执行引擎收到的变量复制给工作内存中的变量
store:把工作内存中的一个变量值传送到主内存,以便随后write操作
write:工作内存传递过来的变量值写在主内存
lock:把主内存的一个变量表示为某个线程独占的状态
unlock:把主内存中的一个处于锁定状态的变量释放出来,被释放的变量才可以被其他线程锁定

2:内存模型三大特性  volatile-->可见性  防止指令重排序

1: 原子性:不可分割----》java内存模型直接保证的原子性操作包括read,load,use,assign,store,write,lock,unlocko这八个。
2:可见性:多线程操作一个变量,其他线程也可以感知到,java内存模型是通过在变量修改后将新值同步会主内存,在变量读取前从主内存刷新变量,用主内存作为传递媒介方式来实现可见性,volatile可以保证新值能立即同步到内存,以及每次只用前都从主内存刷新,synchronized也提供了可见性,synchronized的可见性是由‘对一个变量执行unlock操作之前’,必须先把次变量同步会主内存中,执行(store,write操作)
3:有序性:–>指令重排 volatile通过加入内存屏障执行来禁止内存的重排序,synchronized通过加锁,保证同一时刻只有一个线程执行同步代码


volatile介绍【一般volatile结合锁进行使用,一个线程写,用加锁否,搜则必须加锁】
1:volatile可以防止保证线程之间可见性木有问题,但是volatile不能代替传统的锁,不能保证互斥性,就是多个线程来访问一个变量的时候,还是会产生多线程问题,不能靠volatile来完全代替传统的锁 volatile最适用的场景:一个线程写,多个线程读,如果多个线程来访问这个变量,仍然需要进行加锁或者使用线程安全的工具类

 3        ThreadLocal 的常见应用

1:ThreadLocal介绍:称为线程本地存储区,(Thread Local Storage 简称TLS)每个线程都有自己的私有的本地存储区域,使用set方法存入该线程本地存储区,使用get方法可以获取到之前存入的值。


ThreadLocal 的实现原理
* 下面的 getMap()方法 传入当前线程,获得一个ThreadLocalMap对象,说明每一个线程维护了 * 自己的一个 map,保证读取出来的value是自己线程的。 * * ThreadLocalMap 是ThreadLocal静态内部类,存储value的键值就是ThreadLocal本身。 * * 因此可以断定,每个线程维护一个ThreadLocalMap的键值对映射Map。不同线程的Map的 key值 是一样的, * 都是ThreadLocal,但 value 是不同的。

public class ThreadLocal<T> {

    /**
     * 下面的 getMap()方法 传入当前线程,获得一个ThreadLocalMap对象,说明每一个线程维护了
     * 自己的一个 map,保证读取出来的value是自己线程的。
     *
     * ThreadLocalMap 是ThreadLocal静态内部类,存储value的键值就是ThreadLocal本身。
     *
     * 因此可以断定,每个线程维护一个ThreadLocalMap的键值对映射Map。不同线程的Map的 key值 是一样的,
     * 都是ThreadLocal,但 value 是不同的。
     */
    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 在 Spring 中的使用

Spring事物中使用了大量的ThreadLocal,比如TransactionSynchronizationManger维护了一系列的ThreadLocal变量

/**
 * 管理每个线程的资源和事务同步的中心帮助程序。供资源管理代码使用,但不供典型应用程序代码使用。
 *
 * 资源管理代码应该检查线程绑定的资源,如,JDBC连接 或 Hibernate Sessions。
 * 此类代码通常不应该将资源绑定到线程,因为这是事务管理器的职责。另一个选项是,
 * 如果事务同步处于活动状态,则在首次使用时延迟绑定,以执行跨任意数量资源的事务。
 */
public abstract class TransactionSynchronizationManager {

    /**
     *  一般是一个线程持有一个 独立的事务,以相互隔离地处理各自的事务。
     *  所以这里使用了很多 ThreadLocal对象,为每个线程绑定 对应的事务属性及资源,
     *  以便后续使用时能直接获取。
     */
    private static final ThreadLocal<Map<Object, Object>> resources =
            new NamedThreadLocal<Map<Object, Object>>("Transactional resources");

    private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
            new NamedThreadLocal<Set<TransactionSynchronization>>("Transaction synchronizations");
		//TODO  NamedThreadLocal这个是ThreadLocal的子类 可以定义ThreadLocal的名字
    private static final ThreadLocal<String> currentTransactionName =
            new NamedThreadLocal<String>("Current transaction name");

    private static final ThreadLocal<Boolean> currentTransactionReadOnly =
            new NamedThreadLocal<Boolean>("Current transaction read-only status");

    private static final ThreadLocal<Integer> currentTransactionIsolationLevel =
            new NamedThreadLocal<Integer>("Current transaction isolation level");

    private static final ThreadLocal<Boolean> actualTransactionActive =
            new NamedThreadLocal<Boolean>("Actual transaction active");

    /**
     * 为当前线程 绑定 对应的resource资源
     */
    public static void bindResource(Object key, Object value) throws IllegalStateException {
        Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
        Assert.notNull(value, "Value must not be null");
        Map<Object, Object> map = resources.get();
        // 如果当前线程的 resources中,绑定的数据map为空,则为 resources 绑定 map
        if (map == null) {
            map = new HashMap<Object, Object>();
            resources.set(map);
        }
        Object oldValue = map.put(actualKey, value);
        if (oldValue instanceof ResourceHolder && ((ResourceHolder) oldValue).isVoid()) {
            oldValue = null;
        }
        if (oldValue != null) {
            throw new IllegalStateException("Already value [" + oldValue + "] for key [" +
                    actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");
        }
        if (logger.isTraceEnabled()) {
            logger.trace("Bound value [" + value + "] for key [" + actualKey + "] to thread [" +
                    Thread.currentThread().getName() + "]");
        }
    }

    /**
     * 返回当前线程绑定的所有资源
     */
    public static Map<Object, Object> getResourceMap() {
        Map<Object, Object> map = resources.get();
        return (map != null ? Collections.unmodifiableMap(map) : Collections.emptyMap());
    }
}

4   ThreadLocal 在 Mybatis 中的使用

1:之前dmc封装的数据库Mybatis就是使用了ThreadLocal来管理连接的sqlsession对象中的数值

public class SqlSessionManager implements SqlSessionFactory, SqlSession {

  private final ThreadLocal<SqlSession> localSqlSession = new ThreadLocal<>();

  public void startManagedSession() {
    this.localSqlSession.set(openSession());
  }

  public void startManagedSession(boolean autoCommit) {
    this.localSqlSession.set(openSession(autoCommit));
  }

  public void startManagedSession(Connection connection) {
    this.localSqlSession.set(openSession(connection));
  }

  public void startManagedSession(TransactionIsolationLevel level) {
    this.localSqlSession.set(openSession(level));
  }

  public void startManagedSession(ExecutorType execType) {
    this.localSqlSession.set(openSession(execType));
  }

  public void startManagedSession(ExecutorType execType, boolean autoCommit) {
    this.localSqlSession.set(openSession(execType, autoCommit));
  }

  public void startManagedSession(ExecutorType execType, TransactionIsolationLevel level) {
    this.localSqlSession.set(openSession(execType, level));
  }

  public void startManagedSession(ExecutorType execType, Connection connection) {
    this.localSqlSession.set(openSession(execType, connection));
  }

  public boolean isManagedSessionStarted() {
    return this.localSqlSession.get() != null;
  }

  @Override
  public Connection getConnection() {
    final SqlSession sqlSession = localSqlSession.get();
    if (sqlSession == null) {
      throw new SqlSessionException("Error:  Cannot get connection.  No managed session is started.");
    }
    return sqlSession.getConnection();
  }

  @Override
  public void clearCache() {
    final SqlSession sqlSession = localSqlSession.get();
    if (sqlSession == null) {
      throw new SqlSessionException("Error:  Cannot clear the cache.  No managed session is started.");
    }
    sqlSession.clearCache();
  }

  @Override
  public void commit() {
    final SqlSession sqlSession = localSqlSession.get();
    if (sqlSession == null) {
      throw new SqlSessionException("Error:  Cannot commit.  No managed session is started.");
    }
    sqlSession.commit();
  }

  @Override
  public void commit(boolean force) {
    final SqlSession sqlSession = localSqlSession.get();
    if (sqlSession == null) {
      throw new SqlSessionException("Error:  Cannot commit.  No managed session is started.");
    }
    sqlSession.commit(force);
  }

  @Override
  public void rollback() {
    final SqlSession sqlSession = localSqlSession.get();
    if (sqlSession == null) {
      throw new SqlSessionException("Error:  Cannot rollback.  No managed session is started.");
    }
    sqlSession.rollback();
  }

  @Override
  public void rollback(boolean force) {
    final SqlSession sqlSession = localSqlSession.get();
    if (sqlSession == null) {
      throw new SqlSessionException("Error:  Cannot rollback.  No managed session is started.");
    }
    sqlSession.rollback(force);
  }

  @Override
  public List<BatchResult> flushStatements() {
    final SqlSession sqlSession = localSqlSession.get();
    if (sqlSession == null) {
      throw new SqlSessionException("Error:  Cannot rollback.  No managed session is started.");
    }
    return sqlSession.flushStatements();
  }

  @Override
  public void close() {
    final SqlSession sqlSession = localSqlSession.get();
    if (sqlSession == null) {
      throw new SqlSessionException("Error:  Cannot close.  No managed session is started.");
    }
    try {
      sqlSession.close();
    } finally {
      localSqlSession.set(null);
    }
  }
}

J.U.C 包的实际应用

1:ThreadPoolExecutor 源码

public class ThreadPoolExecutor extends AbstractExecutorService {

/**
 * 核心线程数
 * 当向线程池提交一个任务时,若线程池已创建的线程数小于corePoolSize,即便此时存在空闲线程,
 * 也会通过创建一个新线程来执行该任务,直到已创建的线程数大于或等于corePoolSize
 */
private volatile int corePoolSize;

/**
 * 最大线程数
 * 当队列满了,且已创建的线程数小于maximumPoolSize,则线程池会创建新的线程来执行任务。
 * 另外,对于无界队列,可忽略该参数
 */
private volatile int maximumPoolSize;
/**
 * 线程存活保持时间
 * 当线程池中线程数 超出核心线程数,且线程的空闲时间也超过 keepAliveTime时,
 * 那么这个线程就会被销毁,直到线程池中的线程数小于等于核心线程数
 */
private volatile long keepAliveTime;

/**
 * 任务队列
 * 用于传输和保存等待执行任务的阻塞队列
 */
private final BlockingQueue<Runnable> workQueue;

/**
 * 线程工厂
 * 用于创建新线程。threadFactory 创建的线程也是采用 new Thread() 方式,threadFactory
 * 创建的线程名都具有统一的风格:pool-m-thread-n(m为线程池的编号,n为线程池中线程的编号
 */
private volatile ThreadFactory threadFactory;

/**
 * 线程饱和策略
 * 当线程池和队列都满了,再加入的线程会执行此策略
 */
private volatile RejectedExecutionHandler handler;

/**
 * 构造方法提供了多种重载,但实际上都使用了最后一个重载 完成了实例化
 */
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);
}

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         threadFactory, defaultHandler);
}

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          RejectedExecutionHandler handler) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), handler);
}

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

/**
 * 执行一个任务,但没有返回值
 */
public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    int c = ctl.get();
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        if (! isRunning(recheck) && remove(command))
            reject(command);
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    else if (!addWorker(command, false))
        reject(command);
}

/**
 * 提交一个线程任务,有返回值。该方法继承自其父类 AbstractExecutorService,有多种重载,这是最常用的一个。
 * 通过future.get()获取返回值(阻塞直到任务执行完)
 */
public <T> Future<T> submit(Callable<T> task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<T> ftask = newTaskFor(task);
    execute(ftask);
    return ftask;
}

/**
 * 关闭线程池,不再接收新的任务,但会把已有的任务执行完
 */
public void shutdown() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        checkShutdownAccess();
        advanceRunState(SHUTDOWN);
        interruptIdleWorkers();
        onShutdown(); // hook for ScheduledThreadPoolExecutor
    } finally {
        mainLock.unlock();
    }
    tryTerminate();
}

/**
 * 立即关闭线程池,已有的任务也会被抛弃
 */
public List<Runnable> shutdownNow() {
    List<Runnable> tasks;
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        checkShutdownAccess();
        advanceRunState(STOP);
        interruptWorkers();
        tasks = drainQueue();
    } finally {
        mainLock.unlock();
    }
    tryTerminate();
    return tasks;
}

public boolean isShutdown() {
    return ! isRunning(ctl.get());
}

}

2:Executors 提供的 4 种线程池   这个的底层也是ThreadPoolExecutor 进行封装的

1: 提供了四种线程池 Executors 类 通过 ThreadPoolExecutor 封装了 4 种常用的线程池:CachedThreadPool,FixedThreadPool,ScheduledThreadPool 和 SingleThreadExecutor。其功能如下。

介绍:

  1. CachedThreadPool:(可缓存的线程池) 用来创建一个几乎可以无限扩大的线程池【最大的线程数为Integer.MAX_value】,适用于执行大量短生命周期的异步任务
  2. FixedThreadPool:(固定大小的线程池) 保证线程数可控,不会造成线程过多。
  3. SingleThreadExecutor:(单例的线程池)可以保证任务按调用顺序执行。
  4. ScheduledThreadPool:(定时线程池) 适用于执行 延时 或者周期性任务

如何配置线程池 【一定要牢记,实战创建线程池需要考虑进行参数的配置】

1: cpu密集型任务 :尽量使用较小的线程池,一般为CPU核心数+1。因为CPU密集型任务使得CPU使用率很高,若开过多的线程,会造成CPU过度切换
2:IO 密集型任务:可以使用稍大的线程池,一般为 2*CPU 核心数。 IO 密集型任务 CPU 使用率 并不高,因此可以让 CPU 在等待 IO 的时候有其他线程去处理别的任务,充分利用 CPU 时间。

线程池的实际应用---------》Tomcat 在分发 web 请求 时使用了线程池来处理。

BlockingQueue

> 核心方法  -----------{BlockingQueue当前为接口}

public interface BlockingQueue extends Queue {

// 将给定元素设置到队列中,如果设置成功返回true, 否则返回false。如果是往限定了长度的队列中设置值,推荐使用offer()方法。
boolean add(E e);

// 将给定的元素设置到队列中,如果设置成功返回true, 否则返回false. e的值不能为空,否则抛出空指针异常。
boolean offer(E e);

// 将元素设置到队列中,如果队列中没有多余的空间,该方法会一直阻塞,直到队列中有多余的空间。
void put(E e) throws InterruptedException;

// 将给定元素在给定的时间内设置到队列中,如果设置成功返回true, 否则返回false.
boolean offer(E e, long timeout, TimeUnit unit)
    throws InterruptedException;

// 从队列中获取值,如果队列中没有值,线程会一直阻塞,直到队列中有值,并且该方法取得了该值。
E take() throws InterruptedException;

// 在给定的时间里,从队列中获取值,时间到了直接调用普通的 poll()方法,为null则直接返回null。
E poll(long timeout, TimeUnit unit)
    throws InterruptedException;

// 获取队列中剩余的空间。
int remainingCapacity();

// 从队列中移除指定的值。
boolean remove(Object o);

// 判断队列中是否拥有该值。
public boolean contains(Object o);

// 将队列中值,全部移除,并发设置到给定的集合中。
int drainTo(Collection<? super E> c);

// 指定最多数量限制将队列中值,全部移除,并发设置到给定的集合中。
int drainTo(Collection<? super E> c, int maxElements);

}

主要实现类
1:ArrayBlockingQueue :基于数组的阻塞队列实现,在ArrayBlockingQueue内部,维护了一个定长数组,用来缓存队列中的数据对象,这是一个常用的阻塞队列,内部还保存了两个整形变量,分别标识着队列的头部和尾部在数组中的位置 ,ArrayyBlockQueue和LinkedBlockingQueue间生产者放入数据和消费者获取数据时,都是公用同一个锁对象,由此也意味着无法真正并行运行,这点尤其不同于LinkedBlockIngQueue,这两者还有一个明显的不同之处在于,ArrayBlockingQueue在插入或者删除元素时不会产生或销毁任何额外的对象实例,而后者则会生成一个额外的Node对象,这在长时间内需要高效并发的处理大批量数据的系统中,其对于GC的影响还是存在一定的区别,另外一个就是,而在创建ArrayBlockingQueue时,我们还可以控制对象的内部锁是否采用公平锁,默认采用非公平锁

2: LinkedBlockingQueue 基于链表的阻塞队列,同ArrayListBlockingQueue类似,其内部也维护了一个数据缓冲队列(该队列由一个链表生成),当生产者往队列中放入一个数据的时候,缓存到队列内部,生产者直接进行返回,只有当队列缓冲区到达最大值缓存容量时,(LinkedBlockingQueue可以通过构造函数指定该值),才会阻塞生产者队列,直到消费者从队列中消费掉一份数据,生产者线程才会被唤醒,反之对于消费者这端的处理也是基于同样的原理。而LinkedBlockingQueue之所以能够高效的处理并发数据,还因为其对于生产者端和消费者端分别采用了独立的锁来控制数据同步,也就是说在高并发的情况下生产者和消费者可以并行的操作队列中的数据,以此来提高整个队列的并发性能


如果构造一个LinkedBlockingQueue对象,木有指定容量大小,LinkedBlockingQueue会默认一个类似无线大小的容量(Integer,Max_VALUE),这样的话,如果生产者速度大于消费者,也许还木有等到队列满阻塞产生,系统内存就有可能被消耗殆尽了


3: PriorityBlockingQueue : 基于优先级的阻塞队列(优先级的判断通过构造函数传入的Compator对象来决定),但需要注意的是PriorityBlockIngQueue并不会阻塞数据生产者,只会在木有可消费的数据时,阻塞数据的消费者,使用的时候需要特别注意,生产者生产数据的速度,否则时间一长,会消耗所有的可用堆内存空间,在实现PriorityBlockingQueue时,内部控制线程同步的锁采用的是公平锁

CAS指令和原子类(应用比较多的就是计数器)

1:互斥同步最重要的问题就是进行线程阻塞和唤醒所带来的性能额外损耗,被称为阻塞同步,悲观锁,随着硬件和操作系统指令集的发展和优化,产生了非阻塞同步,乐观锁,—》cas

目前:在java中应用最广泛的非同步阻塞就是CAS,可以使用cas操作,该操作由sun.misu.Unsafe类里面的compareAndWapInt()和compareAndSwapLong()等方法实现。通常情况下,sun.misc.Unsafe类对于开发者是不可见的,因此JDK提供了很多CAS包装类,简化开发者使用,如AtomicInteger,使用java自带的Atomic原子类,可以避免同步锁带来的并发访问性能降低的问题,减少犯错的机会。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值