多线程—线程同步机制(并发)

线程同步机制(并发)

1.同一个对象被多个线程操纵

2.处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改该对象,这时就需要线程同步。线程同步实际上就是一种等待机制,多个需要同时访问该对象的线程进入这个对象的线程池形成队列,等待前面线程执行完毕,下一个线程再继续使用。

3.在对象线程池里存在着队列和锁,队列则是等待操作该对象的线程队伍,锁则是当该对象在被线程操作时,其他线程无法操作该对象,锁机制synchronized,所以当线程获得对象的排他锁,独占资源,使用后释放即可;其他线程必须等待,但是会存在以下问题:

​ 3.1一个线程持有锁会导致其他需要此锁的线程挂起。

​ 3.2多个线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时,引起性能问题。

​ 3.3若优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题。

同步方法

1.synchronized方法(public synchronized void method (int args){})

就是在将买票的方法buy,加上关键字synchronized

//以买火车票为例子

//多个线程操控同一个资源的情况下,线程不安全,数据紊乱
public class ManyOpOne implements Runnable{

    //票数
    private int ticketNum = 5;
    boolean flag = true;

    //synchronized 同步方法,锁的是this,也就是当前对象。
    public synchronized void buy(){
        //判断是否有票
        if(ticketNum <= 0){
            flag = false;
            return;
        }
        //买票
        System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNum-- + "票");//获取线程名字,知道是哪个线程拿的票
    }

    @Override
    public void run() {
        while(flag){
           buy();
        }
    }

    public static void main(String[] args) {
        ManyOpOne manyOpOne1 = new ManyOpOne();

        //每个线程操作的是同一个ManyOpOne对象,所以其中成员变量ticketNum是共用的,但是每个线程的Run方法是独立的,可通过共同类对象的成员变量来控制不同的线程
        new Thread(manyOpOne1,"小明").start();
        new Thread(manyOpOne1,"小李").start();
        new Thread(manyOpOne1,"小张").start();
    }
}

不加同步方法的运行结果:当三给线程去买票的时候,都是5张票,从而造成混乱

多线程—线程同步机制(并发)_第1张图片

加同步方法的运行结果:队列+锁,一个线程一个线程的来

多线程—线程同步机制(并发)_第2张图片

2.synchronized方法控制对象的访问:

synchronized方法都必须调用该方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就独占该锁,直到该方法返回才释放锁,后面的线程才可以活得该锁,继续执行。

同步块用法:1.synchronized(Obj){}。

​ 2.Obj可以是如何对象,但是推荐使用共享资源作为同步监视器,当一个线程访问同步监视器,执行其中代码,则同步监视器将被锁定,无法被其他线程访问。

​ 3.同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象本身,或者是该class。

以两个人去银行取钱为例,两个人同时去到银行,同时操作账户,则会发生两个人看到的余额都是一样的,操作时也会操作一样的账户余额,所以synchronized ()中所要锁的则是账户这个对象,而不是方法。(如果第一种方法的案例要用同步块,则在run方法下面将this对象作为同步监视器对象即可)

//案例:两个人去银行取钱
public class ManyOpOne02 {
    public static void main(String[] args) {
        //账户
        Accout account = new Accout(1000, "零花钱");

        Drawing you = new Drawing(account, 50, "本人");
        Drawing lover = new Drawing(account, 100, "你对象");

        you.start();
        lover.start();
    }
}

class Drawing extends  Thread{
    Accout account;//账户
    //取了多少钱
    int drawingMoney ;
    //现在手里有多少钱
    int nowmoney;

    public Drawing(Accout 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(account.name + "钱不够");
                return;
            }

            //卡内的钱 = 余额 - 取出的钱
            account.money = account.money - drawingMoney;
            //手里的钱 = 原来手里有的钱 + 取出的钱
            nowmoney = nowmoney + drawingMoney;

            //Thread.currentThread().getName() = this.getName()
            System.out.println(this.getName() + "手里的钱" + nowmoney);
            System.out.println(account.name + "余额为" + account.money);
        }
    }
}

class Accout{
    int money;//金额
    String name;//名字

    public Accout(int money,String name ) {
        this.money = money;
        this.name = name;
    }
}

不加synchronized块的运行结果:由于是两个线程一起操作该对象,则顺序紊乱

在这里插入图片描述

加synchronized块的运行结果:一个线程一个线程的对对象进行操作:

在这里插入图片描述

JUC安全类型集合

由于普通ArrayList存在线程安全问题,需要用同步块去解决,则JDK内部有安全类型集合CopyOnWriteArrayList

import java.util.concurrent.CopyOnWriteArrayList;
//测试安全集合JUC
public class TestJUC {
    public static void main(String[] args) {

        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
        //添加10000个线程到该集合
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }

        //延长时间让线程执行完毕
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(list.size());
    }
}

运行结果则是10000;如果是ArrayList的话,运行结果不到10000。

你可能感兴趣的:(多线程,java,开发语言,后端)