前言
java并发是多线程开发中经常遇到的问题,对并发的处理,java提供了一系列的方法.机制,其中锁是其中的一个,synchronized是最常用的,而java除了synchronized同步,还有其他的同步锁,其他的可以完成其他不同的需求,以下就来说说java提供的一些锁吧~~
ReentrantLock
显示锁,可有多个条件锁,并可对某个条件进行唤醒,其中lock.newCondition()为创建一个条件,con.await()为等待此条件,con.signal()为释放一个此条件的等待,con.signalAll()为释放此条件的所有等待.当然这些操作都要遭lock.lock()与lock.unlock()中使用,不然会报错
private final Condition con;
ReentrantLock reentrantLock;
public ReentrantLockTest() {
reentrantLock = new ReentrantLock();//创建显示锁
con = reentrantLock.newCondition();//创建显示锁的一个条件
for (int i=0;i<10;i++) {
new TestThread().start();//启动10等待线程
}
new TestThread2().start();//唤醒线程
}
public class TestThread extends Thread {
@Override
public void run() {
waitTest();
}
}
public class TestThread2 extends Thread {
@Override
public void run() {
for (int i=0;i<10;i++){
try {
sleep(1000);//1秒唤醒一个
} catch (InterruptedException e) {
e.printStackTrace();
}
signalTest();
}
}
}
public void waitTest(){
reentrantLock.lock();
try {
con.await();//等待此条件
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("testThread");
reentrantLock.unlock();
}
public void signalTest(){
reentrantLock.lock();
con.signal();//释放此一个条件
System.out.println("testThread2");
reentrantLock.unlock();
}
运行结果为
Semaphore
信号量,可用于同步同一时间可以运行几个线程,其中初始化时需指定型号量的数量,在这些数量被申请完后,之后申请的会被阻塞,acquire()为获取一个信号量,release()为释放一个信号量.
public class SemaphoreTest {
final Semaphore semaphore = new Semaphore(3);//数量为3的信号量
public SemaphoreTest() {
for (int i=0;i<5;i++){
new TestThread().start();
}
}
public class TestThread extends Thread{
@Override
public void run() {
try {
semaphore.acquire();//获取一个型号量
System.out.println("剩余信号量:"+semaphore.availablePermits());
sleep(3000);
semaphore.release();//释放信号量
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行结果为
其中前3个运行后需要过3秒在运行下面的两句,因为前3个将信号量用完了.
CyclicBarrier
循环栅栏,可用于等待多个线程完成某操作在一起运行之后的行为,其中初始化时需指定栅栏数,当到达这个栅栏数后会自动解锁,解锁后会将等待数清零,之后又要有指定栅栏数等待才可解锁,以此循环,也可以调用reset()来清零等待数,清零时不解锁,调用await来进行等待
public class CyclicBarrierTest {
CyclicBarrier cyclicBarrier = new CyclicBarrier(2);//等待栅栏数量为5
public CyclicBarrierTest() {
for (int i=0;i<4;i++) {
new TestThread().start();
}
}
public class TestThread extends Thread{
@Override
public void run() {
try {
System.out.println("等待");
cyclicBarrier.await();//等待
System.out.println("运行");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
}
运行结果
CountDownLatch
闭锁,与循环栅栏CyclicBarrier类似,但闭锁不可重置,其中初始化时需要指定数值,在将数值减到0时会释放锁,在释放锁后再次锁不会生效,所以不可重复使用
public class CountDownLatchTest {
CountDownLatch countDownLatch = new CountDownLatch(2);//数值为2的CountDownLatch
public CountDownLatchTest() {
try {
new TestThread().start();
countDownLatch.await();//等待
System.out.println("等待结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public class TestThread extends Thread{
@Override
public void run() {
for (int i=0;i<2;i++) {
countDownLatch.countDown();//减一次CountDownLatch的数值
System.out.println("减一次");
}
}
}
}
运行结果
synchronized
最后说下synchronized,synchronized的使用比前面所说的锁都要方便,当然,其功能也是有限的,相当于一个单条件锁,其会将一个object对象作为一个锁条件,当此对象没被其他锁持有,将持有锁并运行下去,如有被其他锁持有则会等待锁的释放
//synchronized主要有两种用法
//1.锁方法
synchronized public void print(){
...
}
synchronized public void print2(){
...
}
//2.锁一个对象
public void print(){
synchronized(this){
...
}
}
public void print2(){
...
synchronized(this){//可以只锁方法中的一部分
...
}
}
上述的两种用法是类似的,只是锁对象可以更灵活的控制哪部分需要锁哪部分不需要,而2用法中的print()方法的效果与1用法的基本相同
需要注意的是synchronized锁的是对象,所以不同对象之间的锁不相互影响.
总结
锁是个好东西,但也不能滥用,因为其会带来一些性能上的损失,并且如果使用不当,很有可能造成死锁导致程序出问题,所以在使用锁的时候需要设计好获取与释放的逻辑.