1. 线程安全
当多个线程并行时,若这些线程对于同一个资源都有操作权限,则会造成同一时间内一个资源被多个线程操作后,出现一种混乱的结果,无法实现程序的既定功能。这就是线程安全问题。因此我们考虑在一个线程操作一个资源的时候,让其他资源无法操作该线程,这就是对这个资源加锁。当占有这个资源的线程结束后,才会解锁,然后其他线程才有操作它的机会。
public class TestLock {
public static void main(String[] args) {
TestLock2 t2 = new TestLock2();
new Thread(t2).start();
new Thread(t2).start();
new Thread(t2).start();
}
}
public class TestLock2 implements Runnable{
int Num = 10;
private final ReentrantLock lock = new ReentrantLock();
public void run() {
while (true){
if (Num>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Num--);
}else {
break;
}
}
}
}
线程对资源的操作有读取和写入两种状态,所以多个线程操作资源可大体分为:读读、写写、读写,只要有写入的情况那么就需要使用锁。
2. 锁的实现
使用锁有两种方法: synchronized 关键字 和 Lock 对象
2.1 synchronized 关键字
1.synchronized(this){代码块}
一个线程访问一个对象中的synchronized(this)同步代码块时,其他试图访问该对象的线程将被阻塞
publid void run() {
while (true){
synchronized(this){ //加锁
if (Num>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Num--);
}else {
break;
}
}
}
}
2.public synchronized void method(){方法}
public synchronized void run() { //加锁
while (true){
if (Num>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Num--);
}else {
break;
}
}
}
一个线程访问该方法时,阻塞其他想要访问该方法的线程
2.2 Lock 对象
private final ReentrantLock lock = new ReentrantLock(); //先创建锁对象
public void run() {
while (true){
try {
lock.lock(); //加锁
if (Num>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Num--);
}else {
break;
}
}finally { //解锁
lock.unlock();
}
}
}
2.3 synchronized与Lock 对象对比
- Lock需要手动开启与关闭/synchronized在线程结束后自动释放
- Lock只有代码块锁,synchronized有代码块锁和方法锁
- Lock锁性能更好,JVM花费较少的时间来调度线程
- 使用优先级:Lock>同步代码块(已经进入方法体,分配了相应资源)>同步方法(在方法体外)
本文介绍了Java中线程安全的概念,当多个线程并发访问共享资源时可能导致的问题及解决方案。通过示例展示了使用`synchronized`关键字和`Lock`对象(如`ReentrantLock`)实现线程同步的方法,并比较了两者之间的差异。讨论了锁的使用场景,指出在性能和灵活性方面,Lock通常优于`synchronized`。最后,提供了选择锁机制的优先级建议。

6552

被折叠的 条评论
为什么被折叠?



