【稀里糊涂多线程】实现Runnable后多线程操作同一个实例变量注意点
程序员文章站
2024-01-26 08:19:22
...
在讲述继承Thread和实现Runnable接口时,我们说到两者的区别中有一个区别是实现Runnable接口,可以多个线程操作同一个实例变量,如下代码,抢票的例子,3个线程共同去操作同一个变量ticket:
public class MainClass {
public static void main(String [] args) throws InterruptedException {
Runnable runnable = new QiangPiaoThread();
new Thread(runnable,"小明").start();
new Thread(runnable,"小红").start();
new Thread(runnable,"小牛").start();
}
}
class QiangPiaoThread implements Runnable{
Integer ticket = 10;
@Override
public void run() {
String name = Thread.currentThread().getName();
while(ticket>0) {
try {
synchronized (ticket) {
System.out.println(name + "抢票了第" +ticket + "张票");
ticket = ticket -1;
}
Thread.sleep(1000L);
}catch(Exception ex){}
}
}
}
// 运行结果
小明抢票了第10张票
小红抢票了第9张票
小牛抢票了第8张票
小牛抢票了第7张票
小明抢票了第6张票
小红抢票了第5张票
小红抢票了第4张票
小明抢票了第3张票
小牛抢票了第2张票
小红抢票了第1张票
小明抢票了第0张票
小牛抢票了第-1张票
这面这段代码通过Runnable来让三个线程操作同一个ticket变量,这事实现接口的一个特点。但是我这里要讲的是,当synchronized和ticket为了实现原子性共同使用时,要注意的一些问题,
从代码的打印结果可以看出,虽然我使用了synchronized并且也用ticket作为了锁,但是结果还是错的。错误的原因是while条件中的ticket>0导致的。因为while(ticket>0)这里还没有通过synchronized实现原子性的操作,所以导致当ticket=1时,小红,小明,小牛都进入了while中,这样就出现三个线程都会执行ticket-1的操作。导致出现了0和-1的错误数据。
所以在实现Runnable来操作同一个变量如ticket时,已经要将对ticket的操作(比较,加减等操作),放到synchronized,下面对其进行修改,如下:
public class MainClass {
public static void main(String [] args) throws InterruptedException {
Runnable runnable = new QiangPiaoThread();
new Thread(runnable,"小明").start();
new Thread(runnable,"小红").start();
new Thread(runnable,"小牛").start();
}
}
class QiangPiaoThread implements Runnable{
Integer ticket = 10;
@Override
public void run() {
String name = Thread.currentThread().getName();
while(true) {
try {
synchronized (ticket) {
if(ticket == 0) { //将对ticket的操作放到这里
break;
}
System.out.println(name + "抢票了第" +ticket + "张票");
ticket = ticket -1;
}
Thread.sleep(1000L);
}catch(Exception ex){}
}
}
}
// 运行结果
小明抢票了第10张票
小红抢票了第9张票
小牛抢票了第8张票
小明抢票了第7张票
小红抢票了第6张票
小牛抢票了第5张票
小红抢票了第4张票
小明抢票了第3张票
小牛抢票了第2张票
小红抢票了第1张票
Process finished with exit code 0
这样通过将ticket放到synchronized中,作为原子操作的一部分,这样就不会出现错结果了。所以当使用Runnable来让多个线程使用同一个共享变量时,一定要注意对这个共享变量如代码中ticket的操作的的位置,一定要在synchronized中。
上一篇: 多次点击多次进入同一个界面