Java并发、同步总结

来源:互联网 发布:尚学堂java ppt 编辑:程序博客网 时间:2024/06/09 13:50

Java中提供并发控制的两种方式:1、同步关键字 2、锁

Java 5.0之前使用的是同步关键字Synchronized和volatile,他们是jvm中的隐式锁
Synchronized和volatile的实现是基于jvm指令的同步代码块实现的。
添加同步关键字后,会在jvm代码块指令前后添加monitorexit和monitorenter两个同步指令。
但是Synchronized修饰的方法却不一样,同步方法的实现是JVM定义了方法的访问标志ACC_SYNCHRONIZED在方法表中,JVM将同步方法前面设置这个标志。

同步代码块和同步方法的字节码指令(javap -c classname)

 public void syncBlockImpl();    Code:       0: aload_0       1: dup       2: astore_1       3: monitorenter       4: getstatic     #15                 // Field java/lang/System.out:Ljava/io/PrintStream;       7: ldc           #23                 // String hello world       9: invokevirtual #22                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V      12: aload_1      13: monitorexit      14: goto          22      17: astore_2      18: aload_1      19: monitorexit      20: aload_2      21: athrow      22: return    Exception table:       from    to  target type           4    14    17   any          17    20    17   any  public synchronized void syncMethodImpl();    Code:       0: getstatic     #15                 // Field java/lang/System.out:Ljava/io/PrintStream;       3: ldc           #23                 // String hello world       5: invokevirtual #22                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V       8: return

Java 5.0之后,JDK添加了多线程并发库java.util.concurrent,提供了线程池(Executors)、信号量(Semaphore)、重入锁(ReentrantLock)、读写锁(ReentrantWriteReadLock)和一些线程安全的集合类(ConcurrentHashMap/ConcurrentLinkedQueue)、CountDownLatch

无论是Semaphore还是ReentrantLock都是基于AbstractQueueSynchronized实现的,AQS实现了自己的算法来实现共享资源的合理控制。AQS中核心的变量替换方法(原子性)是借助CPU硬件指令集compareAndSweep实现的,等待队列是由虚拟无界双向链表CLH实现的。每一个等待线程对一个Node,Node中存储了当前线程引用、等待状态、前一个或后一个等待节点。

CLH锁,是一种基于链表的可扩展、高性能、公平的自旋锁。

锁分为公平锁和协商锁(非公平锁),其中公平锁的开销要大。

一、信号量Semaphore的用法

ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(5, false);//非公平信号量
//开启20个线程
for (int i = 0; i < 20; i++) {
final int Num = i;
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
//线程开始时,获取许可
semaphore.acquire();
System.out.println(“Thread ” + Num + ” accessing”);
Thread.sleep((long) (Math.random() * 10000));
//访问完后释放
semaphore.release();
System.out.println(“available permit = ” + semaphore.availablePermits());
} catch (InterruptedException e) {
e.printStackTrace();
}

    }};

executorService.submit(runnable);
}

Semaphore本质为共享锁,用来限制多个线程对有限资源访问进行控制。

二、ReentrantLock 可重入锁 独占锁

比Synchronized要高效,因为在高度争用情况下,处理器把大部分时间都花在任务处理上,而不是线程调度上
特性:时间等候锁,可中断锁,多个条件变量,锁投票,无块结构锁
一般用法:
try {
reentrantLock.lock();
System.out.println(str + ” 获取锁 “);
doSomeThing(str);
Thread.sleep((long) (Math.random() * 10000));
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println(str + ” 释放锁 “);
reentrantLock.unlock();
}

时间等候锁,在一定时间内获取不到锁,则返回false

