Java-Synchronized(三)-多线程访问同步方法的七种情况

七种情况
  1. 两个线程同时访问一个对象的同步方法
  2. 两个线程访问的是两个对象的同步方法
  3. 两个线程访问的是synchronized修饰的静态方法
  4. 同时访问同步方法与非同步方法
  5. 访问一个对象的不同的普通同步方法
  6. 同时访问静态synchronized方法和非静态的synchronized方法
  7. 方法抛出异常后,会释放锁
一 | 两个线程同时访问一个对象的同步方法
  1. 两个线程t1和t2
  2. 同一个对象,它们都是Runnabl的同一个实例instance
  3. 一个对象的同步方法,被synchronizd修饰的method

结论:串行

并发与串行其实是不同的概念,在单核CPU里面,所有指令都是串行的,并发指的在某一个时间段内,多个线程执行。与串行相对应的是并行,之后用串行和并行来区别。

public class SynchronizedObjectMethod3 implements Runnable {

    static  SynchronizedObjectMethod3 instance = new SynchronizedObjectMethod3();

    public static void main(String[] args) {
        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);
        t1.start();
        t2.start();
//        t1.join();
//        t2.join();
        while(t1.isAlive() || t2.isAlive()) {}
        System.out.println("finished");
    }

    @Override
    public void run() {
        method();
    }

    public synchronized void method() {
        System.out.println("I'm object method lock " + Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("I'm object method lock " + Thread.currentThread().getName() + " is over");
    }
}
二 | 两个线程访问的是两个对象的同步方法
  1. 两个线程t1、t2
  2. 两个对象instance1和instance2

尽管同步代码块对象锁是this,但这个this在起作用是两个不同的实例,用lock1也是一样的。

结论:并行

public class SynchronizedObjectCodeBlock2 implements Runnable {

    static  SynchronizedObjectCodeBlock2 instance1 = new SynchronizedObjectCodeBlock2();
    static  SynchronizedObjectCodeBlock2 instance2 = new SynchronizedObjectCodeBlock2();

    private final Object lock1 = new Object();
    private final Object lock2 = new Object();

    public static void main(String[] args)  throws InterruptedException{
        Thread t1 = new Thread(instance1);
        Thread t2 = new Thread(instance2);
        t1.start();
        t2.start();
//        t1.join();
//        t2.join();
        while(t1.isAlive() || t2.isAlive()) {}
        System.out.println("finished");
    }

    @Override
    public void run() {
        synchronized (this) {
            System.out.println("I'm object code block lock1 " + Thread.currentThread().getName());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("I'm object code block lock1 " + Thread.currentThread().getName() + " is over");
        }

//        synchronized (lock2) {
//            System.out.println("I'm object code block lock2 " + Thread.currentThread().getName());
//            try {
//                Thread.sleep(3000);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//            System.out.println("I'm object code block lock2 " + Thread.currentThread().getName() + " is over");
//        }
    }
}
三 | 两个线程访问的是synchronized修饰的静态方法
public class SynchronizedStaticMethod4 implements Runnable {

    static  SynchronizedStaticMethod4 instance1 = new SynchronizedStaticMethod4();
    static  SynchronizedStaticMethod4 instance2 = new SynchronizedStaticMethod4();

    public static void main(String[] args) {
        Thread t1 = new Thread(instance1);
        Thread t2 = new Thread(instance2);
        t1.start();
        t2.start();
//        t1.join();
//        t2.join();
        while(t1.isAlive() || t2.isAlive()) {}
        System.out.println("finished");
    }

    @Override
    public void run() {
        method();
    }

    public static synchronized void method() {
        System.out.println("I'm object method lock " + Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("I'm object method lock " + Thread.currentThread().getName() + " is over");
    }
}

结论:串行

四 | 同时访问同步方法与非同步方法

结论当然是非同步方法不会受到影响

public class SynchronizedYesOrNo6 implements Runnable{

    static SynchronizedYesOrNo6 instance = new SynchronizedYesOrNo6();

    public static void main(String[] args) {
        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);
        t1.start();
        t2.start();
//        t1.join();
//        t2.join();
        while(t1.isAlive() || t2.isAlive()) {}
        System.out.println("finished");
    }

    @Override
    public void run() {
        if (Thread.currentThread().getName().equals("Thread-0")) {
            method1();
        } else {
            method2();
        }
    }

