【JavaEE初阶】之 多线程基础【下篇】

✨✨hello,愿意点进来的小伙伴们,你们好呐!
系列专栏:【JavaEE】
本篇内容:带你从0到1了解多线程基础
作者简介:一名现大二的三非编程小白,日复一日,仍需努力。

  • 线程的同步:
    • Synchronized对象锁:
      • 理解:
    • 线程的死锁:
    • 释放锁的情况:

线程的同步:

在多线程的编程中,一些比较敏感的数据不允许被多个线程同步访问,因此我们就要用同步访问技术来保证每一个数据在任何时刻,最多只有一个线程可以访问,来保证数据的安全。也就是 在每一时刻只有一个线程对该数据进行操作,其他线程只能等该线程访问完后再来访问。

Synchronized对象锁:

Synchronized可以用在同步代码块和同步方法中。
1.同步代码块:

synchronized (this) {
 
}

2.同步方法:

 public synchronized void run() {
 
}

理解:

同步代码块要如何理解呢?
就好像某个人要去排队核酸一样,当有人在核酸的时候,其他所有人都要等这个人核酸后(解锁)才有机会去核酸(抢锁)

我们来用Synchronized来解决售票问题体会一下同步锁
在这里插入图片描述

1.未使用同步锁

public class test {
    public static void main(String[] args) {
        SellTicket1 ticket = new SellTicket1();
        Thread thread1 = new Thread(ticket);
        Thread thread2 = new Thread(ticket);
        Thread thread3 = new Thread(ticket);
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

class SellTicket1 implements Runnable{

    private static int count = 100;
    @Override
    public  void run() {
        while(true){

            if(count < 0){
                System.out.println("售票结束");
                break;
            }
            System.out.println(Thread.currentThread().getName()
                    + "售出一张票,剩余票数 : " + count--);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }

}

【JavaEE初阶】之 多线程基础【下篇】_第1张图片

我们会发现在售票的时候会出现超卖的现象,有不同线程售同一张票的情况。这时候我们就要使用Synchronized锁来对售票系统进行同步。

=2.使用同步锁==

public class test {
    public static void main(String[] args) {
        SellTicket1 ticket = new SellTicket1();
        Thread thread1 = new Thread(ticket);
        Thread thread2 = new Thread(ticket);
        Thread thread3 = new Thread(ticket);
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

class SellTicket1 implements Runnable{

    private static int count = 100;
    @Override
    public void run() {
        while(true){
            synchronized (this) {
                if(count < 0){
                    System.out.println("售票结束");
                    break;
                }
                System.out.println(Thread.currentThread().getName()
                        + "售出一张票,剩余票数 : " + count--);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

【JavaEE初阶】之 多线程基础【下篇】_第2张图片

当适用synchronized 后就不在再会有超卖的现象了。

接下来我们来了解synchronized 的一些细节

1.Java语言中,引入了互斥锁来保证数据操作的完整性。
2.每一个对象都对应一个互斥锁的标记,这个标记再保证某一时刻,只能有一个线程访问该对象。
3.关键字synchronized来与对象的互斥锁联系,当某个对象用synchronized修饰时,表面该对象在任何一时刻只能有一个线程来访问。
4.同步会让程序的执行效率降低。
5.同步方法(非静态的)锁可以是用this,也是可以是其他对象(但是必须是同一个对象)。
6.同步方法(静态的)的锁为当前类本身。
7.如果同步方法使用start,默认锁对象是当前类.class

线程的死锁:

当我们synchronized使用不当的时候,就有可能会发生一个情况— 线程死锁
就相对于:
妈妈:你先玩写作业,然后再玩手机。
小明:你先让我玩手机,然后再写作业 。
这样子就会一直卡住,然后陷入其中,无法脱离。

下面我们来用代码来解释一下死锁现象:(代码有详细解释)

public class test2 {
    public static void main(String[] args) {
        //模拟死锁现象
        DeadLockDemo A = new DeadLockDemo(true);
        A.setName("A 线程");
        DeadLockDemo B = new DeadLockDemo(false);
        B.setName("B 线程");
        A.start();
        B.start();
    }
}

class DeadLockDemo extends Thread{
    static Object o1 = new Object();// 保证多线程,共享一个对象,这里使用 static
    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 对象锁,就会 Blocked
//3. 如果 flag 为 F, 线程 B 就会先得到/持有 o2 对象锁, 然后尝试去获取 o1 对象锁

//4. 如果线程 B 得不到 o1 对象锁,就会 Blocked
        if (flag) {
            synchronized (o1) {//对象互斥锁, 下面就是同步代码

                System.out.println(Thread.currentThread().getName() + " 进入 1");
                synchronized (o2) { // 这里获得 li 对象的监视权

                    System.out.println(Thread.currentThread().getName() + " 进入 2");
                }
            }
        } else {
            synchronized (o2) {
                System.out.println(Thread.currentThread().getName() + " 进入 3");
                synchronized (o1) { // 这里获得 li 对象的监视权

                    System.out.println(Thread.currentThread().getName() + " 进入 4");
                }
            }
        }
    }
}


释放锁的情况:

会释放锁的操作:

1.同步代码块执行结束
2. 同步方法中遇到break,return
3.同步方法中遇到了Error或者Exception
4. 同步方法中执行了线程对象的wait(),当前线程暂停,并释放锁。

不会释放锁的操作:

1.在同步代码块中调用,Thread.sleep(),只会暂停当前的线程,不会释放锁。
2. 其他线程调用了suspend()后会将线程挂起。也不会释放锁。

你可能感兴趣的:(JavaEE初阶,java-ee,java,面试)