其他
【245期】面试官:同类中两个方法加同步锁,多个线程支持同时访问这两个方法吗?
点击上方“Java精选”,选择“设为星标”
别问别人为什么,多问自己凭什么!
下方有惊喜,留言必回,有问必答!
每天 08:15 更新文章,每天进步一点点...
package synchronizedTest;
public class Example1 {
private int num = 0 ;
(省略getter.setter,后同)
public synchronized void method1() {
System.out.println("同步方法1进入");
for(int i = 0 ; i<10 ; i++) {
System.out.print("同步方法1:"+num+"--");
num++ ;
}
System.out.println("同步方法1结束");
}
public synchronized void method2() {
System.out.println("method2进入:");
for(int i = 0 ; i<10 ; i++) {
System.out.print("method2:"+num+"--");
num++ ;
}
System.out.println("method2结束");
}
public static void main(String[] args) {
final Example1 example1 = new Example1() ;
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
example1.method1();
}
}) ;
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
example1.method2();
}
}) ;
try {
thread2.join();
thread1.join();
thread1.start();
thread2.start();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
输出结果:method1进入
同步方法1:0--同步方法1:1--同步方法1:2--同步方法1:3--同步方法1:4--同步方法1:5--同步方法1:6--同步方法1:7--同步方法1:8--同步方法1:9--method1结束
method2进入:
method2:10--method2:11--method2:12--method2:13--method2:14--method2:15--method2:16--method2:17--method2:18--method2:19--method2结束
显然此时多个线程是不能访问同个类(的一个实例对象)的两个同步方法的2.多个线程同时访问同一个类的不同实例对象的两个同步方法:将上面的代码稍作修改,主函数中多new一个该类实例
final Example1 example2 = new Example1() ;
再修改thread2的run方法调用的类实例为example2Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
example2.method2();
}
}) ;
得到结果:同步方法1进入
method2进入:
method2:0--method2:1--同步方法1:0--同步方法1:1--同步方法1:2--method2:2--同步方法1:3--method2:3--同步方法1:4--method2:4--同步方法1:5--method2:5--同步方法1:6--同步方法1:7--method2:6--同步方法1:8--同步方法1:9--method2:7--同步方法1结束
method2:8--method2:9--method2结束
这时候显然,多个线程是能访问同个类(的不同实例对象)的两个同步方法的。另外,更多面试题推荐公众号Java精选,回复java面试,获取最新面试题资料,支持在线随时随地刷题。小结:这是因为synchronized是对象锁,即线程获得的锁是施加在一个实例对象上的,如果不同的线程访问的是同一对象上的不同的同步方法,那么显然不能同时进行。如果是不同对象上的不同的同步方法,那么就是可以同时进行的。3.多个线程同时访问同一个类实例对象的两个Runnable对象的run方法:package synchronizedTest;
public class Example2 {
private int num ;
public Runnable runnable1 = new Runnable() {
@Override
public void run() {
//同步锁
synchronized (this) {
System.out.println("线程1进入");
for(int i = 0 ; i < 10 ; i ++) {
System.out.print("线程1:"+num+"--");
}
System.out.println("线程1结束");
}
}
};
public Runnable runnable2 = new Runnable() {
@Override
public void run() {
//同步锁
synchronized (this) {
System.out.println("thread2进入");
for(int i = 0 ; i < 10 ; i ++) {
System.out.print("thread2:"+num+"--");
}
System.out.println("thread2结束");
}
}
};
public static void main(String[] args) {
Example2 example = new Example2() ; //创建一个对象
new Thread(example.runnable1).start(); //同步方法1
new Thread(example.runnable2).start(); //同步方法2
}
}
输出结果:thread2进入
线程1进入
thread2:0--线程1:0--线程1:0--thread2:0--线程1:0--线程1:0--线程1:0--thread2:0--线程1:0--thread2:0--thread2:0--线程1:0--thread2:0--线程1:0--thread2:0--thread2:0--线程1:0--thread2:0--线程1:0--thread2:0--线程1结束
thread2结束
可见此时多个线程是能同时访问同个类的两个同步方法的。这是因为synchronized(this){ //... }中锁住的不是代码块,即这个锁在run方法中,但是并不是同步了这个run方法,而是括号中的对象this,也就是说,多个线程会拿到各自的锁,就能够同时执行run方法。(在run方法前声明synchronized也是同样的效果)
new Thread(example.runnable1).start(); //同步方法1
new Thread(example.runnable2).start(); //同步方法2
打印出这个this对象,是两个不同的类实例对象:synchronizedTest.Example2$1@65db6dfa
synchronizedTest.Example2$2@471fab
也说明了不同线程的实例对象不同,都是各自对象的锁,不可以认为是类似于例子1中的同一实例对象,而应该类似与例子2的不同类的实例对象。总结:分析synchronized同步锁的核心在于他是个对象锁,找清楚锁的对象二.ReentrantLock锁1.多个线程同时访问同一个类实例对象的两个同步方法:将例子1的synchronized改为引入ReentrantLock
package ReentrantLockTest;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
private int num;
private Lock lock = new ReentrantLock();
public void method1() {
lock.lock();
System.out.println("同步方法1进入");
for(int i = 0 ; i<10 ; i++) {
System.out.print("同步方法1:"+num+"--");
num++ ;
}
System.out.println("同步方法1结束");
lock.unlock();
}
public void method2() {
lock.lock();
System.out.println("method2进入:");
for (int i = 0; i < 10; i++) {
System.out.print("method2:" + num + "--");
num++;
}
System.out.println("method2结束");
lock.unlock();
}
public static void main(String[] args) {
final LockExample example = new LockExample() ;
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
example.method1();
}
}) ;
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
example.method2();
}
}) ;
try {
thread2.join();
thread1.join();
thread1.start();
thread2.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
输出结果:同步方法1进入
同步方法1:0--同步方法1:1--同步方法1:2--同步方法1:3--同步方法1:4--同步方法1:5--同步方法1:6--同步方法1:7--同步方法1:8--同步方法1:9--同步方法1结束
method2进入:
method2:10--method2:11--method2:12--method2:13--method2:14--method2:15--method2:16--method2:17--method2:18--method2:19--method2结束
可见此时多个线程是不能访问同个类(的一个实例对象)的两个同步方法的2.多个线程同时访问同一个类的不同实例对象的两个同步方法:修改main函数的即可:public static void main(String[] args) {
final LockExample example1 = new LockExample() ;//两个实例
final LockExample example2 = new LockExample() ;
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
example1.method1(); //实例1的同步方法1
}
}) ;
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
example2.method2();//实例2的同步方法2
}
}) ;
try {
thread2.join();
thread1.join();
thread1.start();
thread2.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
输出结果:同步方法1进入
method2进入:
同步方法1:0--method2:0--method2:1--同步方法1:1--method2:2--同步方法1:2--同步方法1:3--method2:3--同步方法1:4--method2:4--同步方法1:5--同步方法1:6--method2:5--同步方法1:7--method2:6--同步方法1:8--同步方法1:9--同步方法1结束
method2:7--method2:8--method2:9--method2结束
可见,多个线程是能访问同个类(的不同实例对象)的两个同步方法的。总结:ReentrantLock和synchronized的前两个例子结论都相同3.多个线程同时访问同一个类实例对象的两个Runnable对象的run方法:package ReentrantLockTest;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Lockexample2 {
private int num;
private Lock lock = new ReentrantLock();
public Runnable runnable1 = new Runnable() {
@Override
public void run() {
lock.lock();//上锁
System.out.println("线程1进入");
for(int i = 0 ; i < 10 ; i ++) {
System.out.print("线程1:"+num+"--");
}
System.out.println("线程1结束");
lock.unlock();
}
};
public Runnable runnable2 = new Runnable() {
@Override
public void run() {
lock.lock();//上锁
System.out.println("thread2进入");
for(int i = 0 ; i < 10 ; i ++) {
System.out.print("thread2:"+num+"--");
}
System.out.println("thread2结束");
lock.unlock();
}
};
public static void main(String[] args) {
Lockexample2 example = new Lockexample2();
new Thread(example.runnable1).start();
new Thread(example.runnable2).start();
}
}
输出结果:线程1进入
线程1:0--线程1:0--线程1:0--线程1:0--线程1:0--线程1:0--线程1:0--线程1:0--线程1:0--线程1:0--线程1结束
thread2进入
thread2:0--thread2:0--thread2:0--thread2:0--thread2:0--thread2:0--thread2:0--thread2:0--thread2:0--thread2:0--thread2结束
这里可以看到,与synchronized的第三个例子出现了不同的结果。在这个地方,ReentrantLock不允许多线程同时访问一个类的不同同步方法。这里要注意的是ReentrantLock与synchronized不同,ReentrantLock的实现方式是要先创建ReentrantLock对象,然后用这个对象的方法来上锁。而一个类的实例中只有一个ReentrantLock对象:
private Lock lock = new ReentrantLock();
而本例中,线程的创建是建立在同一个类实例上的:Lockexample2 example = new Lockexample2();
new Thread(example.runnable1).start();
new Thread(example.runnable2).start();
因此,ReentrantLock对象lock是同一个,因此第一个线程进入同步方法1后就获取了锁,第二个线程无法获取这个锁,只能等待。如果换成是两个实例对象:public static void main(String[] args) {
Lockexample2 example = new Lockexample2();
Lockexample2 example2 = new Lockexample2();
new Thread(example.runnable1).start();
new Thread(example2.runnable2).start();
}
输出结果线程1进入
thread2进入
线程1:0--线程1:0--线程1:0--线程1:0--thread2:0--线程1:0--thread2:0--thread2:0--thread2:0--thread2:0--thread2:0--thread2:0--thread2:0--线程1:0--thread2:0--线程1:0--线程1:0--thread2:0--线程1:0--thread2结束
线程1:0--线程1结束
可见不同的实例对象中是不同的ReentrantLock对象,因此可以同时访问小结:ReentrantLock锁的核心在与ReentrantLock对象是不是同一个。三.结论重新看看这个问题:一个类中的两个方法都加了同步锁,多个线程能同时访问这个类的两个方法吗?现在应该比较清楚了,这个问题要分成synchronized和ReentrantLock两个情况:1、对于synchronized1)一个类中的两个方法都加了同步锁,多个线程不能同时访问这个类的同一实例对象的两个方法2)一个类中的两个方法都加了同步锁,多个线程能同时访问这个类的不同实例对象的两个方法3)一个类中的两个方法**(Runnable的run方法)都加了同步锁,多个线程能**同时访问这个类的两个方法(不论是不是同一实例对象)2、对于ReentrantLock1)一个类中的两个方法都加了同步锁,多个线程不能同时访问这个类的同一实例对象的两个方法(不论同步加在实例方法中或是run方法中)2)一个类中的两个方法都加了同步锁,多个线程能同时访问这个类的不同实例对象的两个方法(不论同步加在实例方法中或是run方法中)版权声明:本文为CSDN博主「Geepyyyy」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
https://blog.csdn.net/weixin_40616523/article/details/87883267
3000+ 道面试题在线刷,最新、最全 Java 面试题!
【238期】Java 8 中 Lambda 实现原理及源码剖析!
【239期】面试官问:你觉得 ThreadLocalRandom 这玩意安全吗?
【240期】面试官问:说说基于 Redis 实现延时队列服务?
【241期】面试官问:有了 for 循环 为什么还要 forEach?
最近有很多人问,有没有读者&异性交流群,你懂的!想知道如何加入。加入方式很简单,有兴趣的同学,只需要点击下方卡片,回复“加群”,即可免费加入交流群!