多线程(三):synchronized和Lock

Synchronized

可重入锁:当一个线程得到一个对象的锁后,其他线程不可再获得此锁,但是此线程可以再次请求获得其对象的锁,这样就称之为可重入锁。可重入锁保证了一个线程获得锁之后可以再次请求锁,能够在一定程度上减少程序死锁的几率。

synchronized和ReentrantLock都是可重入锁。

synchronized的解锁过程是由JVM自动控制,Lock需要手动解锁。

通过synchronzed获取对象锁的两种方式:

  1. 通过执行对象的synchronized方法
  2. 通过执行对象的synchronized代码块

Lock

Lock是一个接口,其下有各种类型的锁,必须手动释放锁(unlock()方法),相较于synchronized,扩展了许多对锁的操作。

比如synchronized一个线程在获得锁之后没有释放,另一个线程再请求锁就会陷入阻塞状态,但lock则未必,boolean tryLock()可以尝试获取一个锁,如果锁没有被其他线程持有,可以获得锁,否则无法获得锁,但不会陷入阻塞状态。

ReentrantLock(可重入锁)为Lock接口下的一种锁,ReentrantLock即可公平锁又可非公平锁。synchronized为非公平锁。

公平锁能保证等待时间最长的线程获得锁,ReentrantLock中公平锁的说明:

-ReentrantLock方法

  • int getHoldCount()    //获取持有当前锁的线程数
  • boolean hasWaiters(Condition condition)   //当前condition是否有等待的线程
  • boolean isFair()    //是否为公平锁
  • boolean isHeldByCurrentThread()    //锁是否被当前线程持有
  • boolean isLocked()    //当前锁是否被任何线程持有
  • void lockInterruptibly()    //只有当前线程interrupted才会获取锁
  • boolean tryLock()     //尝试获取锁,如果没有被其他线程持有,获取到锁,否则停止获取锁
  • Condition newCondition()    //为当前Lock获取一个Condition实例

从这些方法中可以发现ReentrantLock扩展了对锁的操作,使锁操作更加灵活多样。

-ReentrantLock通信

使用Lock加锁时,线程之间的通信使用Condition

Condition中的await()相当于wait(),singal()相当于notify(),signalAll()相当于notifyAll()。

case1:生产者/消费者

不管是synchronized还是Lock,在构思代码的时候,一定要明确被上锁的是谁

在生产者/消费者案例案例中,消费者和生产者线程共同操作产品(Product),要获取的对象锁是产品(Product),所以lock/synchronized应该放在Product类中,这样线程执行到lock/synchronized代码,才会获取到lock/synchronized所在对象的锁。

使用ReentrantLock和Condition来实现生产者/消费者案例:

Product.java:

public class Product {

	//标志位,flag为true,可以生产,不可以取走
	private boolean flag=true;

	private ReentrantLock lock = new ReentrantLock();
	private Condition condition = lock.newCondition();
	
	public void produce() {
		lock.lock();
		//falg为true,可以生产,false不可以生产
		if (!flag) {
			try {
				condition.await();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println(Thread.currentThread().getName()+"生产一件产品");
		//取走之后更改标志位
		flag=false;
		try {
			Thread.sleep(100);
			//唤醒消费者线程
			condition.signal();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}finally {
		lock.unlock();
		}
	}
	
	public void consume() {
		lock.lock();
		//flag为true,不可以取走
		if (flag) {
			try {
				condition.await();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println(Thread.currentThread().getName()+"取走一件产品");
		//取走之后更改标志位
		flag=true;
		try {
			Thread.sleep(100);
			//唤醒生产者线程
			condition.signal();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		lock.unlock();
	}
}

Producer.java:

public class Producer implements Runnable {

	private Product product;

	public Producer(Product product) {
		this.product = product;
	}

	@Override
	public void run() {
		for (int i = 0; i < 5; i++) {
			product.produce();
		}
	}
}

Consumer.java:

public class Consumer implements Runnable {

	private Product product;

	public Consumer(Product product) {
		this.product = product;
	}

	@Override
	public void run() {
		for (int i = 0; i < 5; i++) {
			product.consume();
		}
	}
}

Main.java:

public class Main {
	public static void main(String[] args) {
		
		Product product = new Product();
		
		Producer p = new Producer(product);
		Consumer c = new Consumer(product);
		
		new Thread(p, "生产者-").start();
		new Thread(c, "消费者-").start();
	}
}

case2 :多个Condition进行通信

在synchronized中,notify()随机唤醒所有等待线程中的一个,Condition可以对线程进行分类管理,sinal()只会随机唤醒相应Condition中的await()线程。

public class MultCondition {

	ReentrantLock lock = new ReentrantLock();
	Condition conditionA = lock.newCondition();
	Condition conditionB = lock.newCondition();
	
	public void await(Condition condition,String conditionName) {
		try {
			lock.lock();
			System.out.println(Thread.currentThread().getName()+" "+conditionName+":wait begin");
			condition.await();
			System.out.println(Thread.currentThread().getName()+" "+conditionName+":wait end");
			lock.unlock();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	public void signal(Condition condition,String conditionName) {
		lock.lock();
		System.out.println(Thread.currentThread().getName()+" "+conditionName+":notified");
		condition.signal();
		lock.unlock();
	}
	
	public static void main(String[] args) {
		
		MultCondition test = new MultCondition();
				
		new Thread(() -> test.await(test.conditionA,"conditionA"),"Thread-1").start();
		new Thread(() -> test.await(test.conditionB,"conditionB"),"Thread-2").start();
		new Thread(() -> test.signal(test.conditionB,"conditionB"),"Thread-3").start();
		//注释Thread-4,运行程序后手动Terminate
		//new Thread(() -> test.signal(test.conditionA,"conditionA"),"Thread-4").start();
	}
}

-ReentrantReadWriteLock

ReentrantReadWriteLock实现自ReadWriteLock。ReentrantLock具有完全互斥性,即只能有一个线程执行lock()的内容,而ReentrantReadWriteLock有读锁和写锁,读锁为共享锁,写锁为排它锁即多个读锁之间不互斥,读锁与写锁互斥,写锁与写锁互斥。

 

sum:synchronized使用起来简单方便,而Lock则是对synchronized扩展了对锁的各种功能,在线程之间不需要复杂通讯,或者程序功能并不复杂情况下,可以选择使用synchronized,反之则使用Lock。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值