import java.util.ArrayList;
public class ThreadTest {
public static void main(String[] args) {
ArrayList arr = new ArrayList();
for (int i =0; i<10 ; i++)
arr.add(new MyTh(i));
for (int i =0; i<10 ; i++) {
((Thread ) arr.get(i)).start();
}
}
}
class MyTh extends Thread {
int num;
volatile int age;
MyTh (int k)
{this.num=k; }
@Override
public void run() {
if(this.num==4){
setAge(40);
}
System.out.println(Thread.currentThread().getName() + " :"+this.num + ":" + getAge());
}
synchronized int getAge() {
return age;
}
synchronized void setAge(int k) {
this.age =k;
}
}
我在这里感到困惑,为什么程序的输出只打印一次volatile变量age = 40的值。 我试图在getter / setter中保持同步,然后也只有第4个单线程将其打印40。为什么其他后续线程也不能读取它40?
Thread-0 :0:0
Thread-1 :1:0
Thread-2 :2:0
Thread-3 :3:0
Thread-4 :4:40
Thread-5 :5:0
Thread-6 :6:0
Thread-7 :7:0
Thread-8 :8:0
Thread-9 :9:0
答案 0 :(得分:1)
为什么在变量被声明为synchronized
的同时,将volatile
关键字用于getter和setter方法。这里不需要。您只需要可见性,而不是原子性。所以像这样更改代码,
public class MyTh extends Thread {
int num;
static volatile int age;
MyTh(int k)
{
this.num = k;
}
@Override
public void run() {
if (this.num == 4) {
setAge(40);
}
System.out.println(Thread.currentThread().getName() + " :" + this.num + ":" + getAge());
}
int getAge() {
return age;
}
void setAge(int k) {
age = k;
}
}
首先将age
变量标记为static
,以便在类的所有实例之间共享该变量。在您以前的案例中,这是一个实例字段,因此每个实例都有自己的副本。因此,一个人所做的更改对其他人不可见。
最新输出如下。
Thread-0 :0:0
Thread-3 :3:0
Thread-2 :2:0
Thread-1 :1:0
Thread-4 :4:40
Thread-6 :6:40
Thread-8 :8:40
Thread-5 :5:40
Thread-7 :7:40
Thread-9 :9:40
答案 1 :(得分:0)
我相信您打算将年龄作为静态变量。
static volatile int age;
答案 2 :(得分:0)
您具有age的实例副本,因此它不是共享副本,请使用static关键字使age类变量
尝试下面的代码,我想知道什么
import java.util.ArrayList;
公共类MyTh扩展了Thread { 整数;
static volatile int age;
MyTh(int k)
{
this.num = k;
}
@Override
public void run()
{
if (this.num == 4)
{
setAge(40);
}
System.out.println(Thread.currentThread().getName() + " :" + this.num + ":" + getAge());
}
synchronized int getAge()
{
return age;
}
synchronized void setAge(int k)
{
this.age = k;
}
public static void main(String[] args)
{
ArrayList arr = new ArrayList();
for (int i = 0; i < 10; i++)
arr.add(new MyTh(i));
for (int i = 0; i < 10; i++)
{
((Thread) arr.get(i)).start();
}
}
}
答案 3 :(得分:0)
首先,您正在使用instance variable instead of class variable。您的意图需要后一个。
第二,您不需要在代码中使用volatile
关键字,因为您已经在其getter / setter上使用了synchronized
。 Here is its reason.
此外,为了使您在修改arr
时遇到同步问题,应该使用一种机制来克服它,例如CopyOnWriteArrayList
。这是一些有问题的输出示例,
Thread-9 :9:40
Thread-6 :6:40
Thread-1 :1:0
Thread-8 :8:40
Thread-4 :4:40
Thread-2 :2:0
Thread-5 :5:40
Thread-7 :7:40
Thread-3 :3:40 // <---- ???
Thread-0 :0:0
Thread-1 :1:0
Thread-3 :3:0 // <----
Thread-8 :8:40
Thread-7 :7:40
Thread-5 :5:40
Thread-6 :6:40
Thread-2 :2:0
Thread-4 :4:40
Thread-0 :0:0
Thread-9 :9:40
Thread-1 :1:40 // <---- ??? woww
Thread-3 :3:40 // <---- ??? woww
Thread-8 :8:40 // <---- ok
Thread-7 :7:40 // <---- ok
Thread-5 :5:40 // <---- ok
Thread-6 :6:40 // <---- ok
Thread-2 :2:40 // <---- ??? woww
Thread-4 :4:40 // <---- ok
Thread-0 :0:0
Thread-9 :9:40 // <---- ok
此处的问题是 ArrayList
的操作未同步,也不是线程安全的。 CopyOnWriteArrayList
可用于实现这一目标。
import java.util.concurrent.CopyOnWriteArrayList;
class ThreadTest {
public static void main(String[] args) {
CopyOnWriteArrayList<Thread> arr = new CopyOnWriteArrayList<>();
for (int i =0; i<10 ; i++)
arr.add(new MyTh(i));
for (int i =0; i<10 ; i++) {
(arr.get(i)).start();
}
}
}
class MyTh extends Thread {
int num;
static /*volatile*/ int age;
MyTh (int k)
{this.num=k; }
@Override
public void run() {
if(this.num==4){
setAge(40);
}
System.out.println(Thread.currentThread().getName() + " :"+this.num + ":" + getAge());
}
synchronized int getAge() {
return age;
}
synchronized void setAge(int k) {
this.age =k;
}
}
答案 4 :(得分:0)
这里有两件事:
您正在使用实例变量 age 并创建10个MyTh类实例,因此每个实例将拥有自己的副本,并且不会互相干扰。因此,根据@tanyehzheng的建议,您必须将其设置为静态,以便可以在所有10个实例之间共享。
您关于如果我们有静态变量,volatile将产生什么变化的问题: