Java基础 线程4 Synchronized同步

线程同步机制

1、在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何时刻,最多有一个线程访问,以保证数据的完整性

2、也可以这样理解 :线程同步,即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作

同步具体方法-Synchronized

1、同步代码块

synchronized(对象){ //得到对象的锁,才能操作同步代码

//需要被同步代码

}

2、synchronized还可以放在方法声明中,表示整个方法-为同步方法

public synchronized void m(String name){

//需要被同步的代码

}

3、如何 理解

就好像某小伙伴上厕所前先把门头上(上锁),完事后再出来 (解锁),那么其它小伙伴就可以使用厕所了

4、使用synchronized解决售票问题

/**
 * @ClassName SellTicket
 * @Description 使用多线程模拟三个窗口同时售票100张
 * @Author 小黄debug
 * @Date 2022/3/20 16:58
 * @Version 1.0
 **/
public class SellTicket {
    public static void main(String[] args) {
//        SellTicket01 sellTicket01 = new SellTicket01();
//        SellTicket01 sellTicket02 = new SellTicket01();
//        SellTicket01 sellTicket03 = new SellTicket01();
//
//        //出现票数超卖
//        sellTicket01.start();
//        sellTicket02.start();
//        sellTicket03.start();

        //使用接口方式查看是否存在超卖
//        SellTicker02 sellTicker02 = new SellTicker02();
//        new Thread(sellTicker02).start();
//        new Thread(sellTicker02).start();
//        new Thread(sellTicker02).start();
        SellTicker03 sellTicker03 = new SellTicker03();
        new Thread(sellTicker03).start();
        new Thread(sellTicker03).start();
        new Thread(sellTicker03).start();
    }
}
//实现接口方式,使用synchronized实现线程同步
class SellTicker03 implements Runnable{
    //为什么这里不用静态,是因为SellTicker02只new了一次,所有的都ticketNum都指向这里
    private int ticketNum = 100;   //让多个线程共享ticketNum
    private boolean loop = true;//控制 run方法的变量
    Object object = new Object();

    //同步方法(静态的)的锁为当前类本身
    //1、public synchronized static void m1(){}锁是加在SellTicket03.class
    //2、如果在静态方法中,实现一个同步代码块
    public synchronized static void m1(){
    }

    public static void m2(){
        synchronized (SellTicker03.class){

        }
    }


    //说明
    //1、public synchronized void sell(){}就是一个同步方法
    //2、这时锁在this对象
    //3、也可以在代码块上写synchronized,同步代码块,互斥锁还是在this对象
    public /*synchronized*/  void sell(){ //同步方法,在同一时刻,只能有一个线程来执行run方法
        synchronized(/*this*/object) {
            if (ticketNum <= 0) {
                System.out.println(Thread.currentThread().getName() + "售票结束 。。");
                loop = false;
                return;
            }

            System.out.println("窗口" + Thread.currentThread().getName()
                    + "售出一张票,剩余票数" + (--ticketNum));
        }
    }

    @Override
    public void run() {
        while(loop){
            sell(); //sell方法是一个同步方法
            try {   //将休眠放出来,否则会一直一个线程卖票
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}



class SellTicket01 extends Thread{
    private static int ticketNum = 100;   //让多个线程共享ticketNum

    @Override
    public void run() {
        while(true){
            if(ticketNum <= 0){
                System.out.println("售票结束 。。");
                break;
            }
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口"+Thread.currentThread().getName()
                    +"售出一张票,剩余票数"+(--ticketNum));
        }
    }
}

class SellTicker02 implements Runnable{
    //为什么这里不用静态,是因为SellTicker02只new了一次,所有的都ticketNum都指向这里
    private int ticketNum = 100;   //让多个线程共享ticketNum

    @Override
    public void run() {
        while(true){
            if(ticketNum <= 0){
                System.out.println("售票结束 。。");
                break;
            }
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口"+Thread.currentThread().getName()
                    +"售出一张票,剩余票数"+(--ticketNum));
        }
    }
}

互斥锁:synchronized又被称为互斥锁

基本介绍

1、在Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。

2、每个对象都对应于一个可称为”互斥锁"的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象

3、关键字synchronized来与对象的互斥锁联系。当某个对象用synchronized修饰时,表明该对象在任一时刻只能由一个线程访问

4、同步的局限性:导致程序的执行效率要降低

5、同步方法(非静态的)的锁可以是this,是可以是其他对象(要求是同一对象)

6、同步方法(静态的)的锁为当前类本身

注意事项和细节

1、同步方法如果没有使用static修饰:默认锁对象为this

2、如果方法使用static修饰,默认锁对象:当前类.class

3、实现落地步骤:

需要先分析上锁的代码

选择同步代码块或同步方法

要求多个线程的锁对象为同一个即可!

线程的死锁

多个线程都占用了对方的锁资源,但不肯相让,导致了死锁,在编程是一定要避免死锁的发生

应用案例

妈妈:你先完成作业,才让你玩手机

小明:你先让我玩手机,我才完成作业

/**
 * @ClassName DeadLock_
 * @Description 模拟线程死锁
 * @Author 小黄debug
 * @Date 2022/3/20 22:50
 * @Version 1.0
 **/
public class DeadLock_ {
    public static void main(String[] args) {
        //模拟一个死锁现象
        DeadLockDemo A = new DeadLockDemo(true);
        DeadLockDemo B = new DeadLockDemo(false);
        A.setName("A线程");
        B.setName("B线程");

        A.start();
        B.start();
    }

}
class DeadLockDemo extends Thread{
    static Object o1 = new Object();
    static Object o2 = new Object();
    boolean flag;

    public DeadLockDemo(boolean flag){
        this.flag = flag;
    }

    @Override
    public void run() {
        //下面业务逻辑分析
        //1。如果flag为T,线程A就会得到/持有o1对象锁,然后尝试去获取o2对象锁
        //2。如果线程A得不到o2对象锁,就会Blockecd
        //3。如果flag为F,线程B就会得到/持有o2对象锁,然后尝试去获取o1对象锁
        //4。如果线程B得不到o1对象锁,就会Blocked
        if(flag){
            synchronized (o1){
                System.out.println(Thread.currentThread().getName()+"进入了1");
                synchronized (o2){
                    System.out.println(Thread.currentThread().getName()+"进入了2");
                }
            }
        }else{
            synchronized (o2){
                System.out.println(Thread.currentThread().getName()+"进入了3");
                synchronized ((o1)){
                    System.out.println(Thread.currentThread().getName()+"进入了4");
                }
            }
        }
    }
}

如何避免,一个线程不要去拿两个锁

释放锁

下面操作会释放锁

1、当前线程的同步方法、同步代码块执行结束

        案例:上厕所,完整出来 

2、当前线程在同步代码块、同步方法中遇到break、return.

        案例:没有正常的完整,经理叫他修改bug,不得已出来 

3、当前线程在同步代码块、同步方法中出现了未处理的Erro或Exception,导致异常结束

案例:没有正常的完事,发现忘带纸,不得已出来

4、当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁

案例:没有正常完整,觉得需要酝酿下,所以出来 等会再进去

下面操作不会释放锁

1、线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行,不会释放锁

案例:上厕所,太困了,在才坑位上眯了一会

2、线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁

提示:应尽量避免使用suspend()和resume()来控制线程,方法不再推荐使用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值