synchronized 锁的是调用这个方法的对象对象,也就是同一时间段不可能有两个线程同时进到这个资源类,同一时间段,只允许有一个线程访问资源类里面的其中一个synchronized 方法!
T1:1.标准访问,请问先打印邮件还是短信?
代码:
public class Lock8 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(() -> {
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
}, "A").start();
Thread.sleep(100);
new Thread(() -> {
try {
phone.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
}, "B").start();
}
}
class Phone {
public synchronized void sendEmail() throws Exception {
System.out.println("----sendEmail");
}
public synchronized void sendSMS() throws Exception {
System.out.println("----sendSMS");
}
}
结果:不一定,看cpu的调度,被 synchronized 修饰的方式,锁的对象是方法的调用者,因为这两个方法锁的是同一个对象,所以先调用的先执行。
T2:邮件方法暂停4s,请问先打印邮件还是短信?
代码如下:
public class Lock8 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(() -> {
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
}, "A").start();
Thread.sleep(100);
new Thread(() -> {
try {
phone.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
}, "B").start();
}
}
class Phone {
public synchronized void sendEmail() throws Exception {
Thread.sleep(4000);
System.out.println("----sendEmail");
}
public synchronized void sendSMS() throws Exception {
System.out.println("----sendSMS");
}
}
结果:不一定的,与上题一样的
综合T1,T2:
结论:一个对象里面如果有多个synchronized 方法,某一时刻内,只要有一个线程去调用synchronized 方法了,那么其他的线程只能够等待,换句话说,某一时刻只能够有一个线程去访问这些synchronized方法,锁的是当前对象this,被锁定后,其他的同一个对象调用都不能进入到其他synchronized 方法
T3:新增一个普通方法hello(),请问先打印邮件还是hello? 先hello
代码如下:
public class Lock8 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(() -> {
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
}, "A").start();
Thread.sleep(100);
new Thread(() -> {
try {
// phone.sendSMS();
phone.hello();
} catch (Exception e) {
e.printStackTrace();
}
}, "B").start();
}
}
class Phone {
public synchronized void sendEmail() throws Exception {
Thread.sleep(4000);
System.out.println("----sendEmail");
}
public synchronized void sendSMS() throws Exception {
System.out.println("----sendSMS");
}
public void hello() {
System.out.println("----hello");
}
}
结果:结果是先打印hello,其次4s后再sendEmail,因为hello没有被synchronized 修饰。
结论:普通方法和同步锁无关。
T4:两部手机,请问先打印邮件还是短信?
代码如下:
public class Lock8 {
public static void main(String[] args) {
Phone phone = new Phone();
Phone phone1 = new Phone();
new Thread(() -> {
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
}, "A").start();
Thread.sleep(100);
new Thread(() -> {
try {
// phone.sendSMS();
// phone.hello();
phone1.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
}, "B").start();
}
}
class Phone {
public synchronized void sendEmail() throws Exception {
Thread.sleep(4000);
System.out.println("----sendEmail");
}
public synchronized void sendSMS() throws Exception {
System.out.println("----sendSMS");
}
public void hello() {
System.out.println("----hello");
}
}
结果:先打印的phone1的sendSMS,4s后再打印phone的sendemail,因为资源对象是不同的。
结论:两个对象,就不是同一把锁了。
T5:两个静态同步方法,同一部手机,请问先打印邮件还是短信?
public class Lock8 {
public static void main(String[] args) {
Phone phone = new Phone();
Phone phone1 = new Phone();
new Thread(() -> {
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
}, "A").start();
Thread.sleep(100);
new Thread(() -> {
try {
phone.sendSMS();
// phone.hello();
// phone1.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
}, "B").start();
}
}
class Phone {
public static synchronized void sendEmail() throws Exception {
Thread.sleep(4000);
System.out.println("----sendEmail");
}
public static synchronized void sendSMS() throws Exception {
System.out.println("----sendSMS");
}
public void hello() {
System.out.println("----hello");
}
}
结果:4s过后先打印sendEmail,接着打印sendSMS。
T6:由上一题的一部手机换成两部手机,请问先打印邮件还是短信?
代码如下:
public class Lock8 {
public static void main(String[] args) {
Phone phone = new Phone();
Phone phone1 = new Phone();
new Thread(() -> {
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
}, "A").start();
Thread.sleep(100);
new Thread(() -> {
try {
// phone.sendSMS();
// phone.hello();
phone1.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
}, "B").start();
}
}
class Phone {
public static synchronized void sendEmail() throws Exception {
Thread.sleep(4000);
System.out.println("----sendEmail");
}
public static synchronized void sendSMS() throws Exception {
System.out.println("----sendSMS");
}
public void hello() {
System.out.println("----hello");
}
}
结果:4s过后先打印sendEmail,接着打印sendSMS。
综合T5,T6:
结论:此时锁的不是对象了,而是类!即锁的是phone.class!锁的是模板,Phone phone=new Phone() 锁的就是这个Phone。因为phone和phone1都是Phone的实例对象,而锁的是Phone,所以说,当phone正在执行的时候,phone1就得等着,因为锁的是模板。
T7:1个普通同步方法,1个静态同步方法,1部手机,请问先打印邮件还是短信?
代码如下:
public class Lock8 {
public static void main(String[] args) {
Phone phone = new Phone();
// Phone phone1 = new Phone();
new Thread(() -> {
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
}, "A").start();
Thread.sleep(100);
new Thread(() -> {
try {
phone.sendSMS();
// phone.hello();
// phone1.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
}, "B").start();
}
}
class Phone {
public static synchronized void sendEmail() throws Exception {
Thread.sleep(4000);
System.out.println("----sendEmail");
}
public synchronized void sendSMS() throws Exception {
System.out.println("----sendSMS");
}
public void hello() {
System.out.println("----hello");
}
}
结果:先打印的sendSMS,接着4s后打印的sendEmail。原因:因为sendEmail锁的是模板的(加了static),sendSMS锁的是当前调用的对象但也属于Phone 类的对象,所以被静态同步方法锁住。
T8:1个普通同步方法,1个静态同步方法,2部手机,请问先打印邮件还是短信?
代码如下:
public class Lock8 {
public static void main(String[] args) {
Phone phone = new Phone();
Phone phone1 = new Phone();
new Thread(() -> {
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
}, "A").start();
new Thread(() -> {
try {
phone1.sendSMS();
// phone.hello();
// phone1.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
}, "B").start();
}
}
class Phone {
public static synchronized void sendEmail() throws Exception {
Thread.sleep(4000);
System.out.println("----sendEmail");
}
public synchronized void sendSMS() throws Exception {
System.out.println("----sendSMS");
}
public void hello() {
System.out.println("----hello");
}
}
结果:同T7一样,先打印的sendSMS,接着4s后打印的sendEmail。原因:因为sendEmail锁的是模板的(加了static),sendSMS锁的是当前调用的对象但也属于Phone 类的对象,所以被静态同步方法锁住。
总结:
synchronized实现同步基础:
java中的每一个对象都可以作为锁。
具体的表现形式有下面的三种:
1:对于普通方法而言,锁是当前实例对象(this)(当前调用该方法的对象)。
2:对于静态同步方法,锁是当前类的class对象。
3:对于同步方法块而言,锁是synchronized括号里配置的对象(当前调用该方法块的对象)。
当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁。
也就是说当一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁。如果别的实例对象的非静态同步方法和和该实例对象的非静态同步方法用的是不同的锁,那么就不用等待该实例对象已获取的锁的非静态同步方法释放锁,直接获取他们自己的锁。
对象锁和类锁(this/class)是两个不同的对象,所以静态同步方法和非静态同步方法之间是不会有竞争条件的。但一旦静态方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁。