不冷博客

线程安全,同步方法和锁-11月18日讲课内容

线程安全

出现的原因

线程的抢占发生在任意时期

当多个线程操作同一个数据源的时候吗,并且都涉及到了修改,那么就有可能发生数据重复的问题,这种现象就成为线程安全的问题。

案例代码

public class Demo10 {
    public static void main(String[] args) {
        Thread thread1 = new Thread(new SellTicket(), "淘票票");
        Thread thread2 = new Thread(new SellTicket(), "猫眼");
        Thread thread3 = new Thread(new SellTicket(), "美团");

        thread1.start();
        thread2.start();
        thread3.start();

    }
}

class SellTicket implements Runnable {
    // 100张票
    private static int ticket = 100;

    @Override
    public void run() {
        // 循环卖票
        while (true) {
            // 如果当前票数不为0,就卖一张,票数-1,并睡一会儿
            if (ticket > 0) {
                System.out.println(Thread.currentThread().getName() + "卖出一张票,当前还剩下" + --ticket + "张票");

                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 如果当前票数为0,说明票卖完了,需要退出循环
            } else {
                System.out.println(Thread.currentThread().getName() + "已售罄!!");
                break;
            }
        }
    }
}

导致线程安全问题的因素

1、多个线程

2、同时操作临界资源 共享资源

如何解决线程安全的问题(加锁)

Synchronized
同步代码块
格式:
    synchronized (锁对象) {
        // 需要被同步的代码;
    }

【注意】同步代码块的锁对象可以是任意对象,但必须是同一个对象

public class Demo11 {
    public static void main(String[] args) {
        Thread thread1 = new Thread(new SellTicket1(), "淘票票");
        Thread thread2 = new Thread(new SellTicket1(), "猫眼");
        Thread thread3 = new Thread(new SellTicket1(), "美团");

        thread1.start();
        thread2.start();
        thread3.start();

    }
}

class SellTicket1 implements Runnable {

    private static int ticket = 100;

    private static final Object obj = new Object();

    @Override
    public void run() {

        while (true) {
            synchronized (obj) {
                if (ticket > 0) {
                    System.out.println(Thread.currentThread().getName() + "卖出一张票,当前还剩下" + --ticket + "张票");

                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    System.out.println(Thread.currentThread().getName() + "已售罄!!");
                    break;
                }
            }
        }
    }
}
同步方法
格式:
    synchronized 返回值 方法名(参数列表){
        // 需要被同步的代码;
    }

【注意】同步方法的锁对象是 this

public class Demo12 {
    public static void main(String[] args) {
        SellTicket2 sellTicket2 = new SellTicket2();
        Thread thread1 = new Thread(sellTicket2, "淘票票");
        Thread thread2 = new Thread(sellTicket2, "猫眼");
        Thread thread3 = new Thread(sellTicket2, "美团");

        thread1.start();
        thread2.start();
        thread3.start();

    }
}

class SellTicket2 implements Runnable {
    private static int ticket = 100;
    
    private synchronized boolean sell() {
        if (ticket > 0) {
            System.out.println(Thread.currentThread().getName() + "卖出一张票,当前还剩下" + --ticket + "张票");

            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            return true;
        } else {
            System.out.println(Thread.currentThread().getName() + "已售罄!!");
            return false;
        }
    }

    @Override
    public void run() {

        while (true) {
            
            if (!sell()) {
                break;
            }
        }
    }
}
静态同步方法
格式:
    static synchronized 返回值 方法名(参数列表){
        // 需要被同步的代码;
    }

【注意】同步静态方法的锁对象是当前类的字节码文件对象,保证唯一性

