提到锁,首先要明确一个概念:
1.线程与进程?
一个简单的程序,最少需要一个进程而一个进程最少需要线程,所以关系是线程-->进程-->程序的大致构成结构,所以线程是
执行程序的最下单元,而进程是系统进行资源分配和调度的独立单元,所以以下是我们讨论的都是建立在线程的基础上
2.Thread的几个重要方法?
a.start()方法,调用该方法开始执行线程
b.stop()方法,调用该方法强制结束该线程执行
c.join()方法,调用该方法等待线程结束
d.sleep()方法,调用该方法线程进入等待
e.run()方法,调用该方法直接执行线程的run()方法,这里调用start方法也可以运行线程,区别就是一个是由线程调度运行run()方法
一个是直接调用了线程中的run()方法
f.wait()方法和notify()方法呢?
其实wait()方法和notify都是object()方法,不是thread方法
g.wait()方法和notify()方法的区别?
简单的来说wait()会释放锁对象而sleep()不会释放锁对象
3.线程的状态?
新建状态:新建线程对象,并没有调用start()方法之前
就绪状态:调用start()方法之后线程进入就绪状态,但是并不是说只要调用start()方法现象就能马上变为当前线程
在变成当前线程之前都是就绪状态,值得一提的是,线程在睡眠和挂起中恢复的时候也会进入就绪状态
运行状态:线程被设置为当前线程状态开始执行run()方法,就是就绪状态
阻塞状态:线程被暂停,比如说调用sleep()方法后,线程就进入阻塞状态
死亡状态:线程执行结束
4.锁的类型?
可重入锁:在执行对象中,所有同步方法不用再次获得锁
可中断锁:在等待获取锁的过程中可以中断
公平锁: 按等待获取锁的线程的等待时间进行获取,等待时间长的具有优先获取锁的权利
读写锁: 对资源读取和写入的时候拆分为2部分处理,读的时候可以多线程一起读,写的时候必须同步写
5. synchronized与Lock的区别?
类别 | synchronized | Lock |
存在层次 | java的关键字,在jvm上 | 是一个借口 |
锁的释放 | 1.已获取锁的线程执行完同步代码快 2.线程执行发生异常,jvm会让线程释放锁 |
在finally中必须释放锁,不然容易造成死锁 |
锁的获取 | 假设A线程获得锁,B线程等,如果A线程阻塞.B线程一直等待 | 分情况而定,lock有多个获取锁的方式,具体下面会说,大致就可以尝试获得锁,线程就可以一直等待 |
锁的状态 | 无法判断 | 可以判断 |
锁的类型 | 可重入 不可中断 非公平 | 可重入 可判断 可公平(两者皆可) |
性能 | 少量同步 | 大量同步 |
6.lock详解 (打开 idea ctlr+n 输入lock命令找到lock 源码)
lock接口中我们可以看出主要的方法,这些方法的功能从注释可以看出
lock():获取锁,如果锁被暂用则一直等待
unlock():释放锁
tryLock():注意返回类型是boolean,如果获取锁的时候被占用,则返回false,否则返回true
trylock(long time,timeUnit unit):比起tryLock()就是给了一个时间期限,保证等待参数时间
lockInterruptibly():用该锁的获得方式,如果线程在获取锁的阶段进入了等待,那么可以中断此线程,先去做别的事
通过以上的解释,大致可以解释在上个部分 "锁的类型(lockInterruptibly())","锁状态(tryLock())" 等问题
来看下这边在网上找到的例子(下面是Lock一般使用的例子,注意ReentrantLock是Lock接口的实现。):
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Created by :Corey
* 17:50 2019/4/8
*/
public class lockMethod {
private Lock lock = new ReentrantLock();
//需要参与同步方法
private void method(Thread thread) {
lock.lock();
try {
System.out.println("线程名" + thread.getName() + "获得了锁");
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("线程名" + thread.getName() + "释放量锁");
lock.unlock();
}
}
public static void main(String[] args) {
lockMethod lockMethod = new lockMethod();
//线程1
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
lockMethod.method(Thread.currentThread());
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
lockMethod.method(Thread.currentThread());
}
}, "t2");
t1.start();
t2.start();
}
}
执行结果:
tryLock() & tryLock(long time, TimeUnit unit) 顾明思议,就是尝试获取锁,可以加时间,tryLock(long time, TimeUnit unit) 能够响应中断.及支持对获取的中断,但是但尝试获取一个内部锁(synchronized)的操作是不能被中断,返回的是boolean类型这里可以进行判断,如果获取到了锁,则进行啥,若没有获取到锁执行啥,
测试代码:
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockTest {
private Lock lock = new ReentrantLock();
/**
* true 表示 ReentrantLock 的公平锁
*/
private ReentrantLock lock = new ReentrantLock(true);
public static void main(String[] args) {
//简单的使用
test1();
//trylock
test2();
//中断锁
test3();
}
public static void test1() {
LockTest lockTest = new LockTest();
for (int i = 0; i < 10; i++) {
//线程1
new Thread(() -> {
lockTest.method1(Thread.currentThread());
}, i + "").start();
}
}
//需要参与同步的方法
private void method1(Thread thread) {
//错误
// Lock lock = new ReentrantLock();
lock.lock();
try {
System.out.println("线程名" + thread.getName() + "获得了锁");
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("线程名" + thread.getName() + "释放了锁");
lock.unlock();
}
}
public static void test2() {
LockTest lockTest = new LockTest();
for (int i = 0; i < 10; i++) {
//线程1
new Thread(() -> {
lockTest.method2(Thread.currentThread());
}, i + "").start();
}
}
//需要参与同步的方法
private void method2(Thread thread) {
// 尝试获取锁
boolean b = lock.tryLock();
// 尝试获取锁时间
// boolean b = false;
// try {
// System.out.println("线程名" + thread.getName() + "尝试获取锁--");
// b = lock.tryLock(2, TimeUnit.SECONDS);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
if (!b) {
//未获取到锁
System.out.println("线程名" + thread.getName() + "没有获取到锁,直接返回--");
return;
}
//获取到锁
try {
TimeUnit.SECONDS.sleep(2);
System.out.println("线程名" + thread.getName() + "获得了锁");
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("线程名" + thread.getName() + "释放了锁");
lock.unlock();
}
}
public static void test3() {
LockTest lockTest = new LockTest();
//线程1
Thread t1 = new Thread(() -> {
try {
lockTest.method3(Thread.currentThread());
} catch (InterruptedException e) {
System.out.println("线程名t1中断-----");
// e.printStackTrace();
}
}, "t1");
t1.start();
//线程1
Thread t2 = new Thread(() -> {
try {
lockTest.method3(Thread.currentThread());
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "t2");
t2.start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程名t1执行中断");
t1.interrupt();
}
//需要参与同步的方法
private void method3(Thread thread) throws InterruptedException {
lock.lockInterruptibly();
//获取到锁
System.out.println("线程名" + thread.getName() + "获得了锁");
try {
TimeUnit.SECONDS.sleep(10);
} catch (Exception e) {
// e.printStackTrace();
} finally {
System.out.println("线程名" + thread.getName() + "释放了锁");
lock.unlock();
}
}
}
默认的ReentrantLock是不公平的锁
公平所,公平锁指的是谁先尝试获取锁,谁就能获取到锁,就是有可能的一个线程最先尝试获取锁,但是一直没有获取到锁,索取锁就是随机的,可以通过构造方法创建公平锁
非公平锁性能高于公平锁性能,首先,在恢复一个被挂起的线程与该线程真正运行之间存在着严重的延迟。而且,非公平锁能更充分的利用cpu的时间片,尽量的减少cpu空闲的状态时间。
ReadWriteLock(读写锁)
读写锁,跟lock接口没啥关系主要实现有ReentrantReadWriteLock
public class ReadWriteLockTest {
private static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private static Integer count = 0;
public static void main(String[] args) {
ReadWriteLockTest readWriteLockTest = new ReadWriteLockTest();
for (int i = 0; i < 100; i++) {
//从1到10的int型随数
int j = (int) (1 + Math.random() * (10 - 1 + 1));
if (j % 4 != 1) {
//写操作
new Thread("" + i) {
public void run() {
readWriteLockTest.get(Thread.currentThread());
}
}.start();
} else {
//读操作
int co = i;
new Thread("" + i) {
public void run() {
readWriteLockTest.write(Thread.currentThread(), co);
}
}.start();
}
}
}
public static void get(Thread thread) {
rwl.readLock().lock();
try {
System.out.println("线程" + thread.getName() + "开始读操作...");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程" + thread.getName() + "读操作完毕..." + count);
} finally {
rwl.readLock().unlock();
}
}
public static void write(Thread thread, int i) {
rwl.writeLock().lock();
try {
System.out.println("线程" + thread.getName() + "开始写操作---------"+i);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
count = i;
System.out.println("线程" + thread.getName() + "开始写操作完成------"+i);
} finally {
rwl.writeLock().unlock();
}
}
}
执行结果
开始读写操作,线程可以都进行读操作,要是有写操作,会等读操作都完成
Lock和synchronized的选择:
总的来说,lock和synchronized的选择有以下不同点:
1.lock是一个接口,是jdk层面的实现,而synchronized是java中的关键字,是java的内置特性,是jvm层面的实现
2.synchronized在发生异常的时,会自动释放线程占的锁,因此不会导致死锁现象发生,而lock在发生异常时候,如果没有
unlock()去释放锁,则很可能造成死锁现象,因此使用lock时需要在finally快中释放锁
3.lock可以让等待锁的线程响应中断,而使用synchronized时,等待的线程会一直等待下去,不能响应中断
4.通过lock可以知道有没有成功获取锁,而synchronized却无法办到
5.lock可以提高多个线程进行读操作的效率
在性能上说,如果竞争资源 不激烈,两者性能差不多,而当竞争资源非常激烈时候(既有大量线程同事竞争) 此时lock的性能要
远远优于synchronized