把 java 基础撸一边,从简单的开始。
线程部分:
特别说明来源:
抄了的网站:java并发编程-Lock
Lock的需要
在java中,已经有了synchronize来当锁,为什么还要Lock呢?
被synchronize修饰的方法,当一个线程获得了对应的锁之后,并执行该代码块时,其他线程只能一直等待,其他线程获取锁的情况会有三种。
1:执行完该代码块,然后线程释放对锁的占有
2:线程执行发生异常,此时jvm会让线程自动释放锁
3:这个主要是在等待唤醒机制里面的wait()方法
那么如果这个获取锁的线程要等待IO或其他被阻塞了,但没有释放,其他线程只能干巴巴地等。这样会影响效率,因此我们需要不论程序的代码块执行的如何最终都将锁对象进行释放,方便其他线程的执行
这个时候就需要用到lock
1:Lock不是java的语音内置的,synchronize是java的关键字,因此是内置特性。Lock是一个类,通过这个类可以实现同步方法
2:synchronize是在JVM层面上实现的,不但可以通过一些监控 工具 监控synchronize的锁定,而且在代码执行出现异常,jvm会自动释放锁定,但lock则不行,lock是通过代码执行的,要保证锁一定会被释放,就必须将nuLock放到finally中
3:在资源竞争不是很激烈的情况下,synchronize的性能要优于ReetranLock,但在资源竞争很激烈的情况下,Synchronize的性能会下降几十倍
Lock的使用
Lock是一个抽象类,接口Lock的类有,ReentrantLock 可重入锁,ReadWriteLock读写锁,
ReentantReadWriteLock可重入读写锁,StampedLock
Lock的方法:
//获取锁
void lock();
//获取锁的过程能响应中断
void lockInterruptibly() throws InterruptedException;
//获取锁返回true,否则返回false
boolean tryLock();
//超时获取锁,在规定时间未获取到锁返回false
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
释放锁
void unlock();
//获取与lock绑定的等待通知组件,在前现场必须先获得了锁才能等待,等待会释放锁,
//再次获取到锁才能从等待中返回
Condition newCondition();
复制代码
ReentrantLock使用
在实现类中,用的比较多的是ReentrantLock。下面对这个类的使用做个介绍
public class Demo51 {
Lock lock = new ReentrantLock(); //创建这个类的失恋,保证唯一
public void insert(Thread thread) {
if (lock.tryLock()){ //获取锁
try {
System.out.println(thread.getName()+"得到了锁");
} catch (Exception e) {
// TODO: handle exception
}finally {
System.out.println(thread.getName()+"释放了锁");
lock.unlock(); //记得释放锁
}
}else {
System.out.println(thread.getName()+"获取锁失败");
}
}
public static void main(String[] age){
final Demo51 test = new Demo51();
new Thread(){
public void run() {
test.insert(Thread.currentThread());
};
}.start();
new Thread(){
public void run() {
test.insert(Thread.currentThread());
};
}.start();
}
}复制代码
打印
Thread-0得到了锁
Thread-0释放了锁
Thread-1得到了锁
Thread-1释放了锁
复制代码
这里使用了两个方法
tryLock和unlock 一个获取,一个释放,有获取就一定要有释放。这个和synchronize不一样,当执行完方法的时候会自动释放,让下个线程获取锁,但这个必须代码
public class Demo52 {
private Lock lock = new ReentrantLock();
public static void main(String[] args) {
Demo52 test = new Demo52();
MyThread thread1 = new MyThread(test);
MyThread thread2 = new MyThread(test);
thread1.start();
thread2.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread2.interrupt();
}
public void insert(Thread thread) throws InterruptedException{
lock.lockInterruptibly(); //注意,如果需要正确中断等待锁的线程,必须将获取锁放在外面,然后将InterruptedException抛出
try {
System.out.println(thread.getName()+"得到了锁");
long startTime = System.currentTimeMillis();
for(int i = 0 ;i < 20 ; i ++ ) {
Thread.sleep(200);
if(System.currentTimeMillis() - startTime >= Integer.MAX_VALUE)
break;
//插入数据
}
}
finally {
System.out.println(Thread.currentThread().getName()+"执行finally");
lock.unlock();
System.out.println(thread.getName()+"释放了锁");
}
}
static class MyThread extends Thread{
private Demo52 demo52 = null;
public MyThread(Demo52 demo52){
this.demo52 = demo52;
}
@Override
public void run() {
try {
demo52.insert(Thread.currentThread());
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+"被中断");
}
}
}
}复制代码
打印
Thread-0得到了锁
Thread-1被中断
Thread-0执行finally
Thread-0释放了锁
复制代码
在Thread-0拿到锁之后,Thread-1判断,如果锁被占用,会中断
(还有几个java原生实现了Lock的接口。后续再补吧!给自己留的作业)
手写实现Lock接口类
目标:
1:实现获取锁lock()和释放unlock()方法
2:实现重入锁
初步实现
实现Lock抽象类
public class MyLock5 implements Lock {
private boolean isLock = false ;
@Override
public synchronized void lock() {
while (isLock){
try {
wait(); //等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
isLock = true ;
}
@Override
public synchronized void unlock() {
notify(); //唤醒
isLock = false ;
}
}复制代码
在lock()方法里面对进入的线程进行判断,如果已经有线程进入会是isLock会是true,进入wait等待状态中。执行unlock方法后isLock 为true 这样就可以不需要等待去执行方法
public class Demo54 {
private Lock lock = new MyLock5();
int value = 0 ;
public int getNext(){
lock.lock();
value++ ;
lock.unlock();
return value ;
}
public static void main(String[] age){
Demo54 demo54 = new Demo54();
new Thread(new Runnable() {
@Override
public void run() {
for (;;)
System.out.println(""+demo54.getNext());
}
}).start();
}
}复制代码
打印:
235842
235843
235844
235845
235846
235847
235848
235849
235850
235851
235852
复制代码
可以顺序执行,没有出现问题
可重入锁
如果重入锁呢
public void a(){
lock.lock();
System.out.println("a");
b();
lock.unlock();
}
public void b(){
lock.lock();
System.out.println("b");
lock.unlock();
}复制代码
在线程里执行a()方法
执行结果。打印完a之后就进入了等待。这个时候需要对MyLock进行优化
public class MyLock5 implements Lock {
private boolean isLock = false ;
private Thread nowThread = null ;
private int threadNumber = 0 ;
@Override
public synchronized void lock() {
Thread thread = Thread.currentThread();
while (isLock && thread != nowThread ){
try {
wait(); //等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
isLock = true ;
nowThread = thread ;
threadNumber++ ;
}
@Override
public synchronized void unlock() {
if (nowThread == Thread.currentThread()){
threadNumber-- ;
if (threadNumber == 0){
notify(); //唤醒
isLock = false ;
}
}
}
}
复制代码
1:判断是否是同一个线程,如果是同一个线程的话进入等待状态,如果不是,继续执行
2:对线程进行计数
执行结果。a 方法执行完之后 b也执行