public class MyRunnable implements Runnable{
Integer a=0; //实例变量
public void run(){
synchronized(a){
for( int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+" "+a++);
}
}
}
public static void main(String args[]){
MyRunnable mr=new MyRunnable ();
Thread t1=new Thread(mr); //Thread(Runnable r)
Thread t2=new Thread(mr);
t1.start();
t2.start();
}
}执行结果为:Thread-0 0
Thread-0 2
Thread-1 1
Thread-1 3
Thread-1 4
Thread-1 5
Thread-1 6
Thread-1 7
Thread-1 8
Thread-1 9
Thread-1 10
Thread-1 11
Thread-0 12
Thread-0 13
Thread-0 14
Thread-0 15
Thread-0 16
Thread-0 17
Thread-0 18
Thread-0 19看上去,synchronized没有起作用啊!!!
这是怎么回事?
Integer a=0; //实例变量
public void run(){
synchronized(a){
for( int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+" "+a++);
}
}
}
public static void main(String args[]){
MyRunnable mr=new MyRunnable ();
Thread t1=new Thread(mr); //Thread(Runnable r)
Thread t2=new Thread(mr);
t1.start();
t2.start();
}
}执行结果为:Thread-0 0
Thread-0 2
Thread-1 1
Thread-1 3
Thread-1 4
Thread-1 5
Thread-1 6
Thread-1 7
Thread-1 8
Thread-1 9
Thread-1 10
Thread-1 11
Thread-0 12
Thread-0 13
Thread-0 14
Thread-0 15
Thread-0 16
Thread-0 17
Thread-0 18
Thread-0 19看上去,synchronized没有起作用啊!!!
这是怎么回事?
Thread-0 0
Thread-0 1
Thread-0 2
Thread-0 3
Thread-0 4
Thread-0 5
Thread-0 6
Thread-0 7
Thread-0 8
Thread-0 9
Thread-1 10
Thread-1 11
Thread-1 12
Thread-1 13
Thread-1 14
Thread-1 15
Thread-1 16
Thread-1 17
Thread-1 18
Thread-1 19
改为
Integer a = new Integer(0);
synchronized是为了不让多个现成同时访问同一个地方线程 1
---》 some object
线程 2
楼主你这个虽然加了一个sychronized,那其实就是线程自己在run,根本没有其他线程的互斥访问的概念, 你代码里的
MyRunnable mr=new MyRunnable ();
只是充当了我上面提到的特殊的some object你去掉synzheonized看看,其实效果一样的否则你想好了,估计按你的思路,一旦一个线程跑进synchronized块,势必要把for循环跑完才能让第二个线程接着执行for,实际上Thread 0 和 Thread 1是交叉的,不觉得矛盾吗?
上面一位兄弟看起来运行结果很整齐的可能机器比较快,Thread 0 瞬间就跑完了
其实这个原因并不是synchronized没有起作用 而是和环境有关 并且因为数据的原因有时候锁不住
也有可能 解决的办法就是在中间每次让线程休眠一下public class MyRunnable implements Runnable{
Integer a=0; //实例变量
public void run(){
synchronized(a){
for( int i=0;i <10;i++){
System.out.println(Thread.currentThread().getName()+" "+a++);
}
}
}
public static void main(String args[]){
MyRunnable mr=new MyRunnable ();
Thread t1=new Thread(mr); //Thread(Runnable r)
Thread t2=new Thread(mr);
t1.start();
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
t2.start();
}
}
都说了
Integer a = 0;
改为
Integer a = new Integer(0);
会不会是jdk对互斥机制做过改动,楼主的jdk什么版本?在打印前放了个sleep,下面是打印结果synchronized(a):
Thread-1 0
Thread-1 1
Thread-1 2
Thread-1 3
Thread-1 4
Thread-1 5
Thread-1 6
Thread-1 7
Thread-1 8
Thread-1 9
Thread-0 10
Thread-0 11
Thread-0 12
Thread-0 13
Thread-0 14
Thread-0 15
Thread-0 16
Thread-0 17
Thread-0 18
Thread-0 19无互斥段:
Thread-1 0
Thread-0 1
Thread-1 2
Thread-0 3
Thread-1 4
Thread-0 5
Thread-1 6
Thread-0 7
Thread-1 8
Thread-0 9
Thread-1 10
Thread-0 11
Thread-1 12
Thread-0 13
Thread-0 14
Thread-1 15
Thread-0 16
Thread-1 17
Thread-0 18
Thread-1 19
这和jdk的Integer的缓存机制有关
Integer a = 0;
改为
Integer a = new Integer(0);
就正确了synchronized的这种写法没有问题
后面我在网上搜索发现原因可能是这样的,因为a++这个操作,a的引用就变了,你锁的效果就不在了。而且经过检验确实好像是这样的。你可以再你的类里在加一个Integer b = a;然后你在循环里取看b == a这个情况就不成立了,他们的引用不一样了。
其实是有作业的 只不过是在开始的一段时间里
别说100次了,运行1000次也正常,而且是在print之前加了Thread.sleep(1),如果互斥没有生效是不可能正常打印的Thread-1 996
Thread-1 997
Thread-1 998
Thread-1 999
Thread-0 1000
Thread-0 1001
Thread-0 1002
Thread-0 1003
……
……
public class MyRunnable implements Runnable {
private int[] a = new int[] { 0 }; public void run() {
synchronized (a) {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " "
+ a[0]++);
}
}
} public static void main(String args[]) {
MyRunnable mr = new MyRunnable();
for (int i = 0; i < 100; i++) {
Thread t = new Thread(mr);
t.start();
}
}
}
sychronized是为了防止多线程访问
由于对象锁在不停的变化,所以导致了你这个和没锁的样子差不多,谁拿到对象锁而且有CPU时间片就可以进去了
重新分析一下“
Thread-0先拿到锁"0",而Thread-1在锁改变之前也去争抢"0",结果就是等待Thread-0运行完互斥段,释放锁"0"下面人为制造了一个延迟,使Thread-1拿到新的锁,结果比较清晰的了public class MyRunnable implements Runnable{
Integer a = 0; //实例变量
public void run(){
synchronized(a){
for( int i=0;i <100;i++){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" "+a++);
}
}
}
public static void main(String args[]){
MyRunnable mr=new MyRunnable ();
Thread t1=new Thread(mr); //Thread(Runnable r)
Thread t2=new Thread(mr);
t1.start();
try {
Thread.sleep(3000);
System.out.println("");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
t2.start();
}
}
当执行++时,一个新的Integer对象产生了这时a又引用这个新对象。这样就成了一边获得某个对象同步信号,一边又放弃了当前获得获得的同步信号,又尝试获得新对象的同步信号。正确的作法如下: public void run() {
synchronized (this) {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " " + a++);
}
}
}
好了 上代码import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class ThreadTest {
public static void main(String[] args) {
//创建两个线程
ExecutorService service = Executors.newFixedThreadPool(2);
final Manager manager = new Manager();
//执行线程
service.execute(new Runnable(){ @Override
public void run() {
for (int i = 0; i < 10; i++) {
manager.printA1();
}
}
});
service.execute(new Runnable(){
@Override
public void run() {
for (int i = 0; i < 10; i++) {
manager.printA2();
}
}
});
service.shutdown();//删除所有线程,程序结束
}
}/**
* 业务类.线程执行的方法
* @author Administrator
*
*/
class Manager
{
//要打印的变量
private int a = 0;
//标识此时是否该A1运行
private boolean bshouldA1Run = true;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void printA1()
{
try {
lock.lock();
//如果不该A1运行,那么等待.
if(!bshouldA1Run)
condition.await();
System.out.println(Thread.currentThread().getName() + ",a = " + a++);
//线程运行完毕以后要让线程A2执行,所以修改标识变量为false
this.bshouldA1Run = false;
//唤醒等待的单个线程
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
}
finally
{
lock.unlock();
}
}
public void printA2()
{
try {
lock.lock();
if(bshouldA1Run)
condition.await();
System.out.println(Thread.currentThread().getName() + ",a = " + a++);
this.bshouldA1Run = true;
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
}
finally
{
lock.unlock();
}
}
}
Integer等8中基本数据类型 和String 都有 池 的概念。
涉及到池就麻烦了。
a++;之后 引用a 指向的是 对象 还是 池中的数字呢?看来,使用synchronized锁一个变化的Integer是肯定有问题的
改用Dog类,就锁住了。
Integer等8中包装类型,和String类
都有 池 的概念。当执行a++;引用指向的是原来的对象,还是 池中的数字?我们无法判断
如果 是原来的对象(也就是 原来对象的值是增加了,并且引用a还指向那里),那么Synchronized是能锁住的。
如果 a指向了池中的数字,(已经不是原来的对象了),那么Synchronized一点用都没有。事实也是这样!
综述:
用Synchronized来锁Integer等8种包装类型和String是没有意义的。另:
有没有一种方法,能打印出 a++ 的地址(就像Object类的toString()方法一样),这样就能判断是否是原来的对象?