java多线程:线程同步

线程同步

  • 并发:同一个对象被多个线程同时操作
  • 线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用
  • 线程同步形成条件:队列+锁
  • 锁机制synchronized:当一个线程获得对象的排它锁独占资源,其他线程必须等待,使用后释放锁即可
  • 锁机制存在以下问题:
    • 一个线程持有锁会导致其他所有需要此锁的线程挂起
    • 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换 和 调度延时,引起性能问题
    • 如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题

三大不安全案例

  • 不安全买票

    package com.wu.多线程.线程同步;
    //不安全的买票
    public class UnsafeBuyTicket {
        public static void main(String[] args) {
            BuyTicket station = new BuyTicket();
            new Thread(station,"aa").start();
            new Thread(station,"bb").start();
            new Thread(station,"cc").start();
        }
    }
    
    class BuyTicket implements Runnable {
        private int ticketNums=10;
        private boolean flag=true;//外部停止方式
        @Override
        public void run() {
            while (flag) {
                try {
                    buy();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        private void  buy() throws InterruptedException {
            if (ticketNums <= 0) {
                flag=false;
                return;
            }
            //模拟延时
            Thread.sleep(100);
            //买票
            System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
        }
    }
    
  • 不安全地取钱

    //不安全的取钱
    //两个人去银行取钱
    public class UnsafeBank {
        public static void main(String[] args) {
            //账户
            Account account = new Account(100, "aa");
            Drawing you = new Drawing(account, 50, "你");
            Drawing she = new Drawing(account, 100, "她");
    
            you.start();
            she.start();
        }
    }
    //账户
    class Account{
        int money;//余额
        String name;//卡名
    
        public Account(int money, String name) {
            this.money = money;
            this.name = name;
        }
    }
    //银行 模拟取款
    class Drawing extends Thread{
        Account account;
        int drawingMoney;
        int nowMoney;
    
        public Drawing(Account account, int drawingMoney, String name) {
            super(name);
            this.account = account;
            this.drawingMoney = drawingMoney;
    
        }
    
        @Override
        public void run() {
            if (account.money - drawingMoney < 0) {
                System.out.println(Thread.currentThread().getName()+"钱不够,取不了");
                return;
            }
            //sleep可以放大问题的发生性
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            account.money-=drawingMoney;
            nowMoney+=drawingMoney;
            System.out.println(account.name+"账户余额:"+account.money);
            System.out.println(this.getName()+"手里的钱:"+nowMoney);
        }
    }
    //aa账户余额:50
    //你手里的钱:50
    //aa账户余额:-50
    //她手里的钱:100
    
  • 线程不安全的集合

    package com.wu.多线程.线程同步;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class UnsafeList {
        public static void main(String[] args) {
            List<String> list = new ArrayList<String>();
            for (int i = 0; i < 10000; i++) {
                new Thread(()->{
                    list.add(Thread.currentThread().getName());
                }).start();
            }
            System.out.println(list.size());//9963
        }
    }
    

同步方法及同步块

  • 针对方法提出一套机制:synchronized 关键字,包括两种方法

    • synchronized 方法 (默认锁的是this
    • synchronized 块 (可以锁任何对象)
  • synchronized方法控制对象的访问,每个对象对应一把,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就独占该锁,直到该方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行

  • 缺陷:若将一个大的方法申明为synchronized将会影响效率

  • 方法里面需要修改的内容才需要锁,锁的太多,浪费资源


  • 同步块:synchronized(Obj){}

  • Obj:同步监视器

    • Obj可以是任何对象,但是推荐使用共享资源作为同步监视器
    • 同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象本身,或者是class
  • 同步监视器的执行过程

    1. 第一个线程访问,锁定同步监视器,执行其中代码
    2. 第二个线程访问,发现同步监视器被锁定,无法访问
    3. 第一个线程访问完毕,解锁同步监视器
    4. 第二个线程访问,发现同步监视器没有锁,然后锁定并访问
  • 之前的三个例子:

    //不安全的买票 加锁
    public class UnsafeBuyTicket {
        public static void main(String[] args) {
            BuyTicket station = new BuyTicket();
    
            new Thread(station,"aa").start();
            new Thread(station,"bb").start();
            new Thread(station,"cc").start();
        }
    }
    
    class BuyTicket implements Runnable {
        private int ticketNums=10;
        private boolean flag=true;//外部停止方式
        @Override
        public void run() {
            while (flag) {
                try {
                    //模拟延时
                    Thread.sleep(1000);
                    buy();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
        }
        //synchronized 同步方法,锁的是this
        private synchronized void buy(){
            if (ticketNums <= 0) {
                flag=false;
                return;
            }
    
            //买票
            System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
        }
    }
    
    
    //两个人去银行取钱
    public class UnsafeBank {
        public static void main(String[] args) {
            //账户
            Account account = new Account(100, "aa");
            Drawing you = new Drawing(account, 50, "你");
            Drawing she = new Drawing(account, 100, "她");
    
            you.start();
            she.start();
        }
    }
    //账户
    class Account{
        int money;//余额
        String name;//卡名
    
        public Account(int money, String name) {
            this.money = money;
            this.name = name;
        }
    }
    //银行 模拟取款
    class Drawing extends Thread{
        Account account;
        int drawingMoney;
        int nowMoney;
    
        public Drawing(Account account, int drawingMoney, String name) {
            super(name);
            this.account = account;
            this.drawingMoney = drawingMoney;
    
        }
    
        @Override
        public void run() {
            //锁的对象就是变化的量,就是增删改的对象
            synchronized (account){
                if (account.money - drawingMoney < 0) {
                    System.out.println(Thread.currentThread().getName()+"钱不够,取不了");
                    return;
                }
                //sleep可以放大问题的发生性
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                account.money-=drawingMoney;
                nowMoney+=drawingMoney;
                System.out.println(account.name+"账户余额:"+account.money);
                System.out.println(this.getName()+"手里的钱:"+nowMoney);
            }
    
        }
    }
    //aa账户余额:50
    //你手里的钱:50
    //她钱不够,取不了
    
    public class UnsafeList {
        public static void main(String[] args) {
    
            List<String> list = new ArrayList<String>();
            for (int i = 0; i < 10000; i++) {
                new Thread(()->{
                    synchronized (list) {
                        list.add(Thread.currentThread().getName());
                    }
    
                }).start();
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(list.size());//10000
        }
    }
    
    

测试JUC安全类型的集合

import java.util.concurrent.CopyOnWriteArrayList;
//测试JUC安全类型的集合
public class TestJUC {
    public static void main(String[] args) {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());//10000
    }
}
posted @ 2021-01-21 22:32  迪迦是真的  阅读(95)  评论(0)    收藏  举报
//复制代码按钮 //代码行号 //评论