多线程学习(二):线程安全问题
当线程执行到同步代码块时,会尝试获取锁对象的锁,以确保代码块在同一时间只能被一个线程执行。
1. 问题引入
同时开启两个线程,对同一个资源进行操作,线程之间互相干扰
什么是线程安全问题?
多线程同时修改同一资源,线程之间互相干扰,就会产生线程安全问题
public class ThreadSafe1 implements Runnable{
public static int count = 100;
@Override
public void run() {
if (count > 1) {
try {
// 模拟延时,造成资源争抢
Thread.sleep(30);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
count--;
System.out.println(Thread.currentThread().getName() + ": 当前count值为" + count);
}
}
public static void main(String[] args) {
ThreadSafe1 threadSafe = new ThreadSafe1();
new Thread(threadSafe).start();
new Thread(threadSafe).start();
}
}
2. synchronized 基本使用方法
2.1 代码块
synchronized(锁对象) {
同步代码
}
锁对象可以是:
- this: 当前实例对象的锁
- class: 当前类的锁(静态同步)
- 其他对象: 任何其他对象的锁
Object objectLock = new Object();
synchronized (this) {
// 同步代码
}
private Object objectLock = new Object();
synchronized (objectLock) {
// 同步代码
}
public class ThreadSafe2 implements Runnable{
public static int count = 100;
private Object objectLock = new Object();
@Override
public void run() {
try {
Thread.sleep(30);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (count > 1) {
// this锁
synchronized (this) {
count--;
System.out.println(Thread.currentThread().getName() + ": 当前count值为" + count);
}
// -------------------------------------------
// 对象锁
// synchronized (objectLock) {
// count--;
// System.out.println(Thread.currentThread().getName() + ": 当前count值为" + count);
// }
}
}
public static void main(String[] args) {
ThreadSafe2 threadSafe = new ThreadSafe2();
new Thread(threadSafe).start();
new Thread(threadSafe).start();
}
}
2.2 方法
- 普通方法
在普通方法上使用synchronized关键字,相当于使用了this锁
private synchronized void call() {
// 同步代码
}
// 相当于
private void call() {
synchronized(this) {
// 同步代码
}
}
- 静态方法
在静态方法上使用synchronized关键字,相当于使用了class锁
private synchronized static void call() {
// 同步代码
}
// 相当于
private void call() {
synchronized(ThreadSafe.class) {
// 同步代码
}
}
3. 锁对象
锁对象就是提供锁的对象,如果存在多个就无法保证线程安全。
在下面的代码中,实例化了两个ThreadSafe类,由于同步代码块使用的时this锁,线程会去找各自的实例this来获取锁,也就说两个线程都拿到了锁,因此会出现线程安全问题。
public class ThreadSafe implements Runnable{
public static int count = 100;
@Override
public void run() {
if (count > 1) {
try {
// 模拟延时,造成资源争抢
Thread.sleep(30);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (this) {
count--;
System.out.println(Thread.currentThread().getName() + ": 当前count值为" + count);
}
}
}
public static void main(String[] args) {
ThreadSafe threadSafe = new ThreadSafe();
ThreadSafe threadSafe_ = new ThreadSafe();
new Thread(threadSafe).start();
new Thread(threadSafe_).start();
}
}
4. 死锁问题
当线程间互相持有彼此需要的资源,且彼此都不释放对方所需资源,就会造成死锁问题
以下面的代码为例进行分析:
- 当多线程执行时,会出现这种情况,两个线程同时执行if语句的两个分支
- 可以
- 假定线程A进入了
true
分支,获取到了this锁,现在将执行a()方法 - 假定线程B进入了
false
分支,获取到了lock锁,现在将执行b()方法
- 假定线程A进入了
- 同时
- 线程A执行a()方法,需要获取lock锁,而lock锁被线程B持有
- 线程B执行b()方法,需要获取this锁,而this锁被线程B持有
- 线程AB互相持有彼此需要的锁,且都没有设置超时时间,无法释放,这就造成了死锁
public class ThreadSafe implements Runnable{
private int count = 0;
private static final Object lock = new Object();
@Override
public void run() {
while (true) {
count++;
if (count%2 == 0) {
synchronized (this) {
a();
}
} else {
synchronized (lock) {
b();
}
}
}
}
void a() {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + ":a()");
}
}
void b() {
synchronized (this) {
System.out.println(Thread.currentThread().getName() + ":b()");
}
}
public static void main(String[] args) {
ThreadSafe3 threadSafe = new ThreadSafe();
new Thread(threadSafe).start();
new Thread(threadSafe).start();
}
}
本文是原创文章,转载请注明来自 Lazyking.site
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果
Steam卡片