多线程synchronized与lock
程序员文章站
2022-07-08 16:44:39
...
如何保证线程安全
首先我们知道局部变量是线程安全的,全局变量是线程不安全的,那么如何保证全局变量的线程安全呢?
synchronized
synchronized关键字,就是用来控制线程同步的,保证我们的线程在多线程环境下,不被多个线程同时执行,以确保我们数据的完整性。可以放在方法上也可以放在代码块上。
public class ThreadDemo {
int count = 0; // 记录方法的命中次数
public synchronized void threadMethod(int j) {
count++ ;
int i = 1;
j = j + i;
}
}
这样就可以实现线程同步了,但是我们需要清楚:
- synchronized锁的是括号里的对象,而不是代码。
- 对于非静态的synchronized方法,锁的是对象本身,也就是this。
- 一旦synchronized锁住一个对象后,别的线程必须等到这个线程执行完之后才可以执行,否则就一直处于等待状态。
- 在使用时要注意缩小synchronized的使用范围,防止资源浪费,影响性能。
lock
Lock是在Java1.6被引入进来的,Lock的引入让锁有了可操作性,也就是说我们可以手动的获取锁和释放锁,甚至还可以中断获取以及超时获取。
public class ThreadDemo {
private Lock lock = new ReentrantLock(); // ReentrantLock是Lock的子类
private void method(Thread thread){
System.out.println(thread.getName()+",您好,欢迎您的到来!");
lock.lock(); // 获取锁对象
try {
System.out.println("线程名:"+thread.getName() + "获得了锁");
}catch(Exception e){
e.printStackTrace();
} finally {
System.out.println("线程名:"+thread.getName() + "释放了锁");
lock.unlock(); // 释放锁对象
}
}
}
为了防止我们代码出现异常,所以我们的释放锁操作放在finally中,因为finally中的代码无论如何都是会执行的。
另外几种获取锁的方式
- lock()在获取锁的时候,如果拿不到锁,就一直处于等待状态,直到拿到锁
- tryLock()tryLock是有一个Boolean的返回值的,如果没有拿到锁,直接返回false,停止等待,它不会像Lock()那样去一直等待获取锁。
- tryLock(long time,TimeUnit unit)可以等待time秒,如果time秒之后,还获取不到锁,那么就停止等待。
实例
首先先测试一下lock()方法
public static void main(String[] args) {
ThreadDemo lockTest = new ThreadDemo();
// 线程1
Thread t1 = new Thread(()-> {
lockTest.method2(Thread.currentThread());
}, "t1");
// 线程2
Thread t2 = new Thread(()-> {
lockTest.method2(Thread.currentThread());
}, "t2");
t1.start();
t2.start();
}
运行结果如下:
然后我们来测试一下tryLock方法
private void method(Thread thread){
if (lock.tryLock()){
try {
System.out.println("线程名:"+thread.getName() + "获得了锁");
}catch(Exception e){
e.printStackTrace();
} finally {
System.out.println("线程名:"+thread.getName() + "释放了锁");
lock.unlock(); // 释放锁对象
}
}else {
System.out.println(thread.getName()+",您好,当前访问量比较大,请稍后重试!");
}
}
我们继续使用刚才的两个线程进行测试可以发现,在线程t1获取到锁之后,线程t2立马进来,然后发现锁已经被占用,那么这个时候它也不在继续等待。
但是如果你多试几次就会发现,有时会出现线程t2获取了锁,而线程1却没获得的情况,这是在线程是同步执行的,t2先抢到了锁,而t1没抢到,即第一个线程,拿到锁的时间,比第二个线程进来的时间还要长。
甚至还会出现t1和t2都获得锁的情况,这就是说t1释放了锁之后,t2才进来。
那么会不会有t2先获得锁,t1再获得锁的情况呢,大家可以去试一下。
为了模拟真实情况,我们通过Thread.sleep(3000)模拟一下耗时操作,同时让后面等待的线程等待两秒,如果两秒内获取不到就停止获取。
private void method(Thread thread){
try {
if (lock.tryLock(2, TimeUnit.SECONDS)){
try {
System.out.println("线程名:"+thread.getName() + "获得了锁");
Thread.sleep(3000);
}catch(Exception e){
e.printStackTrace();
} finally {
System.out.println("线程名:"+thread.getName() + "释放了锁");
lock.unlock(); // 释放锁对象
}
}else {
System.out.println(thread.getName()+",您好,当前访问量比较大,请稍后重试!");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
这样我们可以发现,虽然我们等待两秒,但是还是没有获取到锁.
那如果把等待时间改成5秒呢?
参考
上一篇: BeanMapping发布1.0.2版本