什么情况下会产生线程安全问题?
同时满足以下两个条件时:
- 多个线程在操作共享的数据
- 操作共享数据的线程代码有多条
共享数据存在被并发修改的可能,就会导致线程安全问题的产生。
线程安全问题解决思路
将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,其他线程不可以参与运算。
当前线程把这些代码都执行完毕后,其他线程才可以参与运算。
使用Java中同步代码块可以解决这个问题。
synchronized(对象) {
需要被同步的代码;}
这个对象一般称为同步锁。
同步的前提:同步中必须有多 个线程并使用同一个锁。
同步的好处:解决了线程的安全问题。
同步的弊端:相对降低了效率,因为同步外的线程的都会判断同步锁。
Thread thread1 = new Thread() {
@Override
public void run() {
while(true) {
synchronized (Class.class) {
if(ticket>0) {
ticket--;
} else {
break;
}
}
}
}
};
Thread thread2 = new Thread() {
@Override
public void run() {
while(true) {
synchronized (Class.class) {
if(ticket>0) {
ticket--;
} else {
break;
}
}
}
}
};
thread1.start();
thread2.start();
注意事项:使用同一把锁的代码才能实现同步;没有获取到锁的线程即使得到了cpu的执行权,也不能运行;尽量减少锁的范围,避免效率低下;锁可以加在任意类的代码中或方法上。
死锁
使用同步的多个线程同时持有对方运行时所需要的资源
多线程同步时,多个同步代码块嵌套,很容易出现死锁
锁的嵌套越多,越容易造成死锁的情况
死锁的情况
private static String s1 = "筷子左";
private static String s2 = "筷子右";
public static void main(String[] args) {
new Thread() {
public void run() {
while(true) {
synchronized(s1) {
System.out.println(getName() + "...拿到" + s1 + "等待" + s2);
synchronized(s2) {
System.out.println(getName() + "...拿到" + s2 + "开吃");
}
}
}
}
}.start();
new Thread() {
public void run() {
while(true) {
synchronized(s2) {
System.out.println(getName() + "...拿到" + s2 + "等待" + s1);
synchronized(s1) {
System.out.println(getName() + "...拿到" + s1 + "开吃");
}
}
}
}
}.start();
}