线程安全
多个线程在同时运行一段相同的代码。程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的,反之则是线程不安全的。
代码实现不安全的线程
public class 产生 {
public static void main(String[] args) {
//创建线程任务对象
Ticket ticket = new Ticket();
//创建三个窗口对象
Thread t1 = new Thread(ticket, "窗口1");
Thread t2 = new Thread(ticket, "窗口2");
Thread t3 = new Thread(ticket, "窗口3");
//卖票
t1.start();
t2.start();
t3.start();
}
static class Ticket implements Runnable {
private int ticket = 10;
@Override
public void run() {
String name = Thread.currentThread().getName();
while (true) {
if (!sell(name)) {
break;
}
}
}
private boolean sell(String name) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (--ticket > 0) {
System.out.println(name + "卖票:" + ticket);
return true;
}
return false;
}
}
}
产生原因分析
- 线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;
- 若有多个线程同时执行写操作,一般都需要考虑线程同步, 否则的话就可能影响线程安全。
解决方法
保证写操作在同一时间只有一个线程访问即可
synchronized关键字
可加在代码块和方法上,注意锁对象的不同
// ...
private boolean sell(String name) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (this){
if (--ticket > 0) {
System.out.println(name + "卖票:" + ticket);
return true;
}
}
return false;
}
ReentrantLock类
static class Ticket implements Runnable {
private int ticket = 10;
// 注意ReentrantLock对象要是相同的
ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
String name = Thread.currentThread().getName();
while (true) {
if (!sell(name)) {
break;
}
}
}
private boolean sell(String name) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.lock();
if (ticket > 0) {
System.out.println(name + "卖票:" + ticket--);
lock.unlock();
return true;
}
lock.unlock();
return false;
}
}
面试:synchronized和ReentrantLock的区别
- synchronized是一个关键字,ReentrantLock是Juc包下的一个类
- synchronized可以加在代码片段上,也可以加载方法上,范围更广,ReentrantLock只能在方法中使用
- synchronized必须等方法块中的代码执行完才释放锁,ReentrantLock可以通过调用unlock方法释放锁,更加灵活。
死锁
多线程死锁:同步中嵌套同步,且锁对象不相同,导致锁无法释放。
死锁解决办法:不要在同步中嵌套同步
public class 死锁的产生 {
public static void main(String[] args) {
//创建线程任务对象
Ticket ticket = new Ticket();
//创建三个窗口对象
Thread t1 = new Thread(ticket, "窗口1");
Thread t2 = new Thread(ticket, "窗口2");
Thread t3 = new Thread(ticket, "窗口3");
//卖票
t1.start();
t2.start();
t3.start();
}
static class Ticket implements Runnable {
Object lock = new Object();
private int ticket = 100;
@Override
public void run() {
String name = Thread.currentThread().getName();
while (true) {
if ("窗口1".equals(name)) {
synchronized (lock) {
sell(name);
}
} else {
sell(name);
}
if (ticket <= 0) {
break;
}
}
}
private synchronized void sell(String name) {
synchronized (lock) {
if (ticket > 0) {
System.out.println(name + "卖票:" + ticket);
ticket--;
}
}
}
}
}