代码如下:
class Counter {
private static int count;
//Object lock = new Object();
public int getCount() {
synchronized(Counter.class){
return count++;
}
// synchronized(lock){
// return count++;
// }
}
}
这个类用来生成序列号,测试发现:
如果在 getCount方法上面用同步 public [color=red]synchronized[/color] int getCount() {...}当并发调用线程很多时,返回的序列号仍然会有重复。
如果自定义一个对象,在这个对象上应用同步,也会出现序列号重复问题。
只有对 Counter.class 加锁才能够达到预期的效果。
这三种方式有啥区别,线程的那些书上面的例子不都是在方法上面直接用同步么?请各位帮忙解答。
楼主,我完全理解你的意思!你一定看楼上的回复看得晕了吧?那么,我会以简单易懂的语言来回答这个问题,请往下看:
1、你总共用了三种加锁的方法:
(1)
[code="java"]
public int getCount() {
synchronized(Counter.class){
return count++;
}
[/code]
(2)
[code="java"]synchronized(lock){
return count++;
} [/code]
(3)
[code="java"]public synchronized int getCount() {...}[/code]
2、其实,你的第(3)种方法和如下方式是等同的:
[code="java"]
public int getCount() {
synchronized(this){
return count++;
}
[/code]
3、那么,你需要明白java的加锁机制是怎样的。即,多个线程执行一段代码,只有拿到这段代码的锁之后才能执行,其他线程如果拿不到这个“锁”则只能等待,这叫做“互斥”。而要确保线程与线程之间的互斥,只有当他们去拿锁时,那个锁是同一个对象!(这点理解到很重要,是同一对象!)。
4、好了,看了上面我的说明,这下你能明白错误发生原因了。
A. 在你的第(1)种加锁方式中,用的是Counter.class,这是类的元对象,在整个虚拟机运行过程中可以认为是唯一的,他是在类被加载的时候创建的(这个稍微扯得有点远,你明白他是唯一的一个对象就ok了),所以不同线程执行里面的代码时,它们是去拿的同一把锁,也就能保证互斥了。
B. 方式(2)加锁中,很明显,不同线程中,你会new不同的Counter,那么,作为“锁”的对象lock也就不是同一个,也就无法保证互斥。
C. 方式(3)相信你应该能理解了吧,你在不同线程中用new的不同Counter调用方法,this肯定也就不同了。
5、ok,希望你看懂了我的说明,我可是手敲的这么字啊。呵呵,如有不懂之处,可以给我私信哈。我当时学多线程时也是相当痛苦,不过理解了就发现也不是那么痛苦了。
synchronized用在方法上,是对这个类的某个变量进行锁定
比如某个对象P1在多个线程里同时执行synchronized的方法,只能互斥地依次调用,但是另一个对象P2却可以和P1同时调用。
synchronized(Object)也是对对象上锁,获取到这个对象锁的线程才能执行
synchronized(Object.class)是在对象所属的类上加锁,相当于将包含这个synchronized的方法变成static synchronized,所有对象调用该方法都需要同步
[quote]如果在 getCount方法上面用同步 public synchronized int getCount() {...}当并发调用线程很多时,返回的序列号仍然会有重复。 [/quote]
你把这个方法变成synchronized static试试
private static int count;
这个修改下,改成java.util.concurrent.atomic.AtomicInteger
这个是原子的++操作,因为那个++本身他不是原子操作所以有可能出现重号.
AtomicInteger使用参考下API,功能呢就是int的那些功能.
看不出问题.
主要不知道你线程那里是怎么写的.
猜测是每个线程里都有一个Counter 对象,
每个对象调用的 Counter 对象.getCount() 是不同的,是各自对象本身的,所以锁不上.
而 Counter.class 是作为整个 ClassLoader 共享的,简单的说是唯一的.所以可作为临界区的锁.
public synchronized int getCount() {...}
改为
public static synchronized int getCount()
{}
[code="java"]
// synchronized(lock){
// return count++;
// }
[/code]
这样锁不上,也是因为 lock 这个 对象是属于各个线程里的Counter对象的,
如果在线程里面new Counter ,每个Counter.lock 是不同的,
就是说,他们想要获取的锁是不一样的.
附个例子:[quote]
public class TestSynchronized
{
public static void main(String[] args) throws InterruptedException
{
final Test t = new Test();
List list = new ArrayList();
Thread thread;
for (int i = 0; i < 10; i++)
{
thread = new Thread() {
public void run()
{
for (int j = 0; j < 100; j++)
t.up();
}
};
thread.start();
list.add(thread);
}
Iterator<Thread> it = list.iterator();
while (it.hasNext())
{
it.next().join();
}
System.out.println(t.get());
}
}
class Test
{
int i = 0;
public synchronized int up()
{
int j = i;
try
{
Thread.sleep(1);
} catch (InterruptedException e)
{
e.printStackTrace();
}
return i = j + 1;
}
public int get()
{
return i;
}
}
[/quote]
[code="java"]
public class TestSynchronized
{
public static void main(String[] args) throws InterruptedException
{
final Test t = new Test();
List list = new ArrayList();
Thread thread;
for (int i = 0; i < 10; i++)
{
thread = new Thread() {
public void run()
{
for (int j = 0; j < 100; j++)
t.up();
}
};
thread.start();
list.add(thread);
}
Iterator<Thread> it = list.iterator();
while (it.hasNext())
{
it.next().join();
}
System.out.println(t.get());
}
}
class Test
{
int i = 0;
public synchronized int up()
{
int j = i;
try
{
Thread.sleep(1);
} catch (InterruptedException e)
{
e.printStackTrace();
}
return i = j + 1;
}
public int get()
{
return i;
}
}
[/code]