java中锁的分类以及对synchronized和Lock的理解

一、java中锁的分类
1、可重入锁

  • 锁的重入性:如果某个线程试图获得一个由它自己持有的锁,如果这个请求成功,那么这个锁具有重入性(内置锁具有重入性);如果锁具备可重入性,则称作为可重入锁。像synchronized和ReentrantLock都是可重入锁,可重入性表明了锁的分配机制:基于线程的分配,而不是基于方法调用的分配。举个简单的例子,当一个线程执行到某个synchronized方法时,比如说method1,而在method1中会调用另外一个synchronized方法method2,此时线程不必重新去申请锁,而是可以直接执行方法method2。

2、可中断锁

  • 就是可以相应中断的锁。在Java中,synchronized就不是可中断锁,而Lock是可中断锁。如果某一线程A正在执行锁中的代码,另一线程B正在等待获取该锁,可能由于等待时间过长,线程B不想等待了,想先处理其他事情,我们可以让它中断自己或者在别的线程中中断它,这种就是可中断锁。

3、公平锁(一般情况下非公平锁性能好一些)

  • 公平锁即尽量以请求锁的顺序来获取锁。比如同是有多个线程在等待一个锁,当这个锁被释放时,等待时间最久的线程(最先请求的线程)会获得该所,这种就是公平锁。非公平锁即无法保证锁的获取是按照请求锁的顺序进行的。这样就可能导致某个或者一些线程永远获取不到锁。在Java中,synchronized就是非公平锁,它无法保证等待的线程获取锁的顺序。而对于ReentrantLock和ReentrantReadWriteLock,它默认情况下是非公平锁,但是可以设置为公平锁。

4、读写锁

  • 读写锁将对一个资源(比如文件)的访问分成了2个锁,一个读锁和一个写锁。正因为有了读写锁,才使得多个线程之间的读操作不会发生冲突。  
    ReadWriteLock就是读写锁,它是一个接口,
    ReentrantReadWriteLock实现了这个接口。可以通过readLock()获取读锁,通过writeLock()获取写锁。

二、Lock中lock(),tryLock(),tryLock(time,TimeUnit)的测试及理解
lock() unlock() tryLock()实现源码参考如下
https://blog.csdn.net/ameyume/article/details/7567810
粗略介绍:调用lock()与tryLock()方法时这两个函数会调用WaitForSingleObject()函数
调用lock()时,会传入的参数为INFINITE(无限的意思),所以线程2如被阻塞,会一直等待直到解锁变为就绪状态
执行tryLock()会传入的参数为0,线程2发现有锁,不会等待,继续执行后面的程序
执行tryLock(time,TimeUnit)传入的参数为time,线程2会等待time这么长的时间,如果在这段时间段内,锁解除会被唤醒成就绪状态。否则线程2继续执行后面的程序。

	WaitForSingleObject()函数如下:
status_t Mutex::lock()
{
    DWORD dwWaitResult;
    dwWaitResult = WaitForSingleObject((HANDLE) mState, INFINITE);
    return dwWaitResult != WAIT_OBJECT_0 ? -1 : NO_ERROR;
}
 
void Mutex::unlock()
{
    if (!ReleaseMutex((HANDLE) mState))
        LOG(LOG_WARN, "thread", "WARNING: bad result from unlocking mutex\n");
}
 
status_t Mutex::tryLock()
{
    DWORD dwWaitResult;
 
    dwWaitResult = WaitForSingleObject((HANDLE) mState, 0);
    if (dwWaitResult != WAIT_OBJECT_0 && dwWaitResult != WAIT_TIMEOUT)
        LOG(LOG_WARN, "thread", "WARNING: bad result from try-locking mutex\n");
    return (dwWaitResult == WAIT_OBJECT_0) ? 0 : -1;
}

DWORD WaitForSingleObject(
HANDLE hObject, //指明一个内核对象的句柄
DWORD dwMilliseconds); //等待时间
  该函数需要传递一个内核对象句柄,该句柄标识一个内核对象,如果该内核对象处于未通知状态,则该函数导致线程进入阻塞状态;如果该内核对象处于已通知状态,则该函数立即返回WAIT_OBJECT_0。第二个参数指明了需要等待的时间(毫秒),可以传递INFINITE指明要无限期等待下去,如果第二个参数为0,那么函数就测试同步对象的状态并立即返回。如果等待超时,该函数返回WAIT_TIMEOUT。如果该函数失败,返回WAIT_FAILED。

