Android多线程开发核心知识点

什么是线程并发安全

  • 线程安全的本质是能够让并发线程,有序的运行(这个有序可能是先来后到的排队,有可能有人插队,但是不管怎么着,同一时刻只能一个线程有权访问同步资源),线程执行的结果,能够对其他线程可见。

线程安全的几种分类

  • synchronized关键字
  • ReentrantLock锁
  • AtomicInteger…原子类

在这里插入图片描述

  • 锁适合写操作多的场景,先加锁可以保证写操作时数据正确。
  • 原子类适合读操作多的场景,不加锁的特点能够使其读操作的性能大幅提升。

如何保证线程安全

  • AtomicInteger原子包装类,CAS实现无锁数据更新。自旋的设计有效避免线程因阻塞-唤醒带来的系统资源开销。自旋其实就是一个do-while循环。
  • 适用场景:多线程计数,原子操作,并发数量小的场景。

在这里插入图片描述
在这里插入图片描述

  • synchronized
    锁java对象,锁class对象,锁代码块

    • 锁方法。加在方法上,未获取到对象锁的其他线程都不可以访问该方法。
      在这里插入图片描述

    • 锁class对象。加在static方法上相当于给class对象加锁,哪怕是不同的java对象实例,也需要排队执行。
      在这里插入图片描述

    • 锁代码块。未获取到对象锁的其他线程可以执行同步块之外的代码。
      在这里插入图片描述
      在这里插入图片描述

  • ReentrantLock 悲观锁,可重入锁,公平锁,非公平锁

    1. 基本用法
package com.example.myapplication.demo;

import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockDemo {

    static class ReentrantLockTask{
        ReentrantLock reentrantLock = new ReentrantLock();

        void buyTicket(){
            String name= Thread.currentThread().getName();
            try {
                reentrantLock.lock();
                System.out.println(name+":准备好了");
                Thread.sleep(100);
                System.out.println(name+":买好了");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                reentrantLock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        final ReentrantLockTask task = new ReentrantLockTask();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                task.buyTicket();
            }
        };

        for (int i = 0; i < 10; i++) {
            new Thread(runnable).start();
        }
    }
}

//执行结果:
/*
Thread-2:准备好了
Thread-2:买好了
Thread-3:准备好了
Thread-3:买好了
Thread-4:准备好了
Thread-4:买好了
Thread-5:准备好了
Thread-5:买好了
Thread-6:准备好了
Thread-6:买好了
Thread-7:准备好了
Thread-7:买好了
Thread-8:准备好了
Thread-8:买好了
Thread-9:准备好了
Thread-9:买好了
Thread-10:准备好了
Thread-10:买好了
Thread-11:准备好了
Thread-11:买好了
*/
  1. 可重入,避免死锁
package com.example.myapplication.demo;

import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockDemo {

    static class ReentrantLockTask{
        ReentrantLock reentrantLock = new ReentrantLock();

        void buyTicket(){
            String name= Thread.currentThread().getName();
            try {
                reentrantLock.lock();
                System.out.println(name+":准备好了");
                Thread.sleep(100);
                System.out.println(name+":买好了");
                reentrantLock.lock();
                System.out.println(name+":又准备好了");
                Thread.sleep(100);
                System.out.println(name+":又买好了");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                reentrantLock.unlock();
                reentrantLock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        final ReentrantLockTask task = new ReentrantLockTask();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                task.buyTicket();
            }
        };

        for (int i = 0; i < 10; i++) {
            new Thread(runnable).start();
        }
    }
}
/*
Thread-2:准备好了
Thread-2:买好了
Thread-2:又准备好了
Thread-2:又买好了
Thread-3:准备好了
Thread-3:买好了
Thread-3:又准备好了
Thread-3:又买好了
Thread-4:准备好了
Thread-4:买好了
Thread-4:又准备好了
Thread-4:又买好了
Thread-5:准备好了
Thread-5:买好了
Thread-5:又准备好了
Thread-5:又买好了
Thread-6:准备好了
Thread-6:买好了
Thread-6:又准备好了
Thread-6:又买好了
Thread-7:准备好了
Thread-7:买好了
Thread-7:又准备好了
Thread-7:又买好了
Thread-8:准备好了
Thread-8:买好了
Thread-8:又准备好了
Thread-8:又买好了
Thread-9:准备好了
Thread-9:买好了
Thread-9:又准备好了
Thread-9:又买好了
Thread-10:准备好了
Thread-10:买好了
Thread-10:又准备好了
Thread-10:又买好了
Thread-11:准备好了
Thread-11:买好了
Thread-11:又准备好了
Thread-11:又买好了

*/
  1. 公平锁与非公平锁

    • 公平锁,所有进入阻塞的线程排队依次均有机会执行。使用场景:交易
    • 默认非公平锁,允许线程插队,避免每一个线程都进入阻塞,再唤醒,性能高。因为线程可以插队,导致队列中可能会存在线程饿死的情况,一直得不到锁,一直得不到执行。