boolean captured = false;
try {
//在2秒内尝试获取锁
captured = reentrantLock.tryLock(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
System.out.println(“tryLock(10, TimeUnit.SECONDS) ” + captured);
} finally {
if (captured) {
reentrantLock.unlock();
}
}

可中断锁,获取锁的线程可被自己或其他线程中断

try {
reentrantLock.lockInterruptibly();
System.out.println(Thread.currentThread().getName() + ” 获取锁 “);
try {
Thread.sleep(5 * 1000);
reentrantLockTest.plusMethod();
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(Thread.currentThread().getName() + “被中断 “);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
System.out.println(Thread.currentThread().getName() + ” 释放锁 “);
}

多条件变/
* 生产者-消费者
* 多条件锁
*


* Created by zczhang on 16/9/27.
*/
public class ProductQueue {
private T[] items;
private final Lock lock = new ReentrantLock();
private Condition notFull = lock.newCondition();
private Condition notEmpty = lock.newCondition();

private int head, tail, count;public ProductQueue() {    this(10);}public ProductQueue(int maxSize) {    items = (T[]) new Object[maxSize];}public void put(T t) throws InterruptedException {    lock.lock();    try {        while (count == getCapacity()) {            System.out.println("---------队列满,生产者等待------");            notFull.await();        }        items[tail] = t;        if (++tail == getCapacity()) {            tail = 0;        }        ++count;        notEmpty.signalAll();    } finally {        lock.unlock();    }}public T take() throws InterruptedException {    lock.lock();    try {        while (count == 0) {            System.out.println("---------队列空,消费者" + Thread.currentThread().getName() + "等待------");            notEmpty.await();        }        T item = items[head];        items[head] = null;        if (++head == getCapacity()) {            head = 0;        }        --count;        notFull.signalAll();        return item;    } finally {        lock.unlock();    }}public int size() {    lock.lock();    try {        return count;    } finally {        lock.unlock();    }}public int getCapacity() {    return items.length;}

}eentrantReadWriteLock读写锁

读锁为共享锁,写锁为独占锁。读读不互斥,读写互斥,写写互斥
写锁支持Condition,读锁不支持Condition。
支持锁降级,即写锁可以获取读锁,然后释放写锁,这样写锁就变成了读锁
不支持锁升级,即读锁不能直接获取写锁。需将读锁释放后再获取写锁。
支持锁中断
读写锁的最大数量只能是65535(包括重入数)

private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();private ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();private ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();private int resource = 0;public int read() {    try {        try {            Thread.sleep(2000);        } catch (InterruptedException e) {            e.printStackTrace();        }        readLock.lock();        System.out.println(Thread.currentThread().getName() + "获取读锁,尝试读取");        return resource;    } finally {        readLock.unlock();        System.out.println(Thread.currentThread().getName() + "释放读锁");    }}public void write(int newValue) {    try {        try {            Thread.sleep(1000);        } catch (InterruptedException e) {            e.printStackTrace();        }        writeLock.lock();        System.out.println(Thread.currentThread().getName() + "获取写锁,尝试写入");        this.resource = newValue;        System.out.println(Thread.currentThread().getName() + "写入值 " + newValue);    } finally {        writeLock.unlock();        System.out.println(Thread.currentThread().getName() + "释放写锁");    }}

四、CountDownLatch

/** * CountDownLatch 同步辅助类 * 指定数字count初始化,然后调用await方法的线程会一直阻塞,直到count变为0 * <p> * 一个同步辅助类,允许一个或多个线程等待,一直到其他线程完成任务 * 使用场景:1.主线程开启多个子线程去执行分解的任务,当所有子线程都完成后,主线程再继续执行。 * 2.主线程先运行,子线程再运行,主线程等待子线程完成再运行 * <p> * Created by zczhang on 16/9/28. */public class CountDownLatchTest {    public static void main(String[] args) throws InterruptedException {        int childTaskNum = 3;        CountDownLatch startLatch = new CountDownLatch(1);        CountDownLatch childEndLatch = new CountDownLatch(childTaskNum);        for (int i = 0; i < childTaskNum; i++) {            Thread child = new Thread(new ChildTaskRun(startLatch,childEndLatch));            child.start();        }        System.out.println("主线程处理一些任务...");        Thread.sleep(2*1000);        System.out.println("主线程处理一些任务完成");        startLatch.countDown();        System.out.println("主线程等待子线程处理任务完成...");        childEndLatch.await();        System.out.println("主线程处理任务结果");    }    private static class ChildTaskRun implements Runnable {        private CountDownLatch mainStartLatch;        private CountDownLatch childEndLatch;        public ChildTaskRun(CountDownLatch mainStartLatch, CountDownLatch childEndLatch) {            this.mainStartLatch = mainStartLatch;            this.childEndLatch = childEndLatch;        }        @Override        public void run() {            try {                mainStartLatch.await();                doSomeThing();                System.out.println("子线程 " + Thread.currentThread().getName() + "处理任务完成");                childEndLatch.countDown();            } catch (InterruptedException e) {                e.printStackTrace();            }        }        private void doSomeThing() {            System.out.println("子线程 " + Thread.currentThread().getName() + "处理任务中");            try {                Thread.sleep(2 * 1000);            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}

五、CyclicBarrier

/** * 回环栅栏 同步辅助类 * 可实现让一组线程等待至某个状态之后再全部同时执行,这个状态就叫做一个栅栏. * 当所有线程都被释放后,该栅栏可以重用 * <p/> * 使用场景:开启多个子线程处理同步任务,当所有子任务都处理完成后,各个子线程再处理其他任务. * 同时主线程可插入任务,当最后一个子线程完成同步任务后,执行主线程插入任务,然后子线程再处理其他任务 * <p/> * Created by zczhang on 16/9/28. */public class CyclicBarrierTest {    public static void main(String[] args) {        int childTaskNum = 3;        CyclicBarrier cyclicBarrier = new CyclicBarrier(childTaskNum, new Runnable() {            @Override            public void run() {                System.out.println("当最后一个线程 " + Thread.currentThread().getName() + "完成同步任务时,做一些事情...");            }        });        for (int i = 0; i < childTaskNum; i++) {            Thread thread = new Thread(new ChildTaskRun(cyclicBarrier));            thread.start();        }    }    public static class ChildTaskRun implements Runnable {        private CyclicBarrier cyclicBarrier;        public ChildTaskRun(CyclicBarrier cyclicBarrier) {            this.cyclicBarrier = cyclicBarrier;        }        @Override        public void run() {            System.out.println("子线程" + Thread.currentThread().getName() + "开始处理任务...");            try {                Thread.sleep(3 * 1000);            } catch (InterruptedException e) {                e.printStackTrace();            }            System.out.println("子线程" + Thread.currentThread().getName() + "处理任务完成,等待其他线程...");            try {                cyclicBarrier.await();            } catch (InterruptedException e) {                e.printStackTrace();            } catch (BrokenBarrierException e) {                e.printStackTrace();            }            System.out.println("所有子线程完成同步任务, 继续执行其他任务...");        }    }}
0 0