package thread;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class BankTest1 {

	public static void main(String[] args) {
		sell s = new sell();
		Thread thread1 = new Thread(s, "thread1");
		Thread thread2 = new Thread(s, "thread2");
		thread1.start();
		thread2.start();
	}
}

class sell implements Runnable {
	int count = 20;
	Lock lock = new ReentrantLock();

	
	public void run() {// 使用线程lock.lock();方法加锁后,如果不让当前线程暂时阻塞,会一直执行当前线程
		// 如暂时阻塞如加入Sleep(10)线程1与线程2会交替执行。
		// 解释:线程1获得锁后,为了避免线程2进入锁内运行,线程2会被变成阻塞状态,当调用unlock方法时
		// ,会唤醒线程2,使线程2变为就绪状态,由于线程1一直是就绪状态,所以线程1会一直优先于线程2运行(除非线程1在释放锁后被阻塞)。
		for (int i = 0; i < 10; i++) {
			lock.lock();
			System.out.println(Thread.currentThread().getName() + ":"
					+ (count--));
			lock.unlock();
			
		}
	}

运行后结果如下:
在这里插入图片描述

	public void run() { // TODO Auto-generated method stub
		for (int i = 30; i > 0; i--) {
			if (lock.tryLock()) {
				try {
					System.out.println(Thread.currentThread().getName() + ":"
							+ (count--));

				} catch (Exception e) { // TODO Auto-generated catch block
					e.printStackTrace();
				} finally {
					lock.unlock();
				}
			} else {
				System.out.println("当前锁被人占用,无法获取");
			}

		}

	}

运行结果如下在这里插入图片描述` public void run() {
// TODO Auto-generated method stub
for (int i = 10; i > 0; i–) {

		try {
			if (lock.tryLock(200, TimeUnit.MILLISECONDS)) {
				try {
					System.out.println(Thread.currentThread().getName()
							+ ":" + (count--));
					Thread.sleep(100);
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} finally {
					lock.unlock();
				}
			} else {
				System.out.println("当前锁被人占用,无法获取");
			}
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}`

运行结果如下
在这里插入图片描述` public void run() {//

	// TODO Auto-generated method stub
	for (int i = 10; i > 0; i--) {
		synchronized (Thread.currentThread()) {
			System.out.println(Thread.currentThread().getName() + ":"
					+ (count--));

			try {
				Thread.sleep(100);
			} catch (InterruptedException e1) {
				e1.printStackTrace();
			}

		}
	}

}
  • 运行结果如下`

在这里插入图片描述
三、Lock锁与synchronized的区别
1、Lock是接口,有众多的实现类比如ReenTranLock,ReentrantReadWriteLock.ReadLock,ReentrantReadWriteLock.WriteLock.
synchronized是关键字,可修饰方法或独立成块。
2、Lock锁不会自动释放锁,必须用unlock()方法释放。synchronized在包括的代码运行结束或内部代码阻塞时会自动释放锁。
3、用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了;
4、synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可中断、可公平(两者皆可)
5、Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
synchronizedlock都是Java中用来实现线程同步的关键字和类,它们的主要区别如下: 1. 锁的获取和释放方式不同 synchronized关键字是隐式锁,在代码块或方法中使用时,当线程进入同步代码块时,会自动获取锁,执行完同步代码块后,会自动释放锁。而lock是显式锁,需要手动获取锁和释放锁,即通过lock()方法获取锁,通过unlock()方法释放锁。 2. 锁的可重入性不同 synchronized是可重入锁,即同一个线程在持有锁的情况下,能够再次获得该锁。而lock在获取锁时需要先判断当前线程是否已经持有锁,如果是,则允许该线程继续获取锁,也就是说,lock也是可重入锁。 3. 锁的公平性不同 synchronized是非公平锁,即线程在等待锁时,是随机竞争锁的。而lock可以通过构造函数指定锁的公平性,即fair为true时为公平锁,fair为false时为非公平锁。 4. 锁的灵活性不同 synchronized是内置的Java关键字,不能对其进行扩展。而lock是一个接口,可以通过实现该接口来扩展锁的功能,例如可以实现可中断锁或超时锁等。 5. 锁的性能不同 synchronizedJava内置的锁,由JVM实现,JVM可以对其进行优化,锁的性能较高。而lock是基于Java API实现的锁,通常比synchronized稍微慢一些。 总之,synchronizedlock都是Java中用来实现线程同步的关键字和类,它们各有优缺点,应根据具体场景选择使用哪种锁。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值