ReentrantLock reentrantLock = new ReentrantLock(true/false);//true:公平;false不公平
package com.example.myapplication.demo.lock;

import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockDemo2 {
    static class ReentrantLockTask{
        ReentrantLock lock = new ReentrantLock(false);

        void print(){
            String name = Thread.currentThread().getName();
            try {
                lock.lock();
                System.out.println(name+"第一次打印");
                Thread.sleep(1000);
                lock.unlock();

                lock.lock();
                System.out.println(name+"第二次打印");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        final ReentrantLockTask task = new ReentrantLockTask();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                task.print();
            }
        };

        for (int i = 0; i < 10; i++) {
            new Thread(runnable).start();
        }
    }
}
/*
  Thread-2第一次打印
Thread-2第二次打印
Thread-3第一次打印
Thread-3第二次打印
Thread-4第一次打印
Thread-4第二次打印
Thread-5第一次打印
Thread-5第二次打印
Thread-6第一次打印
Thread-6第二次打印
Thread-7第一次打印
Thread-7第二次打印
Thread-8第一次打印
Thread-8第二次打印
Thread-9第一次打印
Thread-9第二次打印
Thread-10第一次打印
Thread-10第二次打印
Thread-11第一次打印
Thread-11第二次打印
*/
package com.example.myapplication.demo.lock;

import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockDemo2 {
    static class ReentrantLockTask{
        ReentrantLock lock = new ReentrantLock(true);

        void print(){
            String name = Thread.currentThread().getName();
            try {
                lock.lock();
                System.out.println(name+"第一次打印");
                Thread.sleep(1000);
                lock.unlock();

                lock.lock();
                System.out.println(name+"第二次打印");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        final ReentrantLockTask task = new ReentrantLockTask();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                task.print();
            }
        };

        for (int i = 0; i < 10; i++) {
            new Thread(runnable).start();
        }
    }
}
/*
Thread-2第一次打印
Thread-3第一次打印
Thread-4第一次打印
Thread-5第一次打印
Thread-6第一次打印
Thread-7第一次打印
Thread-8第一次打印
Thread-9第一次打印
Thread-10第一次打印
Thread-11第一次打印
Thread-2第二次打印
Thread-3第二次打印
Thread-4第二次打印
Thread-5第二次打印
Thread-6第二次打印
Thread-7第二次打印
Thread-8第二次打印
Thread-9第二次打印
Thread-10第二次打印
Thread-11第二次打印
*/
  1. ReentrantLock进阶用法 ——Condition条件对象
    可以使用它的await-singnal指定唤醒一个(组)线程。相比于wait-notify要么全部唤醒,要么只能唤醒一个,更加灵活可控。
package com.example.myapplication.demo.lock;

