第一种:继承Thread的方式实现
public class Ticket extends Thread{
public static int TICKETS = 100; //剩余的票数
private String id;
public Ticket(String id) {
this.id = id;
}
@Override
public void run() {
while (true){
if (TICKETS > 0){
synchronized (this) {
System.out.println(this.id + "---------" + TICKETS--);
}
}
else {
System.out.println("没票了!");
break;
}
}
}
}
因为书上说继承的方式实现的多线程不能访问同一资源,所以我就给线程类中定义了一个static的属性来让他们共同访问,结果没有问题,如图:
没有任何问题。
然后我在循环体中,加了个sleep(),如下:
public class Ticket extends Thread{
public static int TICKETS = 100; //剩余的票数
private String id;
public Ticket(String id) {
this.id = id;
}
@Override
public void run() {
while (true){
if (TICKETS > 0){
try {
sleep(100);
} catch (Exception e) {
System.out.println(e);
}
synchronized (this) {
System.out.println(this.id + "---------" + TICKETS--);
}
}
else {
System.out.println("没票了!");
break;
}
}
}
}
结果就变了:
可以看到,出现了多个站台出售同一张票的情况。即出现了线程不安全的情况。然后我查看了一下运行结果的行数,发现并没有三百行,也就是说并不是每一张票都被出售了三次,所以我的猜想是:是先执行println语句,再进行TICKETS++,这样,在执行了println之后,还没来得及执行TICKETS++就被切到了别的线程,刚开始我还在疑惑,在println和++语句上,我不是加了synchronized吗,后来才想到,我是通过继承的方式实现多线程,也就是说,这三个线程源于不同的类,所以锁标记为this根本对这三个线程没有任何限制作用。至于没有加sleep()之前为什么执行正常,大概是CPU太快了。
第二种:实现Runnable的方式实现
public class Ticket2 implements Runnable {
private int tickets = 100;
@Override
public void run() {
while (true) {
if (tickets > 0) {
System.out.println(Thread.currentThread().getName() + "---------" + tickets--);
} else {
System.out.println("没票了!!");
break;
}
}
}
}
下面是Client类:
public class Client {
public static void main(String[] args) {
//实现接口的方式实现线程
Ticket2 tt = new Ticket2();
Thread tt1 = new Thread(tt,"tt1");
Thread tt2 = new Thread(tt,"tt2");
// tt1.setPriority(10); //设置线程优先级,尽可能的先执行优先级高的线程,但不绝对(1-10,10的优先级最高,默认5)
tt1.start();
tt2.start();
}
}
运行结果:
因为前面没加锁,所以第一遍直接就出现了线程不安全的情况,下面演示加锁:
public class Ticket2 implements Runnable {
private int tickets = 100;
@Override
public void run() {
while (true) {
synchronized (this) {
if (tickets > 0) {
System.out.println(Thread.currentThread().getName() + "---------" + tickets--);
} else {
System.out.println("没票了!!");
break;
}
}
}
}
}
运行结果没有任何问题,如下:
然后像之前一样,加入一个sleep();
public class Ticket2 implements Runnable {
private int tickets = 100;
@Override
public void run() {
while (true) {
synchronized (this) {
if (tickets > 0) {
try {
sleep(100);
} catch (Exception e) {
System.out.println(e);
}
System.out.println(Thread.currentThread().getName() + "---------" + tickets--);
} else {
System.out.println("没票了!!");
break;
}
}
}
}
}
运行结果和上面一样,也没有任何问题;
结论:
在多线程环境下,要访问同一资源,线程要使用实现接口的方式实现,而不能用继承类来实现,即使在类中添加static变量供不同的子类访问,但是,由于他是不同的类实现的线程,各自的对象锁也不一样,所以用synchronized(this)不能锁住对应代码块,同样也会发生线程不安全的情况。
本文探讨了在多线程环境下,通过继承Thread类与实现Runnable接口两种方式实现售票系统的线程安全问题。对比分析了不同实现方式下,同步锁this在确保线程安全中的有效性,指出实现接口方式优于继承类方式。

被折叠的 条评论
为什么被折叠?



