经典八锁问题
1、synchronized标准访问
/**
* @author alex tse
* 8锁问题
* 1、标准访问,先发短信还是先打电话
*/
public class test1 {
public static void main(String[] args) {
phone phone = new phone();
new Thread(()->{
phone.sendSMS(); //线程A执行发短信
},"A").start();
try {
TimeUnit.SECONDS.sleep(1); //休眠1s
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.call(); //线程B执行打电话
},"B").start();
}
}
class phone{
public synchronized void sendSMS(){
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
最终是A线程先执行,B线程后执行
结论一
被synchronized修饰的方法,锁的对象是方法的调用者(对象)。因为两个方法的调用者是同一个,所以两个方法用的是同一个锁,先调用方法的先执行。
2、方法内休眠
/**
* @author alex tse
* 8锁问题
* 1、标准访问,先发短信还是先打电话
* 2、邮件方法暂停4秒钟,请问先打印邮件还是短信?
*/
public class test1 {
public static void main(String[] args) {
phone phone = new phone();
new Thread(()->{
phone.sendSMS();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.call();
},"B").start();
}
}
class phone{
public synchronized void sendSMS(){
try {
TimeUnit.SECONDS.sleep(4); //休眠四秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
结论
被synchronized修饰的方法,锁的对象是方法的调用者(对象)。因为两个方法的调用者是同一个,所以两个方法用的是同一个锁,先调用方法的先执行,第二个方法只有在第一个方法执行完释放锁之后才能执行。
3、增加普通方法
/**
* @author alex tse
* 8锁问题
* 1、标准访问,先发短信还是先打电话
* 2、邮件方法暂停4秒钟,请问先打印邮件还是短信?
* 3、新增一个普通方法hello(),请问先打印邮件还是hello?
*/
public class test1 {
public static void main(String[] args) {
phone phone = new phone();
new Thread(()->{
phone.sendSMS();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
//phone.call();
phone.hello();
},"B").start();
}
}
class phone{
public synchronized void sendSMS(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
public void hello(){
System.out.println("hello");
}
}
结论
普通方法没有被synchronized修饰,不是同步方法,不受锁的影响,不需要等待,其他同步方法需要等待。
4、两个对象
/**
* @author alex tse
* 8锁问题
* 1、标准访问,先发短信还是先打电话
* 2、邮件方法暂停4秒钟,请问先打印邮件还是短信?
* 3、新增一个普通方法hello(),请问先打印邮件还是hello?
* 4、两个生成的对象、请问先打印邮件还是短信?
*/
public class test1 {
public static void main(String[] args) {
phone phone1 = new phone();
phone phone2 = new phone();
new Thread(()->{
phone1.sendSMS(); //phone1
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.call(); //phone2
},"B").start();
}
}
class phone{
public synchronized void sendSMS(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
结论
两个对象,synchronized锁的对象是方法的调用者(对象),因为有两个对象所以有两把锁,两个方法用的不是同一个锁,后调用的方法不需要等待先调用的方法。
5、两个静态同步方法,一个对象
/**
* @author alex tse
* 8锁问题
* 1、标准访问,先发短信还是先打电话
* 2、邮件方法暂停4秒钟,请问先打印邮件还是短信?
* 3、新增一个普通方法hello(),请问先打印邮件还是hello?
* 4、两个生成的对象、请问先打印邮件还是短信?
* 5、两个静态同步方法,同一个对象,请问先打印邮件还是短信?
*/
public class test1 {
public static void main(String[] args) {
phone phone = new phone();
new Thread(()->{
phone.sendSMS();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.call();
},"B").start();
}
}
class phone{
public static synchronized void sendSMS(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public static synchronized void call(){
System.out.println("打电话");
}
}
结论
被synchronized+static修饰的方法,锁的是类的class对象。两个方法都是静态方法,所以用的是同一个锁,先调用的方法先执行
6、两个静态同步方法,两个对象
/**
* @author alex tse
* 8锁问题
* 1、标准访问,先发短信还是先打电话
* 2、邮件方法暂停4秒钟,请问先打印邮件还是短信?
* 3、新增一个普通方法hello(),请问先打印邮件还是hello?
* 4、两个生成的对象、请问先打印邮件还是短信?
* 5、两个静态同步方法,同一个对象,请问先打印邮件还是短信?
* 6、两个静态同步方法,两个对象,请问先打印邮件还是短信?
*/
public class test1 {
public static void main(String[] args) {
phone phone1 = new phone();
phone phone2 = new phone();
new Thread(()->{
phone1.sendSMS();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.call();
},"B").start();
}
}
class phone{
public static synchronized void sendSMS(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public static synchronized void call(){
System.out.println("打电话");
}
}
结论
synchronized+static锁的是类的class模板,类的class是唯一的,所以两个对象的静态方法都被一把锁锁住,先调用的先执行。
7、一个普通同步方法,一个静态同步方法,一个对象
/**
* @author alex tse
* 8锁问题
* 1、标准访问,先发短信还是先打电话
* 2、邮件方法暂停4秒钟,请问先打印邮件还是短信?
* 3、新增一个普通方法hello(),请问先打印邮件还是hello?
* 4、两个生成的对象、请问先打印邮件还是短信?
* 5、两个静态同步方法,同一个对象,请问先打印邮件还是短信?
* 6、两个静态同步方法,两个对象,请问先打印邮件还是短信?
* 7、一个普通同步方法,一个静态同步方法,同一个对象,请问先打印邮件还是短信?
*/
public class test1 {
public static void main(String[] args) {
phone phone = new phone();
new Thread(()->{
phone.sendSMS();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.call();
},"B").start();
}
}
class phone{
public static synchronized void sendSMS(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
结论
synchronized+static锁的是类的class对象,synchronized所得是方法的调用者,所以两个方法不是被同一个锁锁住,后调用的方法不需要等待先调用的。
8、一个普通同步方法,一个静态同步方法,两个对象
/**
* @author alex tse
* 8锁问题
* 1、标准访问,先发短信还是先打电话
* 2、邮件方法暂停4秒钟,请问先打电话还是短信?
* 3、新增一个普通方法hello(),请问先发短信还是hello?
* 4、两个生成的对象、请问先打电话还是短信?
* 5、两个静态同步方法,同一个对象,请问先打电话还是短信?
* 6、两个静态同步方法,两个对象,请问先打电话还是短信?
* 7、一个普通同步方法,一个静态同步方法,同一个对象,请问先打电话还是短信?
* 8、一个普通同步方法,一个静态同步方法,两个对象,请问先打电话还是短信?
*/
public class test1 {
public static void main(String[] args) {
phone phone1 = new phone();
phone phone2 = new phone();
new Thread(()->{
phone1.sendSMS();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.call();
},"B").start();
}
}
class phone{
public static synchronized void sendSMS(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
结论
synchronized+static修饰的是方法是锁住类的class对象,synchronized修饰的方法锁住的是方法的调用者,所以是两个不同的锁,后调用的方法不需要等待先调用的方法。
小结
一个对象里面如果有多个synchronized方法,某个时刻内,只要一个线程去调用其中一个synchronized方法了,其他的线程都要等待,换句话说,在某个时刻内,只能有唯一一个线程去访问这些synchronized方法,锁的是当前对象this,被锁定后,其他的线程都不能进入到当前对象的其他的synchronized方法;
加个普通方法后发现和同步锁无关,换成两个对象后,不是同一把锁,情况立刻变化
都换成静态同步方法后,情况又变化了。所有的非静态的同步方法用的都是同一把锁----实例对象本身
synchronized实现同步的基础:java中的每一个对象都可以作为锁
具体的表现为以下三种形式:
对于普通同步方法,锁的是当前实例对象
对于静态同步方法,锁的是当前的Class对象。
对于同步方法块,锁是synchronized括号里面的配置对象
当一个线程试图访问同步代码块时,他首先必须得到锁,退出或者是抛出异常时必须释放锁,也就是说如果一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁,可以是别的实例对象非非静态同步方法因为跟该实例对象的非静态同步方法用的是不同的锁,所以必须等待该实例对象已经获取锁的非静态同步方法释放锁就可以获取他们自己的锁。
所有的静态同步方法用的也是同一把锁----类对象本身
现和同步锁无关,换成两个对象后,不是同一把锁,情况立刻变化
都换成静态同步方法后,情况又变化了。所有的非静态的同步方法用的都是同一把锁----实例对象本身
synchronized实现同步的基础:java中的每一个对象都可以作为锁
具体的表现为以下三种形式:
对于普通同步方法,锁的是当前实例对象
对于静态同步方法,锁的是当前的Class对象。
对于同步方法块,锁是synchronized括号里面的配置对象
当一个线程试图访问同步代码块时,他首先必须得到锁,退出或者是抛出异常时必须释放锁,也就是说如果一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁,可以是别的实例对象非非静态同步方法因为跟该实例对象的非静态同步方法用的是不同的锁,所以必须等待该实例对象已经获取锁的非静态同步方法释放锁就可以获取他们自己的锁。
所有的静态同步方法用的也是同一把锁----类对象本身
这两把锁的是两个不同的对象,所以静态的同步方法与非静态的同步方法之间是不会有竞争条件的,但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,而不是同一个实例对象的静态同步方法之间,还是不同的实例对象的静态同步方法之间,只要他们用一个的是同一个类的实例对象。