java多线程访问同一变量及简单同步问题(待后续整理)
2015-06-26 16:09
323 查看
先贴代码:
import java.util.Vector;
public class Counter {
public static int count = 0;
// synchronized
public static void inc() {
for (int i = 0; i < 1000; i++){
count++;
}
System.out.println(Thread.currentThread().getName() + "----" + count);
}
public static void main(String[] args) {
Vector<Thread> ts = new Vector<Thread>();
for (int i = 0; i < 200; i++) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
Counter.inc();
}
});
ts.add(t);
t.start();
}
for (Thread t : ts) {
try {
t.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("----------" + Counter.count);
}
}
很简单的逻辑,就是for循环开新的线程然后每个线程执行对static静态变量count的修改。
在inc()方法中,对count用for循环执行操作很重要。因为现在的电脑性能都应经很好(一般情况下资源都浪费了),所以如果简单地执行一次count++的话,又没有一些线程和它竞争CPU,所以很有可能看不到实验的结果。(我的机器就是这种情况,找我们项目组的哥问了才了解)。
线上一张图,然后再说:(图片来自:http://www.cnblogs.com/aigongsi/archive/2012/04/01/2429166.html)<----他的这篇文章有问题,可以说代码根本和他说的对不上,下面再喷他,但是他的分析是对的。
我们的用循环新开的线程肯定不是主线程。所以我们的子线程要去访问count变量,就是这样,read -> load -> use -> assign -> store -> write.
read 和load一下子就OK了,但是use,assign,store是对自己现成的这块内存的操作,是可以进行很多次和需要时间的。
那么问题容易出现在什么地方呢,就出在这块时间上,世界并不是静止的,计算机也一样。你操作的这段时间,其他的进程也可以操作(因为你并没有加锁同步)。街上的美女又不是只有你可以看,对不对。
好了,这就是问题所在。执行这段代码的时候一个线程拿到了主线程内存中的count的拷贝,然后要进行1000次加操作。可是1000次还没干完呢,又来了个线程得到了最新的count的拷贝,也进行1000加操作。但是第二个进程得到的拷贝并不是1000,可能只有209.好了,解释到这里就可以了。
然后最后打印出最后的count的值,验证代码。
开始喷(轻喷)。上图的作者的代码里面,没有:
for (Thread t : ts) {
try {
t.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
这段代码,然后就输出,就说怎么地怎么地。问什么不对很简单,这段代码的作用是让主进程在所有子进程执行完之后执行。如果子进程没执行完就把半路上的count输出,效果虽然跟没同步是一样的但是绝不是那么回事。
关于让主进程在子进程之后执行的方法主要有两个,以后再说。哦,对了,join()的意思是:“等待该线程执行完之后再执行”,将主语和宾语带入,就是:“等待我子线程执行完之后你主线程再执行”。
(如果把join用在产生新线程的for循环中的话你会发现线程ID都是排的整整齐齐的呢。。。。为什么应该不难理解)
接下来想想,同步的话怎么搞呢?
synchrinized放在public和static之间就好(这么详细我也醉了)。然后运行一下看看,你就懂了。
import java.util.Vector;
public class Counter {
public static int count = 0;
// synchronized
public static void inc() {
for (int i = 0; i < 1000; i++){
count++;
}
System.out.println(Thread.currentThread().getName() + "----" + count);
}
public static void main(String[] args) {
Vector<Thread> ts = new Vector<Thread>();
for (int i = 0; i < 200; i++) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
Counter.inc();
}
});
ts.add(t);
t.start();
}
for (Thread t : ts) {
try {
t.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("----------" + Counter.count);
}
}
很简单的逻辑,就是for循环开新的线程然后每个线程执行对static静态变量count的修改。
在inc()方法中,对count用for循环执行操作很重要。因为现在的电脑性能都应经很好(一般情况下资源都浪费了),所以如果简单地执行一次count++的话,又没有一些线程和它竞争CPU,所以很有可能看不到实验的结果。(我的机器就是这种情况,找我们项目组的哥问了才了解)。
线上一张图,然后再说:(图片来自:http://www.cnblogs.com/aigongsi/archive/2012/04/01/2429166.html)<----他的这篇文章有问题,可以说代码根本和他说的对不上,下面再喷他,但是他的分析是对的。
我们的用循环新开的线程肯定不是主线程。所以我们的子线程要去访问count变量,就是这样,read -> load -> use -> assign -> store -> write.
read 和load一下子就OK了,但是use,assign,store是对自己现成的这块内存的操作,是可以进行很多次和需要时间的。
那么问题容易出现在什么地方呢,就出在这块时间上,世界并不是静止的,计算机也一样。你操作的这段时间,其他的进程也可以操作(因为你并没有加锁同步)。街上的美女又不是只有你可以看,对不对。
好了,这就是问题所在。执行这段代码的时候一个线程拿到了主线程内存中的count的拷贝,然后要进行1000次加操作。可是1000次还没干完呢,又来了个线程得到了最新的count的拷贝,也进行1000加操作。但是第二个进程得到的拷贝并不是1000,可能只有209.好了,解释到这里就可以了。
然后最后打印出最后的count的值,验证代码。
开始喷(轻喷)。上图的作者的代码里面,没有:
for (Thread t : ts) {
try {
t.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
这段代码,然后就输出,就说怎么地怎么地。问什么不对很简单,这段代码的作用是让主进程在所有子进程执行完之后执行。如果子进程没执行完就把半路上的count输出,效果虽然跟没同步是一样的但是绝不是那么回事。
关于让主进程在子进程之后执行的方法主要有两个,以后再说。哦,对了,join()的意思是:“等待该线程执行完之后再执行”,将主语和宾语带入,就是:“等待我子线程执行完之后你主线程再执行”。
(如果把join用在产生新线程的for循环中的话你会发现线程ID都是排的整整齐齐的呢。。。。为什么应该不难理解)
接下来想想,同步的话怎么搞呢?
synchrinized放在public和static之间就好(这么详细我也醉了)。然后运行一下看看,你就懂了。
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- Python3写爬虫(四)多线程实现数据爬取
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树