大家好,我是奕叔😈,一个工作八年快退休的程序员,微信:lyfinal,交个朋友,一起交流Java后端技术,相互成长,成为更优秀的🐒~
锁是一个非常难并且复杂的问题,需要计算机底层熟悉才能更好的理解。
- 首先,同步锁有两种,JVM的synchronized和JDK的ReentrantLock;
- 然后,多个线程访问这个类的两个方法也有不同的形式,例如访问这个类的两个方法是通过同一个类的实例对象来访问还是通过不同的类的实例对象访问;
- 再者,一个类的两个方法加了同步锁,这两个被同步方法也没有说明是什么样的方法。他可能是类的普通实例方法,也可能是类中Runnable对象的run方法。
一.synchronized
1.多个线程同时访问同一个类实例对象的两个同步方法:
java
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();
}
}
}
输出结果:
java
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一个该类实例
java
final Example1 example2 = new Example1() ;
再修改thread2的run方法调用的类实例为example2
java
Thread thread2 = new Thread(new Runnable() {@
Override
public void run() {
example2.method2();
}
});
得到结果:
java
同步方法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结束
小结:这是因为synchronized是对象锁,即线程获得的锁是施加在一个实例对象上的,如果不同的线程访问的是同一对象上的不同的同步方法,那么显然不能同时进行。
如果是不同对象上的不同的同步方法,那么就是可以同时进行的。
3.多个线程同时访问同一个类实例对象的两个Runnable对象的run方法:
java
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
}
}
输出结果:
java
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也是同样的效果)
java
new Thread(example.runnable1).start(); //同步方法1
new Thread(example.runnable2).start(); //同步方法2
打印出这个this对象,是两个不同的类实例对象:
java
synchronizedTest.Example2$1@65db6dfasynchronizedTest.Example2$2@471fab
也说明了不同线程的实例对象不同,都是各自对象的锁,不可以认为是类似于例子1中的同一实例对象,而应该类似与例子2的不同类的实例对象
总结:分析synchronized同步锁的核心在于他是个对象锁,找清楚锁的对象。