上一篇博客写了几种锁,但是没写完,具体的实现没有验证,今天来解决它。
首先Lock和Synchronized的区别:
1.Lock默认是公平锁,谁等待的时间长谁优先获得锁,Synchronized为非公平锁遵循抢占式调度。
2.Lock锁被获得时,有线程在等待可以先中断等待,然后执行其他方法,而Synchronized锁的等待线程则会一直等待着。
3.Lock是用Java写的接口,里面有很多api方法,可以判断锁是否被占用,而Synchronized是多线程的内置关键字,不能判断锁是否被占用。
4.Synchronized锁在执行完了之后会自动释放锁,而Lock则必须手动释放(调用unLock()方法)
其实Synchronized我们都应该很熟悉了,重量型锁在上一篇博客中写了它的生产者和消费者的例子,这里就不在多说。下面来看Lock的例子:
public class locktest implements Runnable{
@Override
public void run() {
say();
}
public void say() {
Lock lock=new ReentrantLock();
lock.lock();//加上锁了
for (int i = 0; i < 10000; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
try {
}finally {
lock.unlock();//释放锁
}
}
}
//这是线程
这个代码是错的,因为这里的锁的创建是放在方法里面的,这样的话每次使用此目标对象创建一个线程,就会执行一次方法,那么就会重新创建一个锁,每次获得的锁都是新的,也就不存在锁之间的竞争了,这个案例也可以解释在类中如果有俩个Synchronized方法,那么当A线程执行一个时,B线程能不能再执行另一个方法的问题。(之后会有案例)
正确的代码如下:
public class locktest implements Runnable{
Lock lock=new ReentrantLock();
@Override
public void run() {
say();
}
public void say() {
lock.lock();//加上锁了
for (int i = 0; i < 10000; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
try {
}finally {
lock.unlock();//释放锁
}
}
}
主方法:
public static void main(String[] args) {
locktest lt=new locktest();
Thread td1=new Thread(lt);
Thread td2=new Thread(lt);
td1.start();
td2.start();
}
这就是Lock锁
那么在深入的看看,java.util.concurrent这个包下有哪些锁接口和具体的实现类。
首先第一个就是Lock接口了,上面有演示,直接看API方法:
-
void lock() – 如果锁可用就获得锁,如果锁不可用就阻塞直到锁释放
-
void lockInterruptibly() – 和 lock()方法相似, 但阻塞的线程可中断,抛出 java.lang.InterruptedException异常
-
boolean tryLock() –lock() 方法的非阻塞版本;尝试获取锁,如果成功返回true
-
boolean tryLock(long timeout, TimeUnit timeUnit) – 和tryLock()方法相似,只是 在放弃尝试获取锁之前等待指定的时间。
- void unlock() – 释放锁
实现Lock接口的是ReentrantLock ,实例就是上面的。
第二个接口是读写锁接口:ReadWriteLock ,ReentrantReadWriteLock 类实现了此接口。这个锁可以拆分为俩个锁,分别是读锁和写锁。调用相应的API方法就可以得到;
writeLock() 获得写锁
readLock() 获得读锁
读-读不互斥
读-写互斥
写-写互斥
Lock暂时就总结到这,因为用的少,所以知道了思想之后,用的时候在看就行。
接下来要试验一下这个问题。
一个类中有俩个Syncronized方法那么当一个执行时另一个会不会执行?
看了资料有的人说会有的说不会,经过自己的验证发现不会执行。
代码如下:
public class Orange {
public synchronized void chi() {
System.out.println("吃橙子了");
}
public synchronized void mai() {
System.out.println("买橙子了");
}
}
public class OrangeThread1 implements Runnable{
private Orange orange;
public OrangeThread1(Orange orange) {
this.orange=orange;
}
@Override
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 100; i++) {
orange.chi();
}
}
}
public class OrangeThread2 implements Runnable{
private Orange orange;
public OrangeThread2(Orange orange) {
this.orange=orange;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
orange.mai();
}
}
}
public static void main(String[] args) {
Orange orange=new Orange();
Thread thread1=new Thread(new OrangeThread1(orange));
Thread thread2=new Thread(new OrangeThread2(orange));
thread1.start();
thread2.start();
}
道理很简单,当方法chi执行时对象锁已经被一个线程拿走了,当方法mai在执行时,他的对象锁还是Orange,而此对象锁已经被之前的线程占用了,所以当然要等待了。
参考博客:https://blog.csdn.net/sinat_26279177/article/details/80646301