多线程与并发 - 多线程访问同步方法的7种情况

2021/9/15 6:06:38

本文主要是介绍多线程与并发 - 多线程访问同步方法的7种情况,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

不啰嗦,我们直接开始!

1、两个线程同时访问一个对象的同步方法

代码:

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

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

    public synchronized void method() {
        System.out.println("对象锁的方法修饰符形式,我叫:" + Thread.currentThread().getName());
        try {
            System.out.println(Thread.currentThread().getName() + " 休眠3秒");
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "运行结束");

    }

    public static void main(String[] args) {
        //两个线程访问一个对象的同步方法,对象为instance实例
        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);
        t1.start();
        t2.start();
        while (t1.isAlive() || t2.isAlive()) {

        }
        System.out.println("fininshed");
    }
}

运行结果:

对象锁的方法修饰符形式,我叫:Thread-0
Thread-0 休眠3秒
Thread-0运行结束
对象锁的方法修饰符形式,我叫:Thread-1
Thread-1 休眠3秒
Thread-1运行结束
fininshed

解析:需要争抢同一把锁this,所以顺序执行。

2、两个线程访问的是两个对象的同步方法

代码:

public class SynchronizedTwoThreadToTwoObject implements Runnable {
    static SynchronizedTwoThreadToTwoObject instance1 = new SynchronizedTwoThreadToTwoObject();
    static SynchronizedTwoThreadToTwoObject instance2 = new SynchronizedTwoThreadToTwoObject();

    @Override
    public void run() {
        // 当前对象作为锁
        synchronized (this) {
            System.out.println("我是对象锁的代码块形式,我叫" + Thread.currentThread().getName());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "运行结束");
        }
    }

    public static void main(String[] args) {
        //两个线程访问的是两个对象的同步方法,两个对象一个为instance1,另一个为instance2
        Thread t1 = new Thread(instance1);
        Thread t2 = new Thread(instance2);
        t1.start();
        t2.start();
        while (t1.isAlive() || t2.isAlive()) {

        }
        System.out.println("fininshed");
    }
}

运行结果:

我是对象锁的代码块形式,我叫Thread-0
我是对象锁的代码块形式,我叫Thread-1
Thread-1运行结束
Thread-0运行结束
fininshed

解析:并行处理,不受干扰,锁的实例不是同一个。

3、两个线程访问的是Synchronized的静态方法

代码:

public class SynchronizedClassStatic implements Runnable {
    static SynchronizedClassStatic instance1=new SynchronizedClassStatic();
    static SynchronizedClassStatic instance2=new SynchronizedClassStatic();

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

    public static synchronized void method(){
        System.out.println("我是类锁的一种形式,我叫"+Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"线程结束");
    }
    
    public static void main(String[] args){
        //两个线程访问synchronized的静态方法
        Thread t1=new Thread(instance1);
        Thread t2=new Thread(instance2);
        t1.start();
        t2.start();
        while (t1.isAlive()||t2.isAlive()){
        }
        System.out.println("fininshed");
    }
}

运行结果:

我是类锁的一种形式,我叫Thread-0
Thread-0线程结束
我是类锁的一种形式,我叫Thread-1
Thread-1线程结束
fininshed

解析:对应的锁是同一把,一个一个的顺序执行。

4、同时访问同步方法与非同步方法

代码:

public class SynchronizedYesOrNo implements Runnable {
    static SynchronizedYesOrNo instance=new SynchronizedYesOrNo();
    public static void main(String[] args) {
        Thread th1=new Thread(instance);
        Thread th2=new Thread(instance);
        th1.start();
        th2.start();
        while(th1.isAlive()||th2.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("我是加了同步的方法"+Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"结束");
    }

    public synchronized static void method2(){
        System.out.println("我是没加了同步的方法"+Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"结束");

    }
}

运行结果:

我是加了同步的方法Thread-0
我是没加了同步的方法Thread-1
Thread-0结束
Thread-1结束
finished

解析:同步方法不会出现并发问题,非同步方法不会受到影响,出现并发问题。

5、访问同一个对象的不同的普通同步方法

代码:

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

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

    public synchronized  void method1(){
        System.out.println("我是加锁的方法"+Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"线程执行结束");
    }

    public synchronized void method2(){
        System.out.println("我也是加锁的方法"+Thread.currentThread().getName()+"线程执行结束");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"线程执行结束");
    }

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

运行结果:

我是加锁的方法Thread-0
Thread-0线程执行结束
我也是加锁的方法Thread-1线程执行结束
Thread-1线程执行结束
fininshed

解析:拿到的是this锁,所以还是会受影响,串行执行。

6、同时访问静态synchronized和非静态synchronized

代码:

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

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

    }

    public synchronized static void method1(){
        System.out.println("我是加锁的静态方法1 "+Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"线程执行结束");
    }

    public synchronized void method2(){
        System.out.println("我是加锁的非静态方法2 "+Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"线程执行结束");
    }

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

运行结果:

我是加锁的静态方法1 Thread-0
我是加锁的非静态方法2 Thread-1
Thread-1线程执行结束
Thread-0线程执行结束
fininshed

解析:method1锁的是.class对象,method2锁的是this对象,锁不一样,没有冲突,并行执行。

7、方法抛异常后,会释放锁

代码:

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

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

    }

    public synchronized void method1() {
        /*
        // 抛出Exception
        System.out.println("我是加锁的静态方法1" + Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
            //异常抛出后,JVM会自动帮你释放锁,不需要自己手动释放锁。
            throw new Exception();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "线程执行结束");
        */

        // 抛出RuntimeException
        System.out.println("我是加锁的静态方法1" + Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        //异常抛出后,JVM会自动帮你释放锁,不需要自己手动释放锁。
        throw new RuntimeException();
        // System.out.println(Thread.currentThread().getName() + "线程执行结束");
    }

    public synchronized void method2() {
        System.out.println("我也是加锁的非静态方法2" + Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "线程执行结束");
    }

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

运行结果:

我是加锁的静态方法1Thread-0
Exception in thread "Thread-0" java.lang.RuntimeException
	at com.interview.javabasic.thread.a0914.SynchronizedException.method1(SynchronizedException.java:50)
	at com.interview.javabasic.thread.a0914.SynchronizedException.run(SynchronizedException.java:16)
	at java.lang.Thread.run(Thread.java:748)
我也是加锁的非静态方法2Thread-1
Thread-1线程执行结束
fininshed

解析:方法抛出异常后,会释放锁。

总结:

  1. 一把锁只能同时被一个线程获取,没有拿到锁的线程必须等待(对应第1、5种情况)
  2. 每一个实例都对应有自己的一把锁,不同实例之间互不影响;例外:锁对象是 *.class 以及synchronized修饰的是static方法的时候,所有对象公用一把类锁(对应第2、3、4、6种情况)
  3. 无论方法正常执行完毕或者方法抛出异常,都会释放锁(对应第7种情况)。
  4. 目前进入被synchronized 的方法,在这方法里面调用没有synchronized修饰的方法。还是线程安全的吗?(不是线程安全的,出了synchronized,进入没有被synchronized修饰的方法,就可以同时被多个线程调用。)

参考文章:

  • (Synchronized)多线程访问同步方法的七种具体情况
  • Java 多线程访问同步方法的七种情况

不啰嗦,文章结束,建议三连!



这篇关于多线程与并发 - 多线程访问同步方法的7种情况的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程