目录
第四种情况:两部手机,phone调用邮件,phone2调用SMS:
第七种:一个静态同步方法,一个普通同步方法,一部手机,打印顺序?
第八种:1个静态同步方法,一个普通同步方法,两部手机 ,打印顺序?
第一种标准访问,一个对象两个同步方法:
package com.isea.java;
class Phone{
public synchronized void sendEmail(){
System.out.println("-----Email");
}
public synchronized void sendSMS(){
System.out.println("-----SMS");
}
}
------------------------------------------------
public class Lock8 {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
new Thread(() -> {
phone.sendEmail();
},"A").start();
Thread.sleep(100);
new Thread(() -> {
phone.sendSMS();
},"B").start();
}
}
百分之九十九的情况时顺序执行,但是有百分之一的情况是先打印SMS,后打印Email,为了保证一定是A先启动,B后启动,可以在A线程和B线程之间添加,
Thread.sleep(100);
为甚这能够做到,一定是A先启动呢?上面的这个程序会创建了三个线程,程序从上到下顺序执行,执行到main的时候创建一个线程,执行到第一个New Thread的时候,创建了一个线程,start之后,进入就绪状态,这个时候主线程sleep了,会让出CPU,由于这个时候B线程还没有被创建,所以,这个时候一定是A线程先获得CPU,那么就会先执行A线程。一旦A线程获得CPU,就会加对象锁,B即便已经就绪也必须等待A线程执行完毕才能执行。
所以第一种打印顺序:
-----email
-----SMS
我们这里只有一个phone,线程A想用手机来发短信,B 想要用手机发邮件,即便这是两个不同的方法,B也必须等待A把手机用完了,B才能使用,这里AB线程竞争的不是手机里的方法,而是手机本身。
同一个时间段,有且仅有一个线程能够访问synchronized方法所在的资源类。加锁锁的不是这个方法,锁的是这个方法所在的资源类。线程之间竞争的不是资源里面的方法,而是资源本身。
第二种:新增4秒钟sleep,在发邮件:
public synchronized void sendEmail() throws Exception {
TimeUnit.SECONDS.sleep(4);
System.out.println("-----Email");
}
打印结果:
-----Email
-----SMS
A线程被创建,并就绪,mian线程sleep之后,A线程获得CPU,开始执行但是sleep了,但是sleep不会释放对象锁,在4秒钟,实际上B线程已经被创建 ,并进入了就绪状态,但是由于A线程不会释放资源,B线程就不得不等待,所以打印顺序如上。
第三种情况,A邮件,B:Hello
package com.isea.java;
import java.util.concurrent.TimeUnit;
class Phone{
public synchronized void sendEmail() throws Exception {
TimeUnit.SECONDS.sleep(4);
System.out.println("-----Email");
}
public synchronized void sendSMS(){
System.out.println("-----SMS");
}
public void getHello(){
System.out.println("-----Hello");
}
}
--------------------------------------------
public class Lock8 {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
new Thread(() -> {
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
},"A").start();
Thread.sleep(100);
new Thread(() -> {
phone.getHello();
},"B").start();
}
}
打印结果:
-----Hello
-----Email
先创建A线程,main线程sleep,B没有创建 ,A 获得CPU时间片,开始执行,Sleep4秒钟,且没有释放锁,在这期间,B线程创建并就绪,但是由于B调用的是资源类的非同步方法,所以可以直接访问(相当于是手机外壳,并不是手机里面的功能),所以打印结果如上.
第四种情况:两部手机,phone调用邮件,phone2调用SMS:
new Thread(() -> {
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
},"A").start();
--------------------------------
new Thread(() -> {
phone2.sendSMS();
},"B").start();
打印结果:
-----SMS
-----Email
main开始,创建了两个资源实例,接着A线程被创建并就绪,main休眠,A线程获得CPU的时间片,此时A线程Sleep4秒钟,释放CPU,但是不会释放锁,main醒来,B线程被创建,由于B有自己的资源实例,所以会直接执行,不会等待A线程的资源。
注意这里锁的是资源实例。
第五种:两个静态同步方法,一部手机,打印顺序?
static可以修饰方法,变量,类(静态内部类)
public static synchronized void sendEmail() throws Exception {
TimeUnit.SECONDS.sleep(4);
System.out.println("-----Email");
}
public static synchronized void sendSMS(){
System.out.println("-----SMS");
}
---------------------------------------------
new Thread(() -> {
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
},"A").start();
Thread.sleep(100);
new Thread(() -> {
phone.sendSMS();
},"B").start();
打印结果:
-----Email
-----SMS
main执行,A线程被创建,并就绪,main休息,让出CPU,A获得CPU,开始执行,sleep4秒,不释放锁,main醒来,B线程被创建,并就绪,由于资源被A锁住,所以必须等待,打印顺序如上
静态锁锁的是class对象
第六种:两个静态同步方法,两部手机,打印顺序?
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(() -> {
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
},"A").start();
Thread.sleep(100);
new Thread(() -> {
phone2.sendSMS();
},"B").start();
打印结果:
-----Email
-----SMS
静态方法锁的class对象。
第七种:一个静态同步方法,一个普通同步方法,一部手机,打印顺序?
public static synchronized void sendEmail() throws Exception {
TimeUnit.SECONDS.sleep(4);
System.out.println("-----Email");
}
public synchronized void sendSMS(){
System.out.println("-----SMS");
-----------------------------------------
new Thread(() -> {
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
},"A").start();
Thread.sleep(100);
new Thread(() -> {
phone.sendSMS();
},"B").start();
打印顺序:
-----SMS
-----Email
static锁的工厂类对象class、synchronized锁的是this对象,实例。两者是互不影响的,工厂的资源锁住了,但是这个工厂生产出来的时候依然能够使用,A线程去工厂发邮件,B使用手机实例发短信,两者不一样。
第八种:1个静态同步方法,一个普通同步方法,两部手机 ,打印顺序?
public static synchronized void sendEmail() throws Exception {
TimeUnit.SECONDS.sleep(4);
System.out.println("-----Email");
}
public synchronized void sendSMS(){
System.out.println("-----SMS");
}
---------------------------------------------------
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(() -> {
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
},"A").start();
Thread.sleep(100);
new Thread(() -> {
phone2.sendSMS();
},"B").start();
打印顺序:
-----SMS
-----Email
1234,情况,锁的都是当前对象this,即锁的是实例对象。而5678,情况中,static锁的模板。锁的是当前对象this,还是类对象class的区别。
关于线程8个锁的总结:
一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用其中的一个synchronized方法了, 其它的线程都只能等待,换句话说,某一个时刻内,只能有唯一一个线程去访问这些synchronized方法 锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法 加个普通方法后发现和同步锁无关 换成两个对象后,不是同一把锁了,情况立刻变化。 都换成静态同步方法后,情况又变化 所有的非静态同步方法用的都是同一把锁——实例对象本身, synchronized实现同步的基础:Java中的每一个对象都可以作为锁。 具体表现为以下3种形式。 对于普通同步方法,锁是当前实例对象。 对于静态同步方法,锁是当前类的Class对象。 对于同步方法块,锁是Synchonized括号里配置的对象。 当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁。 也就是说如果一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁, 可是别的实例对象的非静态同步方法因为跟该实例对象的非静态同步方法用的是不同的锁, 所以毋须等待该实例对象已获取锁的非静态同步方法释放锁就可以获取他们自己的锁。 所有的静态同步方法用的也是同一把锁——类对象本身, 这两把锁是两个不同的对象,所以静态同步方法与非静态同步方法之间是不会有竞态条件的。 但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁, 而不管是同一个实例对象的静态同步方法之间, 还是不同的实例对象的静态同步方法之间,只要它们同一个类的实例对象!