在同一个JVM下,如何确保线程的安全呢?
本篇文章主要通过synchronized关键词确保线程的安全性,在不考虑线程池的情况下,线程的创建请参考上一篇文章。
通过一个通俗的案例,来更加形象的说明我们的线程是安全的,需求如下:我们现在总共有10张通往大理的飞机票,并且有三个售票窗口进行售票,该如何设计购票程序,使其不会有少售,多售的情况出现?
在不使用线程安全的情况下:
定义三个售票窗口如下:
MyRunableUnSyn myRunable = new MyRunableUnSyn();
Thread thread = new Thread(myRunable, "窗口一");
Thread thread1 = new Thread(myRunable, "窗口二");
Thread thread2 = new Thread(myRunable, "窗口三");
thread.start();
thread1.start();
thread2.start();
定义具体售票操作如下:
public class MyRunableUnSyn implements Runnable {
static int tickets = 10; // 定义十张票
@Override
public void run() {
while (true){
if (tickets > 0){
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() +" 出售了第 " + tickets-- + " 张票");
}catch (Exception e){
e.printStackTrace();
}
}
}
}
}
此时进行卖票,肯定会出现同一张票多卖的情况,执行结果如下:
第一种:在使用线程安全的情况下
定义三个售票窗口如下:
说明 :
此时是不同线程是使用的同一个具体售票的对象,此时买票是串行操作
MyRunable myRunable = new MyRunable();
Thread thread = new Thread(myRunable, "窗口一");
Thread thread1 = new Thread(myRunable, "窗口二");
Thread thread2 = new Thread(myRunable, "窗口三");
thread.start();
thread1.start();
thread2.start();
定义具体售票操作如下:
public class MyRunable implements Runnable {
static int tickets = 10; // 定义十张票
@Override
public void run() {
while (true){
synchronized (this){
if (tickets > 0){
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() +" 出售了第 " + tickets-- + " 张票");
}catch (Exception e){
e.printStackTrace();
}
}
}
}
}
}
此时进行卖票,肯定不会出现多卖、少卖的情况,执行结果如下:
第二种:在使用线程安全的情况下
定义三个售票窗口如下:
此时这种情况下是并行操作的,线程不安全的,在线程中的变量不是共享的。
MyRunable myRunable1 = new MyRunable();
MyRunable myRunable2 = new MyRunable();
MyRunable myRunable3 = new MyRunable();
Thread thread3 = new Thread(myRunable1, "窗口一");
Thread thread4 = new Thread(myRunable2, "窗口二");
Thread thread5 = new Thread(myRunable3, "窗口二");
thread3.start();
thread4.start();
thread5.start();
将并行转成串行,线程是安全的,具体操作如下:
MyRunable myRunable1 = new MyRunable();
MyRunable myRunable2 = new MyRunable();
MyRunable myRunable3 = new MyRunable();
Thread thread3 = new Thread(myRunable1, "窗口一");
Thread thread4 = new Thread(myRunable2, "窗口二");
Thread thread5 = new Thread(myRunable3, "窗口二");
thread3.start();
thread4.start();
thread5.start();
// join()是将并行转为串行操作
thread3.join();
thread4.join();
thread5.join();
定义具体售票操作如下:
参考第一种
第三种:在使用线程安全的情况下,并行情况下保证线程安全
定义三个售票窗口如下:
并发情况
MyRunable myRunable1 = new MyRunable();
MyRunable myRunable2 = new MyRunable();
MyRunable myRunable3 = new MyRunable();
Thread thread3 = new Thread(myRunable1, "窗口一");
Thread thread4 = new Thread(myRunable2, "窗口二");
Thread thread5 = new Thread(myRunable3, "窗口二");
thread3.start();
thread4.start();
thread5.start();
定义具体售票操作如下:
public class MyRunable implements Runnable {
static int tickets = 10; // 定义十张票
@Override
public void run() {
while (true){
synchronized (MyRunable.class){
if (tickets > 0){
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() +" 出售了第 " + tickets-- + " 张票");
}catch (Exception e){
e.printStackTrace();
}
}
}
}
}
}
为什么此种情况下,线程是安全的?
synchronized关键词锁的是当前同一个当前类,确保同步的是一个东西,所以线程是安全的
第四种:在使用线程安全的情况下,并行情况下保证线程安全(第三种的另一种写法)
定义三个售票窗口如下:
参考第三种
定义具体售票操作如下:
public class MyRunable implements Runnable {
static int tickets = 10; // 定义十张票
@Override
public void run() {
while (true){
if (tickets > 0){
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() +" 出售了第 " + tickets-- + " 张票");
}catch (Exception e){
e.printStackTrace();
}
}
}
}
}
为什么此种情况下,线程是安全的?
因为每次静态方法每次调用的内部变量,都是局部变量,每次调用静态方法时都会为它重新分配内存空间,所以是安全的。