import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockDemo3 {
    static class ReentrantLockTask {

        private Condition workerCondition, worker2Condition;
        ReentrantLock lock = new ReentrantLock(true);

        volatile int flag = 0;

        public ReentrantLockTask() {
            workerCondition = lock.newCondition();
            worker2Condition = lock.newCondition();
        }

        void work1() {
            try {
                lock.lock();
                if (flag == 0 || flag % 2 == 0) {
                    System.out.println("worker1 休息会");
                    workerCondition.await();
                }
                System.out.println("worker1 搬到的砖是:" + flag);
                flag = 0;
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }

        void work2() {
            try {
                lock.lock();
                if (flag == 0 || flag % 2 != 0) {
                    System.out.println("worker2 休息会");
                    worker2Condition.await();
                }
                System.out.println("worker2 搬到的砖是:" + flag);
                flag = 0;
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }

        void boss() {
            try {
                lock.lock();
                flag = new Random().nextInt(100);
                if (flag % 2 == 0) {
                    worker2Condition.signal();
                    System.out.println("唤醒工人2:"+flag);
                }else {
                    workerCondition.signal();
                    System.out.println("唤醒工人1:"+flag);
                }
            } finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        final ReentrantLockTask lockTask = new ReentrantLockTask();

        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                   lockTask.work1();
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    lockTask.work2();
                }
            }
        }).start();

        for (int i = 0; i < 10; i++) {
            lockTask.boss();
        }
    }
}
/*

worker1 休息会
唤醒工人2:82
worker2 搬到的砖是:82
唤醒工人1:17
worker2 休息会
worker1 搬到的砖是:17
唤醒工人1:71
worker1 搬到的砖是:71
唤醒工人1:59
worker1 搬到的砖是:59
唤醒工人1:77
worker1 搬到的砖是:77
唤醒工人1:87
worker1 搬到的砖是:87
唤醒工人2:54
worker1 休息会
worker2 搬到的砖是:54
唤醒工人2:80
worker2 搬到的砖是:80
唤醒工人2:42
worker2 搬到的砖是:42
唤醒工人2:56
worker2 搬到的砖是:56
worker2 休息会
*/
  1. ReentrantReadWriteLock 共享锁、排他锁

    • 共享锁,所有线程均可同时获得,并发量高,比如在线文档查看
    • 排他锁,同一时刻只有一个线程有权修改资源,比如在线文档编辑
package com.example.myapplication.demo.lock;

import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReentrantLockDemo4 {

    static class ReentrantReadWriteLockTask{
        private final ReentrantReadWriteLock.ReadLock readLock;
        private final ReentrantReadWriteLock.WriteLock writeLock;
        ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

        ReentrantReadWriteLockTask(){
            readLock  = lock.readLock();
            writeLock = lock.writeLock();
        }

        void read(){
            String name = Thread.currentThread().getName();
            try {
                readLock.lock();
                System.out.println("线程"+name+" 正在获取数据...");
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                readLock.unlock();
                System.out.println("线程"+name+" 释放了读锁...");
            }
        }

        void write(){
            String name = Thread.currentThread().getName();
            try {
                writeLock.lock();
                System.out.println("线程"+name+" 正在写入数据...");
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                writeLock.unlock();
                System.out.println("线程"+name+" 释放了写锁...");
            }
        }
    }

    public static void main(String[] args) {
        final ReentrantReadWriteLockTask task = new ReentrantReadWriteLockTask();

        for (int i = 0; i < 3; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    task.read();//因为是读写锁,所以3个线程的日志会一起打印出来
                }
            }).start();
        }

        for (int i = 0; i < 3; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    task.write();
                }
            }).start();
        }
    }
}
/*
线程Thread-2 正在获取数据...
线程Thread-3 正在获取数据...
线程Thread-4 正在获取数据...
线程Thread-3 释放了读锁...
线程Thread-6 正在写入数据...
线程Thread-2 释放了读锁...
线程Thread-4 释放了读锁...
线程Thread-6 释放了写锁...
线程Thread-5 正在写入数据...
线程Thread-5 释放了写锁...
线程Thread-7 正在写入数据...
线程Thread-7 释放了写锁...
*/

如何正确的使用锁&原子类

  • 减少持锁时间
    在这里插入图片描述

  • 锁分离
    在这里插入图片描述

  • 锁粗化
    多次加锁,释放锁合并成一次
    在这里插入图片描述
    在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值