解决多线程安全问题的方式(加锁)包含以下几种:
synchronized关键字:又包含同步代码块,同步方法
Lock锁:
锁的分类: 类锁(class锁),对象锁
实例:现有如下代码,总共100张票,3个窗口同时售卖,这里采用实现Runnable接口的方式,还可以使用继承Thread方式实现,Runnable与Thread类详解
public class RunnableWindowTest{
public static void main(String[] args) {
RunnableWindow runnableWindow = new RunnableWindow();
new Thread(runnableWindow,"窗口1").start();
new Thread(runnableWindow,"窗口2").start();
new Thread(runnableWindow,"窗口3").start();
}
}
class RunnableWindow implements Runnable {
private int ticket=100;
@Override
public void run() {
while (true){
if (ticket>0){
System.out.println(Thread.currentThread().getName()+":卖出票号"+ticket);
ticket--;
// 睡眠100毫秒凸显问题
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
break;
}
}
}
}
出现的问题,运行时,会出现重票,错票的问题,可以明显看到下图,出现很多重票,同时很多票号也不见了,例如11号票
image.png解决方案
synchronized关键字之-同步代码块
同步代码快之类锁方式实现线程安全:使用synchronized包裹if-else块,如果直接锁住while循环,会导致第一个进去的线程一直抢占cpu资源,虽然票号正确,但是所有票都是同一个窗口卖出
class RunnableWindow implements Runnable {
private int ticket = 100;
@Override
public void run() {
while (true) {
// 新加行,synchronized (RunnableWindow.class)表示锁住当前类,synchronized (this)指代当前类的实例对象
//synchronized (this){
synchronized (RunnableWindow.class) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + ":卖出票号" + ticket);
ticket--;
// 睡眠100毫秒凸显问题
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
break;
}
}
}
}
}
同步代码块之对象锁方式实现线程安全
class RunnableWindow implements Runnable {
private int ticket = 100;
// 新加行,创建一个object对象
private Object obj = new Object();
@Override
public void run() {
while (true) {
// 新加行,使用对象来锁住当前代码块
synchronized (obj) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + ":卖出票号" + ticket);
ticket--;
// 睡眠100毫秒凸显问题
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
break;
}
}
}
}
}
synchronized关键字之-同步方法
同步方法之对象锁方式实现线程安全:synchronized关键字修饰的非静态方法,表示当锁为当前类对象实例,如果修饰的为静态方法,则表示锁为当前类,为类锁
class RunnableWindow implements Runnable {
private int ticket = 100;
@Override
public void run() {
while (true) {
// 新加行调用同步方法
getTicket();
// 循环判断票数是否小于等于0,是则跳出循环
if (ticket<=0){
break;
}
}
}
// synchronized关键字修饰方法,实现同步方法
private synchronized void getTicket(){
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + ":卖出票号" + ticket);
ticket--;
// 睡眠100毫秒凸显问题
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Lock锁方式:lock()与unlock()方法之间的代码为同步代码块,每次只允许一个线程进入
class RunnableWindow implements Runnable {
private int ticket = 100;
// 新建一个ReentrantLock实例
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
// 新加行
lock.lock();
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + ":卖出票号" + ticket);
ticket--;
// 睡眠100毫秒凸显问题
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
break;
}
} finally {
// 新加行
lock.unlock();
}
}
}
}
image.png
网友评论