多线程与高并发3

本文探讨了Java并发编程中的关键概念,包括同步方法与非同步方法的调用顺序,脏读问题的解释,以及synchronized关键字的使用,如可重入锁和异常处理。示例代码展示了不同场景下的并发行为,强调了同步控制在避免数据不一致性和提高并发性能中的作用。同时,分析了synchronized的锁升级机制,从偏向锁到自旋锁再到重量级锁的过程。
摘要由CSDN通过智能技术生成

1、同步方法和非同步方法的先后调用顺序

同步方法和非同步方法可不可以同时调用?

package com.example.demo.gc7;

public class T {
    public synchronized void m1(){
        System.out.println(Thread.currentThread().getName()+"m1 start");

        try {
            Thread.sleep(10000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"m1 end");

    }
    public void m2(){
        try{
            Thread.sleep(10000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"m2");
    }

    public static void main(String[] args) {
        T t = new T();
       /* new Thread(() ->
            t.m2(),"t2"
        ).start();

        new Thread(() ->
            t.m1(),"t1").start();*/

       new Thread(t :: m1,"t1").start();
       new Thread(t :: m2,"t2").start();
    }
}

在这里插入图片描述
答案肯定是可以的。因为在访问m1同步方法时候。加锁。但是我想访问m2的时候并不需要加锁。所以可以同时访问

t :: m1 等同于t.m1();

2、dirtyRead脏读问题

其实就是。加锁的方法和未加锁方法同时运行。也就是上面的同步方法和非同步方法的同时调用

####面试题:
如果有这么一种场景:一个账户。一个姓名和余额。写方法给它设置金额。读方法通过姓名读取余额、这种业务场景会不会出现问题?

package com.example.demo.gc8;

import java.util.concurrent.TimeUnit;

public class Account {
    String name;
    double balance;

    public synchronized void set(String name,double balance){
        try {
            Thread.sleep(2000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        this.balance = balance;
    }
    public /*synchronized*/ double getBalance(String name){
        return this.balance;
    }

    public static void main(String[] args) {
        Account a = new Account();
        new Thread(() -> a.set("zhangsan",100.0)).start();
        try{
            TimeUnit.SECONDS.sleep(2);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        System.out.println(a.getBalance("zhangsan"));
    }
}


在这里插入图片描述

(1)看你业务需求。set方法是同步方法。当线程睡眠时候。如果下面getbalace方法不是同步的。将会出现脏读情况。如果业务允许。则可以不加synchronized

(2)两个同步方法不会出现脏读情况。但是加锁以后效率低下。

3、synchronized可重入锁

可重入锁发生在两个同步方法中

一个线程。两个同步方法。可以同时访问。这就是synchronized可重入锁
保证同一线程,才能获得锁资源

package com.example.demo.gc9;

import java.util.concurrent.TimeUnit;

public class T {
    // 可重入锁
    synchronized void m1(){
        System.out.println("m1 start");
        try{
            TimeUnit.SECONDS.sleep(1);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        m2();
        System.out.println("m1 end");
    }

    synchronized void m2() {
        try{
            TimeUnit.SECONDS.sleep(2);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        System.out.println("m2");
    }

    public static void main(String[] args) {
        new T().m1();
    }

}

在这里插入图片描述

模拟父类子类
子类重写父类同步方法。也是加可重入锁

package com.example.demo.gc10;

import java.util.concurrent.TimeUnit;

public class T {
    synchronized void m(){
        System.out.println("m start");
        try {
            TimeUnit.SECONDS.sleep(1);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        System.out.println("m end");
    }

    public static void main(String[] args) {
        new TT().m();
    }


}
class TT extends T{
    @Override
    synchronized void m(){
        System.out.println("child m start");
        super.m();
        System.out.println("child m end");
    }
}

在这里插入图片描述

4、synchronized异常锁

异常锁就是指定某种条件下。。。被想拿到锁资源的线程乱冲进来。发生的异常

package com.example.demo.gc11;

import java.util.concurrent.TimeUnit;

public class T {
    int count = 0;
    synchronized void m(){
        System.out.println(Thread.currentThread().getName()+"start");
        while (true){
            count++;
            System.out.println(Thread.currentThread().getName()+"count="+count);
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (count == 5){
                int i = 1/0; //此处自定义算数异常
                System.out.println(i);
            }
        }
    }

    public static void main(String[] args) {
        T t =new T();
        Runnable r = new Runnable() {
            @Override
            public void run() {
                t.m();
            }
        };
        new Thread(r,"t1").start();
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(r,"t2").start();
    }
}

在这里插入图片描述

###发生异常后。另外线程抢到锁资源的一种过程

5、synchronized底层

synchronized底层就是锁升级。

何为锁升级。能用轻量级锁。则用轻量级锁。锁不住,则往上用

一个线程访问,并没有对对象加锁时候。只是记录这个线程的id:偏向锁
偏向锁如果有线程争用。则升级为自旋锁

自旋锁。旋十次以后升级为重量级锁

参考资料
synchronized

执行时间短。线程少。用自旋锁。
执行时间长。用系统锁

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值