    public synchronized void method1() {
        System.out.println("I'm lock " + Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("I'm lock " + Thread.currentThread().getName() + " is over");
    }

    public void method2() {
        System.out.println("I'm not lock " + Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("I'm not lock " + Thread.currentThread().getName() + " is over");
    }
}

试想一下如果本该作为临界资源的变量i,除了在同步代码块内,还在普通方法的地方被修改,会发生什么呢?

public class DisappearRequest1 implements Runnable {

    static DisappearRequest1 instance = new DisappearRequest1();

    static int i = 0;

    public static void main(String[] args) throws InterruptedException{
        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(i);
    }

    @Override
    public  void run() {
        if(Thread.currentThread().getName().equals("Thread-0")) {
            method1();
        } else {
            method2();
        }
    }

    public synchronized void method1() {
        for(int j=0; j<100000; j++) {
            i++;
        }
    }

    public void method2() {
        for(int j=0; j<100000; j++) {
            i++;
        }
    }
}

i的最后结果是小于200000的…所以临界资源必须进行互斥操作。

五 | 访问一个对象的不同的普通同步方法
public class SynchronizedDifferentMethod7 implements Runnable{

    static SynchronizedDifferentMethod7 instance = new SynchronizedDifferentMethod7();

    public static void main(String[] args) {
        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);
        t1.start();
        t2.start();
//        t1.join();
//        t2.join();
        while(t1.isAlive() || t2.isAlive()) {}
        System.out.println("finished");
    }

    @Override
    public void run() {
        if (Thread.currentThread().getName().equals("Thread-0")) {
            method1();
        } else {
            method2();
        }
    }

    public synchronized void method1() {
        System.out.println("I'm lock " + Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("I'm lock " + Thread.currentThread().getName() + " is over");
    }

    public synchronized void method2() {
        System.out.println("I'm not lock " + Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("I'm not lock " + Thread.currentThread().getName() + " is over");
    }

}

结论串行:其实相当于是synchronized锁了同一个this对象

六 | 同时访问静态synchronized方法和非静态的synchronized方法

预测:并行

public class SynchronizedStaticAndNormal8 implements Runnable{
    static SynchronizedStaticAndNormal8 instance = new SynchronizedStaticAndNormal8();

    public static void main(String[] args) {
        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);
        t1.start();
        t2.start();
//        t1.join();
//        t2.join();
        while(t1.isAlive() || t2.isAlive()) {}
        System.out.println("finished");
    }

    @Override
    public void run() {
        if (Thread.currentThread().getName().equals("Thread-0")) {
            method1();
        } else {
            method2();
        }
    }

    public static synchronized void method1() {
        System.out.println("I'm static lock " + Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("I'm static lock " + Thread.currentThread().getName() + " is over");
    }

    public synchronized void method2() {
        System.out.println("I'm not static " + Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("I'm not static " + Thread.currentThread().getName() + " is over");
    }
}

结论:并行

原因:static加锁的对象是该类的类类型,而普通方法锁加锁的对象是this,这显然是两把不同的锁。

章七 | 方法抛出异常后,会释放锁

要使用RuntimeException来抛出异常

public class SynchronizedException9 implements Runnable {
    static SynchronizedException9 instance = new SynchronizedException9();

    public static void main(String[] args) {
        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);
        t1.start();
        t2.start();
//        t1.join();
//        t2.join();
        while(t1.isAlive() || t2.isAlive()) {}
        System.out.println("finished");
    }

    @Override
    public void run() {
        if (Thread.currentThread().getName().equals("Thread-0")) {
            method1();
        } else {
            method2();
        }
    }

    public  synchronized void method1() {
        System.out.println("I'm static lock " + Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
//            throw new Exception();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
//        catch (Exception e) {
//            e.printStackTrace();
//        }
        throw new RuntimeException();
//        System.out.println("I'm static lock " + Thread.currentThread().getName() + " is over");
    }

    public synchronized void method2() {
        System.out.println("I'm not static " + Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("I'm not static " + Thread.currentThread().getName() + " is over");
    }
}
总结
  1. 一把锁只能被一个线程所获取,其他线程只能等待
  2. 每个实例都对应有自己的一把锁,类锁就是所有实例的类类型的锁,有同一个类派生而来的实例自然只有一个类类型
  3. 无论发放还是正常执行完毕还是抛出异常,synchronized修饰的都将释放锁
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值