
定制线程类中的实例变量可以与其他线程共享或不共享. 这是在多个线程之间进行交互时的重要技术要点.
1. 不共享数据时
未共享数据的情况如图1-18所示.

图1-18未共享数据
让我们看一个不共享数据的示例.
创建一个用于实验的Java项目t3,MyThread.java类代码如下:
公共类MyThread扩展了线程{
private int count = 5;
public MyThread(String name){
super();
this.setName(name); //设置线程名称
}
@Override
public void run(){
super.run();
同时(计数> 0){
count-;
System.out.println(“您” + this.currentThread(). getName()
+“计算,计数=” +计数);

}
}
}
Run.java的代码如下:
公共类运行{
public static void main(String [] args){
MyThread a = new MyThread(“ A”);
MyThread b = new MyThread(“ B”);
MyThread c = new MyThread(“ C”);
a.start();
b.start();
c.start();
}
}
程序运行的结果如图1-19所示.
从图1-19中可以看到,该程序总共创建了3个线程,每个线程都有自己的计数变量,并减少了自己的计数变量的值. 在这种情况下,变量不会共享. 此示例在任何情况下都不会有多个线程访问同一个实例变量.
如果要实现三个线程对一个count变量执行减法运算,应如何设计代码?
2. 数据共享情况
数据共享情况如图1-20所示.


对于共享数据,多个线程可以访问相同的变量. 例如,在实现投票功能的软件中,多个线程同时处理同一个人的投票.
让我们看一个共享数据的例子.
创建t4测试项目,MyThread.java类代码如下:
公共类MyThread扩展了线程{
private int count = 5;
@Override
public void run(){
super.run();
count-;
//在此示例中,请勿使用while语句,否则将导致其他线程无法运行
//因为第一个线程执行while语句会将计数值减小为0
//减法总是由一个线程执行
System.out.println(“由“ + this.currentThread(). getName()+”计算,count =” + count);
}
}
Run.java的代码如下:
公共类运行{
public static void main(String [] args){
MyThread mythread = new MyThread();
Thread a = new Thread(mythread,“ A”);

线程b =新线程(mythread,“ B”);
Thread c = new Thread(mythread,“ C”);
线程d =新线程(mythread,“ D”);
线程e =新线程(mythread,“ E”);
a.start();
b.start();
c.start();
d.start();
e.start();
}
}
程序运行的结果如图1-21所示.
从图1-21中可以看到,线程A和B输出的计数值均为3,表明A和B同时处理计数,从而产生了``非线程安全性''问题. 但是我们要获得输出的结果不会重复,应该依次减小.
发生非线程安全的情况是因为在某些JVM中,count--的操作需要分解为以下三个步骤(执行这三个步骤的过程将被其他线程中断):
1)获取原始计数值.
2)计算count-1.
3)分配要计数的值.
在这三个步骤中,如果同时有多个线程正在访问,则很可能出现非线程安全问题. 获取重复值的步骤如图1-22所示.

A线程和B线程将计数减1以获得相同的4的过程如下.

1)以1为时间单位,A线程获得计数变量5的值.
2)B线程以2为时间单位获得计数变量5的值.
3)A线程以3为时间单位执行计数计算,并将计算出的值4存储在临时变量中.
4)B线程以4为时间单位执行计数计算,并将计算出的值4存储在临时变量中.
5)在时间单位5,A线程在临时变量中分配值4进行计数.
6)B线程以6为时间单位在临时变量中分配值4进行计数.
7)最终结果是A线程和B线程获得了相同的计算结果4,并且出现了非线程安全问题.
实际上,此示例是典型的销售场景. 五个销售人员,每个销售人员在售出产品后不能获得相同的剩余数量,必须在每个销售人员售出产品后才能获得,其余销售人员只有这样,您才能继续对新剩余项目减去1. 这时,您需要同步多个线程多线程技术问题解决,即使用顺序队列减去1并按如下所示更改代码:
公共类MyThread扩展了线程{
private int count = 5;
@Override
同步的public void run(){
super.run();
count-;
System.out.println(“由“ + this.currentThread(). getName()+”计算,count =” + count);
}
}
重新运行程序后,将不会显示相同的值,如图1-23所示.

通过在run()方法之前添加synced关键字,可以在执行run()方法时在队列中处理多个线程. 程调用run()方法之前,首先确定run()方法是否已锁定. 如果run()方法被锁定,则意味着其他线程正在调用run()方法,并且必须等待其他线程运行(). 调用完成后,线程可以执行run()方法,这也达到了将run()方法排队的目的,从而按顺序将count变量减少1. 同步可以锁定任何对象和方法,被锁定的代码称为“互斥区域”或“关键区域”.
当线程想要以同步方法执行代码时,该线程将首先尝试申请锁定. 如果可以应用锁定,则线程将同步执行代码. 如果无法应用该锁,则该线程将继续尝试申请该锁多线程技术问题解决,直到被应用为止,并且多个线程将同时争用该锁.
本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/jisuanjixue/article-266386-1.html
强行驱离