一个有关线程问题的疑惑,比较简单,我是新手,请指教下
如下这段代码,我希望打印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的循环就够了)就可以看出来.
[解决办法]
---------------------
关键是要把两个动作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
}