一个类中的两个方法都加了同步锁,多个线程能同时访问这个类的两个方法吗?

看到这个问题第一反应是不懂,然后查询了网上的一些说法,感觉略有不一。细看之下发现大家对这个问题的分析角度不太一样,就会出现不同的结果,在这里记一下我收集来的不同的角度和自己的例子,感觉这个题目还是蛮有意思的。

首先,同步锁有两种,JVM的synchronized和JDK的ReentrantLock;
然后,多个线程访问这个类的两个方法也有不同的形式,例如访问这个类的两个方法是通过同一个类的实例对象来访问还是通过不同的类的实例对象访问;
再者,一个类的两个方法加了同步锁,这两个被同步方法也没有说明是什么样的方法。他可能是类的普通实例方法,也可能是类中Runnable对象的run方法。

看到这里也许会觉得我对于问题过于的咬文嚼字,但是我想要探讨更多的可能,不同的情形有着不同的结果,而且这些不同的情形能开拓思路,让我们看问题能多个角度,也可以帮我加深多线程的理解。如果本文中有错误或者不恰当的例子,或者代码写的不严谨不规范风格不好,都可以留言提出。

一.synchronized

1.多个线程同时访问同一个类实例对象的两个同步方法:

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方法调用的类实例为example2

Thread 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结束

这时候显然,多个线程是能访问同个类(的不同实例对象)的两个同步方法的。

小结:这是因为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结束

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结束

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两个情况:

一.对于synchronized
1.一个类中的两个方法都加了同步锁,多个线程不能同时访问这个类的同一实例对象的两个方法
2.一个类中的两个方法都加了同步锁,多个线程能同时访问这个类的不同实例对象的两个方法
3.一个类中的两个方法**(Runnable的run方法)都加了同步锁,多个线程能**同时访问这个类的两个方法(不论是不是同一实例对象)

二.对于ReentrantLock
1.一个类中的两个方法都加了同步锁,多个线程不能同时访问这个类的同一实例对象的两个方法(不论同步加在实例方法中或是run方法中)
2.一个类中的两个方法都加了同步锁,多个线程能同时访问这个类的不同实例对象的两个方法(不论同步加在实例方法中或是run方法中)
————————————————
版权声明:本文为CSDN博主「Geepyyyy」的原创文章。
原文链接:一个类中两个方法都加了同步锁,多个线程能同时访问这个类的两个方法吗

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 如果这两个方法锁的是同一个对象,那么当一个线程进入其中一个方法并获得了锁之后,另一个线程就无法进入另一个方法,因为该对象已经被锁住了。只有当第一个线程释放锁之后,第二个线程才能获得锁并进入另一个方法。 如果这两个方法锁的是不同的对象,那么两个线程可以同时进入各自的方法,因为它们并没有竞争同一个锁。 ### 回答2: 在一个类中,如果两个方法都使用了锁来进行同步控制,当一个线程进入了其中一个方法时,另一个线程将无法同时进入另一个方法。 当一个线程进入了一个锁的方法时,它会获取到该方法对应的锁,并且其他线程需要等待该锁的释放才能进入这个方法。这是因为锁的作用是保证同一时间只有一个线程能够执行锁的方法,防止多个线程同时访问某个共享资源导致线程安全问题。 因此,即使类中两个锁的方法,当一个线程获取了其中一个方法的锁之后,另一个线程无法同时进入另一个方法,它需要等待第一个线程释放对应的锁才能继续执行。 总结起来,通过在需要同步控制的方法锁,可以确保在同一时间只有一个线程能够执行该方法,其他线程需要等待锁的释放才能进入该方法。 ### 回答3: 一个类中两个方法了锁,当一个线程进入其中一个方法,另一个线程不能同时进入另一个方法。在Java中,锁可以通过synchronized关键字实现,该关键字可以用来修饰方法或者代码块。当一个线程进入了一个被synchronized修饰的方法,它会获得该方法的锁,并执行其中的代码。此时,其他线程如果想要进入同一个被synchronized修饰的方法,需要等待该锁的释放。因此,当一个线程进入了第一个方法,另一个线程不能同时进入第二个方法。 实际上,锁是为了保护共享资源的并发访问。当多个线程同时对某个共享资源进行读写操作时,为了避免出现竞态条件和数据不一致的问题,可以使用锁机制确保同一时间只有一个线程能够访问共享资源。 在上述情况下,两个方法了锁,是为了保证同一时间只有一个线程能够执行这两个方法中的任意一个。如果一个线程已经获取了第一个方法的锁,那么第二个方法的锁就会被另一个线程阻塞,直到第一个方法的锁被释放。只有当第一个方法的锁被释放后,另一个线程才能获取该锁,并进入第二个方法执行相关的代码。 因此,当一个线程进入了一个方法,另一个线程不能同时进入另一个方法。只有等待第一个线程释放锁后,才能进行竞争获取锁资源。这样可以保证对共享资源的访问线程安全的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值