欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

多线程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();
	}

运行结果如下:
多线程synchronized与lock

然后我们来测试一下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立马进来,然后发现锁已经被占用,那么这个时候它也不在继续等待。
多线程synchronized与lock

但是如果你多试几次就会发现,有时会出现线程t2获取了锁,而线程1却没获得的情况,这是在线程是同步执行的,t2先抢到了锁,而t1没抢到,即第一个线程,拿到锁的时间,比第二个线程进来的时间还要长。
多线程synchronized与lock
甚至还会出现t1和t2都获得锁的情况,这就是说t1释放了锁之后,t2才进来。
多线程synchronized与lock
那么会不会有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();
	}
}

这样我们可以发现,虽然我们等待两秒,但是还是没有获取到锁.
多线程synchronized与lock
那如果把等待时间改成5秒呢?
多线程synchronized与lock

参考

5个步骤,教你瞬间明白线程和线程安全

相关标签: Java 多线程