public class Demo13 {
    public static void main(String[] args) {
        
        Thread thread1 = new Thread(new SellTicket3(), "淘票票");
        Thread thread2 = new Thread(new SellTicket3(), "猫眼");
        Thread thread3 = new Thread(new SellTicket3(), "美团");

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

class SellTicket3 implements Runnable {
    private static int ticket = 100;
    
    private static synchronized boolean sell() {
        if (ticket > 0) {
            System.out.println(Thread.currentThread().getName() + "卖出一张票,当前还剩下" + --ticket + "张票");

            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            return true;
        } else {
            System.out.println(Thread.currentThread().getName() + "已售罄!!");
            return false;
        }
    }

    @Override
    public void run() {

        while (true) {
            
            if (!sell()) {
                break;
            }
        }
    }
}

【注意】

1、锁修饰的内容只能是引用类型对象,一般都是使用this,但是一定保证是同一个类对象,否则this不唯一

2、锁的位置很关键,一般都是锁定是引起线程安全的部分

3、锁会自动释放,内部代码执行完毕之后

4、多个线程必须使用同一把锁

Synchronized 用法的区别

1、同步代码块 锁的粒度可以很小

锁的粒度来讲:同步代码块 < 同步方法 < 同步静态方法

粒度 也就是锁的范围 粒度越小越好

2、同步代码块 锁的对象可以是:引用类型属性、this、字节码对象

同步方法 锁的对象只能是当前类的实例 this

3、同步静态方法 锁的对象是当前类的字节码对象

Lock

【注意】Lock锁的性能要比 synchronized 高,但是lock要求必须手动释放锁

格式:
    private final Lock lock = new ReentrantLock();

    public void method() { 
        lock.lock();  // block until condition holds
         try {
               // ... method body
         } finally {
               lock.unlock()
         }
       } 

案例代码

public class Demo14 {
    public static void main(String[] args) {
        SellTicket4 sellTicket4 = new SellTicket4();
        
        Thread thread1 = new Thread(sellTicket4, "淘票票");
        Thread thread2 = new Thread(sellTicket4, "猫眼");
        Thread thread3 = new Thread(sellTicket4, "美团");

        thread1.start();
        thread2.start();
        thread3.start();

    }
}

class SellTicket4 implements Runnable {

    private static int ticket = 100;

    private final Lock lock = new ReentrantLock();

    @Override
    public void run() {

        while (true) {
            lock.lock();
            try {
                if (ticket > 0) {
                    System.out.println(Thread.currentThread().getName() + "卖出一张票,当前还剩下" + --ticket + "张票");

                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    System.out.println(Thread.currentThread().getName() + "已售罄!!");
                    break;
                }
            } finally {
                lock.unlock();
            }
        }
    }
}

案例代码2

public class Demo15 {
    public static void main(String[] args) {
        Thread thread1 = new Thread(new SellTicket5(), "淘票票");
        Thread thread2 = new Thread(new SellTicket5(), "猫眼");
        Thread thread3 = new Thread(new SellTicket5(), "美团");

        thread1.start();
        thread2.start();
        thread3.start();

    }
}

class SellTicket5 implements Runnable {

    private static int ticket = 100;

    private static final Lock lock = new ReentrantLock();

    @Override
    public void run() {

        while (true) {
            lock.lock();
            try {
                if (ticket > 0) {
                    System.out.println(Thread.currentThread().getName() + "卖出一张票,当前还剩下" + --ticket + "张票");

                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    System.out.println(Thread.currentThread().getName() + "已售罄!!");
                    break;
                }
            } finally {
                lock.unlock();
            }

        }
    }
}

死锁

两个或两个以上的线程在争夺资源的过程中,发生的一种相互等待的现象

产生的原因

1、2个以上的线程

2、2个以上的锁

3、同步代码块嵌套同步代码块

自定义锁对象

class MyLock {
    // 第一把锁
    public static Object LOCK1 = new Object();
    // 第二把锁
    public static Object LOCK2 = new Object();
}

死锁类

public class DeadLock implements Runnable {

    boolean flag;

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

    @Override
    public void run() {
        if (flag) {
            synchronized (MyLock.LOCK1) {
                System.out.println("if LOCK1");
                synchronized (MyLock.LOCK2) {
                    System.out.println("if LOCK2");
                }
            }
        } else {
            synchronized (MyLock.LOCK2) {
                System.out.println("else LOCK2");
                synchronized (MyLock.LOCK1) {
                    System.out.println("else LOCK1");
                }
            }
        }
    };
}

测试类

public class Test {
    public static void main(String[] args) {
        Thread th1 = new Thread(new DeadLock(false));
        Thread th2 = new Thread(new DeadLock(true));

        th1.start();
        th2.start();
    }
}

当前页面是本站的「Google AMP」版。查看和发表评论请点击:完整版 »