JAVA并发之多线程基础(5)

上面介绍了并发编程中的栅栏等JAVA并发之多线程基础(4) 。经过惟一的一个终点线来帮助肯定线程是多晚开始执行下一次操做。java

LockSupport

提供了一个比较底层的线程挂起操做。有点相似于suspend()方法,可是这个方法不建议使用。编程

  1. park()使得当前线程挂起。里面也是调用了Unsafe类进行底层操做。
public static void park() {
        UNSAFE.park(false, 0L);
    }
复制代码

2.unpark(Thread thread)是将当前线程继续往下执行。数组

public static void unpark(Thread thread) {
        if (thread != null)
            UNSAFE.unpark(thread);
    }
复制代码

LockSupport上面的两个方法不像suspend()resume()方法。在这两个方法中resume()必须在suspend()以前执行。不然线程就会被永远的挂起,形成死锁。而LockSupport中的unpark能够在park以前。安全

package com.montos.lock;
import java.util.concurrent.locks.LockSupport;

public class LockSupportDemo {
	public static Object obj = new Object();
	static ChangeObjectThread t1 = new ChangeObjectThread("t1");
	static ChangeObjectThread t2 = new ChangeObjectThread("t2");

	public static class ChangeObjectThread extends Thread {
		public ChangeObjectThread(String name) {
			super(name);
		}
		@Override
		public void run() {
			synchronized (obj) {
				System.out.println("in " + getName());
				LockSupport.park();
			}
		}
	}
	public static void main(String[] args) throws InterruptedException {
		t1.start();
		Thread.sleep(100);
		t2.start();
		LockSupport.unpark(t1);
		LockSupport.unpark(t2);
		t1.join();
		t2.join();
	}
}
复制代码

经过上面的一个小的Demo能够看出LockSupport的使用方法,在使用方法中也是很简单的。以上篇幅就对JDK内的并发类进行了讲解。大多数用到了CAS无锁操做。避免线程阻塞的状况发生。多线程

并发集合

在平常的业务处理中,集合类是咱们使用最多的一个操做。对应的也就是三大类:MapSet以及最后的List。在平常并发量小的状况下,也许咱们会这么使用对应的集合操做:并发

//Map集合
private static final Map<String,String> map = Collections.synchronizedMap(new HashMap<String,String>());
//List集合 
private static final List<String> list = Collections.synchronizedList(new ArrayList<String>());
//Set集合
private static final Set<String> set = Collections.synchronizedSet(new HashSet<>());
复制代码

上面的使用了Collections中的同步方法进行包装。深刻了解里面的调用过程,也就是各个方法上面加上了synchronized进行限制。从而达到最后线程安全的目的。若是并发量很大的话,是很会影响性能的,毕竟它使得对应的读和写操做都变成了串行。因而有了下面的几种线程安全类:ide

  • ConcurrentHashMap高性能并发的Map。里面的操做是跟HashMap是同样的。可是里面多了一种结构:Segment(段)。
static class Segment<K,V> extends ReentrantLock implements Serializable {
        private static final long serialVersionUID = 2249069246763182397L;
        final float loadFactor;
        Segment(float lf) { this.loadFactor = lf; }
    }
复制代码

能够看出他是继承了重入锁进行实现的。为何会有段的概念在这里面呢?为了更好的支持高并发而设计的一个概念。每一个元素在特定的段中,将整个集合分红许多段进行管理,而高并发的时候,只要对每一个段进行控制就能起到一个同步的做用。高并发

再统计数量大小的时候,ConcurrentHashMap中利用了CounterCell结构体进行统计。每个段中存储了该段的大小,而后再循环求和出当前的数组大小状况。post

final long sumCount() {
        CounterCell[] as = counterCells; CounterCell a;
        long sum = baseCount;
        if (as != null) {
            for (int i = 0; i < as.length; ++i) {
                if ((a = as[i]) != null)
                    sum += a.value;
            }
        }
        return sum;
    }
复制代码
  • BlockingQueue是一个很是好的多线程共享数据的容器。当队列为空的时候,读线程就会进行等待,相反,若是当前队列已满,那么写线程就会等待。看到对应的实现类中都会有下面三个成员变量:
/** Main lock guarding all access */
final ReentrantLock lock; //加锁进行控制访问

/** Condition for waiting takes */
private final Condition notEmpty;//元素获取的时候,进行判断是否为空进行阻塞

/** Condition for waiting puts */
private final Condition notFull;//元素加入的时候,判断队列是否已满
复制代码

这上面的三个变量就控制整个阻塞队列的访问以及元素的增长。从根本上来讲他并不像上面介绍的ConcurrentHashMap访问性能高,可是咱们须要的是他的多线程访问共有数据的能力。性能

阻塞队列的元素获取方法

public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();//获取可中断锁
        try {
            while (count == 0)
                notEmpty.await();//队列为空,进行读线程等待
            return dequeue();
        } finally {
            lock.unlock();
        }
    }
复制代码
private E dequeue() {
        // assert lock.getHoldCount() == 1;
        // assert items[takeIndex] != null;
        final Object[] items = this.items;
        @SuppressWarnings("unchecked")
        E x = (E) items[takeIndex];
        items[takeIndex] = null;
        if (++takeIndex == items.length)
            takeIndex = 0;
        count--;
        if (itrs != null)
            itrs.elementDequeued();
        notFull.signal();//通知写线程进行写入操做
        return x;
    }
复制代码

阻塞队列的元素增长方法

public void put(E e) throws InterruptedException {
        checkNotNull(e);//判断元素是否为空
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();//获取可中断锁
        try {
            while (count == items.length)
                notFull.await();//队列满时,进行写线程等待
            enqueue(e);
        } finally {
            lock.unlock();
        }
    }
复制代码
private void enqueue(E x) {
        // assert lock.getHoldCount() == 1;
        // assert items[putIndex] == null;
        final Object[] items = this.items;
        items[putIndex] = x;
        if (++putIndex == items.length)
            putIndex = 0;
        count++;
        notEmpty.signal();//通知读线程进行操做 
    }
复制代码
  • ConcurrentLinkedQueue上面谈及到的是多线程共享数据的容器,而这个是高并发状况下使用的。里面大量的使用了无锁的操做以及锁自旋,能够很好的保证性能问题,有兴趣的小伙伴能够去了解下。

以上谈及到的就是JDK中关于并发的一些简单介绍。在我介绍完成以后,我会对底层的源码进一步讲解,有兴趣的小伙伴能够关注我~

相关文章
相关标签/搜索