Synchronized关键字:
实现:利用每个对象的内部锁进行同步;
使用1:
public synchronized void myMethod(){
if(!(ok to process))
await();
//add your code here
notifyAll();
}
使用2:
Object obj = new Object();
public synchronized void myMethod(){
syschronized(obj){
if(!(ok to process))
obj.await();
//add your code here
obj.notifyAll();
}
}
ReentrantLock类:
使用:
Lock mlock = new ReentrantLock();
public void myMethod(){
mlock.lock();
try{
//add your code here
}
finally{
mlock.unlock();
}
}
注意:
锁是可以重入的,一个线程可以重复的获得已经持有的锁;
ReentrantReadWriteLock类:
适用于很多线程从一个数据结构读取数据而很少的线程修改其中数据;
使用:
ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
//该锁可以被多个读操作共用的读锁,但会排斥写操作
Lock readLock = rwl.readLock();
//该锁排斥所有其他的读操作和写操作
Lock writeLock = rwl.writeLock();
public int getNumber(){
readLock.lock();
try{
//add your code here
}
finally{
readLock.unlock();
}
}
public void setNumber(){
writeLock.lock();
try{
//add your code here
}
finally{
writeLock.unlock();
}
}
Condition条件:(与ReentrantLock配套使用)
使用:
Lock mlock = new ReentrantLock();
Condition mCondition = mlock.newCondition();
public void myMethod(){
mlock.lock();
try{
if(!(ok to process))
mConditon.await();
//add your code here
mConditon.signalAll();
}
finally{
mlock.unlock();
}
}
同步器:
- Semaphore(信号量):
- 信号量维持一个计数,许可的数目时固定的,由此限制了通过的线程数量;
- 线程通过调用acquire请求许可,通过release()释放许可;
- 许可不是必须由获取它的线程释放,任何线程都可以释放任意数目的许可;
- CountDownLatch(倒计时门栓):
- 让一个线程集等待直到计数器变为0;
- 倒计时门栓是一次性的,一旦计数为0,就不能再重用了;
- 调用countDown则计数减1;
- CyclicBarrier(障栅):
- 当指定数目的线程都完成了相应的工作后,到达障栅处,此时障栅自动撤销,线程可以继续运行;
- 使用1
static int data=0; static int nthreads = 10; static CyclicBarrier barrier = new CyclicBarrier(nthreads); public static void main(String[] args) { for(int ii=0;ii<nthreads;ii++){ Runnable myAction = new Runnable(){ public void run(){ //add pre-action data++; try { barrier.await(); System.out.println(data); //add post-action } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } } }; new Thread(myAction).start(); } } //会打印输出10个10;破栅后会执行执行await后面的代码
- 使用2:
static int data=0; static int nthreads = 10; static Runnable barrierAction = new Runnable(){ @Override public void run() { //add post-action System.out.println(data); } }; static CyclicBarrier barrier = new CyclicBarrier(nthreads,barrierAction); public static void main(String[] args) { for(int ii=0;ii<nthreads;ii++){ Runnable myAction = new Runnable(){ public void run(){ //add pre-action data++; try { barrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } } }; new Thread(myAction).start(); } }//会打印输出1个10;破栅后会先执行barrierAction的run方法,随后执行await后面的代码
- Exchanger(交换器):
- 两个线程在同一个缓冲区的两个实例上工作;
- 如一个向缓冲区写,一个从缓冲区读;
- 当各自完成了各自的工作后交换缓冲区;
- SynchronousQueue(同步队列):
- 实现了BlockingQueue接口
- 不过它的size永远是0;
- 调用put方法时将一直阻塞下去,知道另一个线程调用了take方法为止;
Volatile域:
保证读取到的值是当前最新的;用该关键字声明的变量具备可见性;
阻塞队列:
java.util.concurrent包下有如下阻塞队列(使用 ReentrantLock实现线程安全)
- LinkedBlockingQueue:容量没上限,也可指定最大容量,是一个双端的版本
- ArrayBlockingQueue:构造时需要指定容量
- PriorityBlockingQueue:优先级队列
- 注意:使用put方法添加元素,使用take方法获取元素,这两个方法是阻塞的
线程安全集合:
java.util.concurrent包下(使用Synchronized关键字或者ReentrantLock实现线程安全)
- ConcurrentHashMap
- ConcurrentSkipListMap
- ConcurrentSkipListSet
- ConcurrentLinkedQueue
- CopyOnWriteArrayList //往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。读多写少的并发场景。只能保证数据的最终一致性,不能保证数据的实时一致性。虽然读取操作不阻塞,但是每次在读取该对象的数据时确保get的index参数不超过访问数组的边界!否则容易边界越界的情况。get方法其实是不安全的访问方法,使用COWIterator则是绝对安全的操作,因为该对象的引用是数据修改之前的那个数组,它是不会变化的!!
- CopyOnWriteArraySet
java.util包下(使用Synchronized关键字实现线程安全)
- Vector
- Hashtable //key/value均不支持是null