网上看了很多ReentrantLock和synchronized的区别,但是我想问的一点是,方法分别被两个锁锁上时,线程需要获取的锁有什么区别?本人测试方法A和方法B,一个用ReentrantLock加锁一个用synchronized加锁,还会出现线程同步问题。
贴代码吧~~
public static void main(String[] args) throws InterruptedException {
ThreadDemo demo = new ThreadDemo();
new Thread(new Thread0(demo)).start();
new Thread(new Thread1(demo)).start();
System.out.println("main结束");
}
public class Thread0 implements Runnable{
private ThreadDemo demo;
public Thread0(ThreadDemo demo){
this.demo = demo;
}
@Override
public void run() {
demo.A();
System.out.println("0结束");
}
}
public class Thread1 implements Runnable{
private ThreadDemo demo;
public Thread1(ThreadDemo demo){
this.demo = demo;
}
@Override
public void run() {
demo.B();
System.out.println("1结束");
}
}
public class ThreadDemo {
private Lock lock = new ReentrantLock();
public void A(){
try {
lock.lock();
for(int i=0; i<10; i++){
System.out.println("A"+Thread.currentThread().getName()+":"+i);
Thread.sleep(100);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
lock.unlock();
}
}
public synchronized void B(){
// lock.lock();
for(int i=0; i<10; i++){
System.out.println("B"+Thread.currentThread().getName()+":"+i);
}
// lock.unlock();
}
}
结果:
AThread-0:0
main结束
BThread-1:0
BThread-1:1
BThread-1:2
BThread-1:3
BThread-1:4
BThread-1:5
BThread-1:6
BThread-1:7
BThread-1:8
BThread-1:9
1结束
AThread-0:1
AThread-0:2
AThread-0:3
AThread-0:4
AThread-0:5
AThread-0:6
AThread-0:7
AThread-0:8
AThread-0:9
0结束
@让我发会呆: 你那个sleep。。。。。
@Daniel Cai: sleep并不会释放锁,只是让为了让同步问题展现出来,否则10条数据很难出现异步。
@让我发会呆: 多线程和异步是两码事,我是说你那个sleep导致b执行完了a还在sleep。
@Daniel Cai: 这个就是我要问的问题,如果把A方法改为synchronized,获取把B方法改为用lock.lock()锁住,即使你再怎么sleep,依然会其中一个线程执行完,另一个线程才会执行
@让我发会呆: 你怎么还没明白?你那一sleep,100ms这种循环可以跑上万次,你这sleep一下肯定会导致最后输出变成那样。
@Daniel Cai: 把B方法改为
public void B(){
lock.lock();
for(int i=0; i<10; i++){
System.out.println("B"+Thread.currentThread().getName()+":"+i);
}
lock.unlock();
}
A不变
运行结果:
AThread-0:0
main结束
AThread-0:1
AThread-0:2
AThread-0:3
AThread-0:4
AThread-0:5
AThread-0:6
AThread-0:7
AThread-0:8
AThread-0:9
0结束
BThread-1:0
BThread-1:1
BThread-1:2
BThread-1:3
BThread-1:4
BThread-1:5
BThread-1:6
BThread-1:7
BThread-1:8
BThread-1:9
1结束
可以看出两线程是串行的,A方法再怎么sleep,也不会改变运行结果啊。
@让我发会呆: 你放大迭代次数到1k你再看。
@Daniel Cai: 哇,园龄8年老司机啊,崇拜!
不过现在只有两个线程,其中一个进入A时,获取锁,无论它执行多慢,sleep多久,其他的线程肯定没办法再次获取该锁的,只有其中一个线程执行完,另一个才会去执行,这应该是没问题的吧,现在是两种锁混合使用,没有起到锁该有的作用。这两种锁应该是有一些比较深层的区别,我也只是好奇问一问,看看有没有遇到类似问题的圆友。
@让我发会呆: 前面说的是对的。
后面不对,10次迭代周期太短,很难看到区别,你要么就两个方法内都sleep 10 ms看下结果。
从本质上说synchronized锁实例,ReentrantLock通过cas来模拟所谓“锁”的过程。
而且后者可以有着更加精细的控制,比如从lock派生的ReadWriteLock等。
PS:你的代码没有任何所谓的同步问题
@Daniel Cai: 第一种结果的加锁方式,即使不加任何sleep代码,当迭代次数达到一万时,会出现Thread0和Thread1交替执行,这难道不是同步问题吗?
@让我发会呆:你这里连竞争都没有,哪来的同步问题?
@Daniel Cai: 说的我有点懵逼了,怎么就没有竞争了,两个线程都去访问ThreadDemo的方法,这不是竞争吗?而且我加锁不就是为了解决因为竞争(并发)可能导致数据错乱的问题啊。
@让我发会呆: 这两个线程虽然都是访问同一个对象,但它们之间没任何关系啊,
一个线程访问a访问,一个线程访问b方法,这两个方法间又没有共享变量,你这里就算把这些同步措施全部拿掉结果也不会有任何变化。
你可以把i提升为对象字段,然后两个方法分别for(;i++;i<1000),不使用任何同步手段就会出现问题
for(;i++;i<100){
//your work here
Thread.sleep(1);
}
@让我发会呆: ps下,如果使用我上面说的方法如果需要保证数据正确性,必须使用同种同步方式,如果还是按照你的代码两种混在一起还是有问题。
关于线程安全性,你只要保证在任意时刻如果多个线程对同一对象做了变化,那么你就要注意了。
@Daniel Cai: 我只是代码里没有对同一数据进行改变,模拟一下而已,因为如果出现线程交替运行,我就认为会出现线程并发的问题啦
@Daniel Cai: 是的,这两个锁一起用并没什么用,以后遇到再去发现原因吧,谢谢你了