1.问题的产生
原因:多个线程操作同一个共享数据。
原理:多个线程在访问共享数据时,由于CPU的随机性,一个线程还没有执行完,执行权被其他线程抢走了,这个时候就有可能出现线程安全问题。
解决方式:把操作共享数据的代码锁起来,让单一线程执行完毕才能执行其他的
2.解决方法【上锁】
三种方法的测试类【测试类相同】
public class Demo {
public static void main(String[] args) {
//创建线程任务对象
TicketRunnable ticketRunnable = new TicketRunnable();
//传递到线程对象中
Thread thread1 = new Thread(ticketRunnable);
Thread thread2 = new Thread(ticketRunnable);
Thread thread3 = new Thread(ticketRunnable);
thread1.setName("1号窗口---");
thread1.start();
thread2.setName("2号窗口---");
thread2.start();
thread3.setName("3号窗口---");
thread3.start();
}
}
打印结果:
----------------------------------------------------------------------------------------------------
2号窗口---卖了一张票,还剩2张票
3号窗口---卖了一张票,还剩1张票
3号窗口---卖了一张票,还剩0张票
<1>同步代码块
锁对象:可以是任意对象,但是要保证唯一;
synchronized(锁对象){
多个线程访问共享数据的代码
}
//1.同步代码块
//具体实现类
public class TicketRunnable implements Runnable {
int ticket = 100;
//Object obj = new Object();
@Override
public void run() {
while (true) {
//同步代码块
//synchronized (obj)//锁对象可以是任意对象,但是必须创建且唯一
//synchronized (this)//[不能直接在内部new,如果线程公用一个任务对象,则可以直接使用这个对象]
synchronized (TicketRunnable.class){//类的字节码对象唯一,也可锁住
if (ticket<=0){
break;
}else {
try {
Thread.sleep(100);//睡眠100毫秒模拟卖票[不能抛出]
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket--;
System.out.println(Thread.currentThread().getName()+"卖了一张票,还剩" + ticket + "张票");
}
}
}
}
}
<2>同步方法/静态同步方法
同步方法:一个线程执行这个方法,另一个线程就必须等待;
同步方法的锁对象:this
public class TicketRunnable implements Runnable {
private static int ticket =100;
@Override
public void run() {
while (true) {
//同步方法
boolean flag = method();
if(flag){
break;
}
}
}
//同步方法 private synchronized boolean method()
//同步静态方法
//同步方法的锁:this
//静态同步方法的锁:类名的.class字节码文件对象
private static synchronized boolean method() {
if (ticket<=0){
return true;
}else {
try {
Thread.sleep(100);//睡眠100毫秒模拟卖票[不能抛出]
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket--;
System.out.println(Thread.currentThread().getName()+"卖了一张票,还剩" + ticket + "张票");
return false;
}
}
}
<3>lock锁手动上锁和解锁
ReentrantLock lock = new ReentrantLock(); //【具体子类或者实现类】创建锁对象
//上锁
lock.lock(); //一个线程执行到这里,其他线程就必须在这里等
//有可能产生安全问题的代码
//解锁
lock.unlock(); //等这个线程执行完了,其他线程才能进来执行。
public class TicketRunnable implements Runnable {
private int ticket = 100;
//创建锁对象
ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
//上锁[默认打开,线程进入关闭]
try {
lock.lock();
if (ticket <= 0) {
break;
} else {
Thread.sleep(100);//睡眠100毫秒模拟卖票[不能抛出]
ticket--;
System.out.println(Thread.currentThread().getName() + "卖了一张票,还剩" + ticket + "张票");
}
//解锁
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
3.死锁情况【面试】
产生条件:就是锁的嵌套导致
概念:当其他线程持有对方所需要的资源,导致这些线程处理等待状态,无法继续执行
public class Demo1 {
public static void main(String[] args) {
Object objA = new Object();
Object objB = new Object();
new Thread (() -> {
while (true){
synchronized (objA){
synchronized (objB){
System.out.println("小康同学正在走路");
}
}
}
}).start();
new Thread(() -> {
while (true){
synchronized (objB){
synchronized (objA){
System.out.println("小微同学正在走路");
}
}
}
}).start();
}
}
打印结果:【程序不结束,且也无法向前运行】
----------------------------------------------------------------------------------------------------
小康同学正在走路
小康同学正在走路
小康同学正在走路
小康同学正在走路
小康同学正在走路
结果分析:
正常情况:小康走完objA和objB,A,B锁正常开闭;
异常情况:小康走过objA,A锁关闭,此时小薇同学走过objB,B锁关闭,次数A,B锁都关闭,导致死锁。