Java并发、同步总结

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("所有子线程完成同步任务, 继续执行其他任务...");

        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值