多线程--线程不安全和线程死锁,同步锁Synchronize

线程不安全和线程死锁、同步锁Synchronize

什么叫线程不安全?

当多个线程共享同一个资源时,在不使用同步锁的情况下对共享资源进行读写操作是,导致的程序执行结果异常,产生不正常的结果数据。
举个例子:两个人去抢票,当强到最后一张的时,在两个人眼里都看见有一张票,于是两个同时去抢,结果两个人都抢到了,而票的数量变成的-1。

解决线程不安全的办法?

解决线程不安全的办法就是给线程加锁,使用线程同步机制synchronize。线程同步其实就是个等待机制,多个线程访问同一个资源时,就要让这些线程进入一个等待池形成队列,等待前一个线程使用完毕,下一个线程使用。
在这里插入图片描述
排队使用机制。

Synchronize同步锁

形成机制:队列 + 锁
当一个线程获取到对象的排它锁时,其他线程必须等待,等该线程结束使用(打开锁)时才能使用。
使用同步锁容易产生的问题?

  • 线程必须要等待上一个线程结束才能使用,会导致程序效率问题。
  • 在多线程且有多个资源对象锁的使用情况下,可能会产生一个线程占用了另一个进程的待使用资源,而处于一直等待该线程释放锁的状态,如果该线程一直占用锁,那么就会产生线程死锁问题。
  • 如果一个优先级高的线程等待一个优先级底的线程释放锁,会影响性能问题(比如很多个简单线程都在等一个复杂线程处理事务结束,就会影响执行效率)。

Synchronize同步锁:同步方法 和 同步代码块
同步方法 默认锁为this(当前对象),任何对象都能成为锁。

   private synchronized  void buyTicket() {
        System.out.println(Thread.currentThread().getName() + ":我抢到了一个票" + ticketnum--);
        // 控制线程结束
        if (ticketnum <= 0) {
            falg = false;
        }
    }

同步代码块 :一般将共享资源或者需要被操作的资源对象作为锁。

private   void buyTicket() {
        synchronized (this){   // Obj 代表任何资源对象
            System.out.println(Thread.currentThread().getName() + ":我抢到了一个票" + ticketnum--);
            // 控制线程结束
            if (ticketnum <= 0) {
                falg = false;
            }
        }
    }

PS:线程sleep方法是让线程休息等待,不释放锁。wait是让线程进入等待区,释放锁。

什么是线程死锁?

线程死锁其实在上面已经提到过了。多个线程在共享一些共同资源的情况下,且拥有两个以上锁,而都在等待着对方释放锁,导致的线程处于一直等待状态。
举例:我有笔,但是我需要你的纸才能写字,而你需要我的笔才能写字。但是我们都等着对放给自己,导致的一直等待状态。
实现:

// 笔对象
class Pen{}
// 纸对象
class Paper{}

// 写字
class Write implements Runnable{
    static  Pen pen = new Pen();
    static  Paper paper = new Paper();
    int flag;
    String name;
    Write(int flag,String name){
         this.flag = flag;
         this.name = name;
    }

    @Override
    public void run() {
        try {
            writeWork();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

public  void writeWork() throws InterruptedException {
        // 当flag 0时获取笔
        if(flag==0){
             synchronized (pen){
                System.out.println(name+":拿到了笔");
                Thread.sleep(2000);
                // 然后去拿纸的锁
                 synchronized (paper){
                     System.out.println(name+":拿到了纸");
                 }
             }
        }else {
            synchronized (paper){
                System.out.println(name+":拿到了纸");
                Thread.sleep(2000);
                // 然后去拿纸的锁
                synchronized (pen){
                    System.out.println(name+":拿到了笔");
                }
            }
        }
    }
}

/**
 * Created by 一只会飞的猪 on 2021/3/8.
 * 实现线程死锁现象,写字 = 笔 + 纸
 */
public class DeadLockThread {
    public static void main(String[] args) {
        new Thread(new Write(0,"小美")).start();
        new Thread(new Write(1,"小丑")).start();
    }
}

结果:小美和小丑都拿着自己的锁,等终身。
在这里插入图片描述
怎么解决死锁呢?
解决死锁没有什么好的办法,一般都是根据业务需求,准备的对对象进行锁和释放锁处理。比如上例。
我们只要让两个人拿到一个资源之后释放锁,再去取另一个资源就行。

public  void writeWork() throws InterruptedException {
        // 当flag 0时获取笔
        if(flag==0){
             synchronized (pen){
                System.out.println(name+":拿到了笔");
                Thread.sleep(2000);
             }
            // 然后去拿纸的锁
            synchronized (paper){
                System.out.println(name+":拿到了纸");
            }
        }else {
            synchronized (paper){
                System.out.println(name+":拿到了纸");
                Thread.sleep(2000);
            }
            // 然后去拿纸的锁
            synchronized (pen){
                System.out.println(name+":拿到了笔");
            }
        }
    }

结果:两个人都能愉快。
在这里插入图片描述
PS: Synchronize同步锁(隐式同步),在实行完作用域之后,会自动释放锁。而Lock锁(下片),需要主动释放锁(显示同步)

猜你喜欢

转载自blog.csdn.net/qq_31142237/article/details/114552225