读书人

一个有关线程有关问题的疑惑比较简单

发布时间: 2013-04-02 12:35:26 作者: rapoo

一个有关线程问题的疑惑,比较简单,我是新手,请指教下
如下这段代码,我希望打印0-100,但实际运行还是会产生重复的数,我应该怎么改写代码呢,我已经使用了synchronized关键词啊?


public class Test{

public static void main(String[ ] args) {
ThreadTest a = new ThreadTest();
for(int i=0;i<100;i++){
new Thread(a).start();
a.print();
}
}
}

class ThreadTest implements Runnable {
Count c = new Count();

@Override
public void run() {
c.increase();
}

public void print() {
System.out.println(c.getValue());
}
}

final class Count {
private long value = 0;

public synchronized long getValue() {
return value;
}

public synchronized long increase() {
if(value == Long.MAX_VALUE) {
throw new IllegalStateException("error");
}

return ++value;
}
}

[解决办法]
你只是同步了increase方法内部和getValue方法内部,在方面调用之间和与其他方法一块用时,无法保证同步。要想不重复就把打印的代码和增加value的代码放到一个同步代码块里面。
[解决办法]
不知道你的代码想实现一个什么功能。
new Thread(a).start();
a.print();
这两行什么意思啊?完全没有体现多线程与同步的思想啊。
你在一个线程里面实现count+1,在主线程里面调用print方法,有什么意义呢?

如下是我修改了你的代码,同时给你一个,我写的一个例子。希望对你有帮助。
public class Test22{

public static void main(String[ ] args) {
ThreadTest a = new ThreadTest();
for(int i=0;i<100;i++){
new Thread(a).start();
// a.print();
}


}
}

class ThreadTest implements Runnable {
Count c = new Count();

public void run() {
c.increase();
}

// public void print() {
// System.out.println(c.getValue());
// }
}

final class Count {
private long value = 0;

public synchronized long getValue() {
return value;
}

public synchronized long increase() {
if(value == Long.MAX_VALUE) {
throw new IllegalStateException("error");
}
System.out.println(++value);


return value;
}
}



public class VolatileTest {

static Integer count = 0;

static volatile Integer countVolatile = 0;

static Integer countSynchronizedMethod = 0;

static Integer countSynchronizedAttribute = 0;

static void addCount(){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
count ++;
}

static void addCountVolatile(){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
countVolatile ++;
}

static synchronized void addCountSynchronizedMethod(){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
countSynchronizedMethod ++;
}

static void addCountSynchronizedAttribute(){
synchronized(countSynchronizedAttribute){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
countSynchronizedAttribute ++;
}

}

/**
* 功能:
* @param args
*/
public static void main(String[] args) {

ThreadGroup group = new ThreadGroup("test");

for (int i = 0; i < 1000; i++) {
Thread thread = new Thread(group,""){
public void run() {
addCount();
addCountVolatile();
addCountSynchronizedMethod();
addCountSynchronizedAttribute();
}
};

thread.start();
}

try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}


System.out.println("count:" + count);
System.out.println("countVolatile:" + countVolatile);
System.out.println("countSynchronized:" + countSynchronizedMethod);
System.out.println("countSynchronizedAttribute:" + countSynchronizedAttribute);

}

}


[解决办法]
1楼里面的代码会出现问题是因为你在main方法中调用了a.print方法. 这个时候前面一行语句new Thread(a).start()并不一定就执行了.(换句话说就是这个在main方法中执行到a.print())的时候前面启动的线程中的run()方法并不一定就已经执行.)
后来的代码中出现加不加synchronized关键字都一样的情况其实只是错觉.因为你电脑够快, 所以每次运行的时候都是直接过掉了. 如果你的代码在run方法里面的increae()方法和System.out.println()中间加上其他代码的话就有可能引起打印数据异常的情况. 所以还是要添加synchronized关键字
另外, 您这个程序中虽然可以保证每次都打印从1~100, 但是你没法保证打印出来的数字的顺序.
这一点你可以在run()方法两行代码中加入一个for循环(1~100的循环就够了)就可以看出来.
[解决办法]
引用:
谢谢 经过楼上提醒 我把代码改成了这样 问题貌似解决了 但是又有一个新问题了,我把Count类中的synchronized去掉,打印的结果居然跟同步的一模一样。这样怎么才能区分出synchronized的同步性呢?
……
---------------------

关键是要把两个动作increase和print同步在一起。你代码中的同步synchronized 都是没有意义的。

你比较一下下面两个不同方案的执行结果吧:

// 方案1 :同步
public void run() {
synchronized (c) { // 这里是关键,把(动作1,动作2)同步
// 这意味着,在动作1和动作2之间,别的线程插不进来


c.increase(); // 动作1

try {
Thread.currentThread().sleep(100); // Sleep 加强观察效果
} catch (InterruptedException e) {}

System.out.println(c.getValue()); // 动作2
}
}

// 方案2:无同步
public void run() {
c.increase(); // 动作1

try {
Thread.currentThread().sleep(100); // Sleep 加强观察效果
} catch (InterruptedException e) {}

System.out.println(c.getValue()); // 动作2
}

另外,

// 方案1 :同步
public void run() {
synchronized (c) {
//...body
}
}

// 等同于:
public synchronized void run() {
//...body
}

读书人网 >J2SE开发

热点推荐