线程池、线程安全问题、互斥锁以及线程安全的集合类

线程池用于管理线程,减少创建销毁的开销,如ExecutorService的newFixedThreadPool和newCachedThreadPool。Callable任务可返回结果并处理异常。线程安全问题涉及多个线程访问临界资源,解决手段包括synchronized的同步方法和同步代码块,以及并发集合如ConcurrentHashMap和CopyOnWriteArrayList。
摘要由CSDN通过智能技术生成

线程池

  • 作用:可以承载管理多个线程任务,需要时将任务提交执行,任务执行结束并不会立即销毁,而是回到池中等待下次执行,直至线程池关闭。可以大大降低一个任务反复执行时多次创建线程对象带来的内存压力。

相关API

  1. ExecutorService:线程池接口

    • 对象.submit(任务对象):将线程任务对象提交执行

    • 对象.shutdown():关闭线程池

  2. Excutors:线程池工具类

    • ExecutorService newfixedThreadPool(int ):获取一个固定并发数量的线程池对象

      • 执行:当设置的并发数量大于等于任务提交数量时,则任务会直接并发执行。当设置的并发数量小于提交数量时,先提交的任务先并发执行,当有任务执行结束让位之后,剩余任务才能进入池中参与执行

    • ExecutorService newCachedThreadPool():获取一个不固定并发数量的线程池

      • 执行:所有提交的任务都会同时并发执行

线程任务

  1. Runnable:run()

    • 特点:无返回值,不能上抛异常

    ​
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    ​
    public class TestES {
        public static void main(String[] args) {
            //获取一个固定并发数量的线程池
           // ExecutorService es = Executors.newFixedThreadPool(2);
            //获取不固定并发数量的线程池
            ExecutorService es = Executors.newCachedThreadPool();
    ​
            Runnable r1=new Runnable() {
                @Override
                public void run() {
                    for (int i = 1; i <=50 ; i++) {
                        System.out.println("r1:"+i);
                    }
                }
            };
            Runnable r2=new Runnable() {
                @Override
                public void run() {
                    for (int i = 51; i <=100 ; i++) {
                        System.out.println("r2="+i);
                    }
                }
            };
            Runnable r3=new Runnable() {
                @Override
                public void run() {
                    for (int i = 101; i <=150 ; i++) {
                        System.out.println("r3>>>"+i);
                    }
                }
            };
    ​
            es.submit(r1);
          //  es.submit(r1);
            es.submit(r2);
            es.submit(r3);
    ​
            //关闭线程池
            es.shutdown();
        }
    }
  2. Callable<返回值泛型>:call()

    • 特点:有返回值,可以上抛异常,默认上抛Exception

    • 使用:会将返回值封装进一个Future对象,该对象中的get()可以获取到被封装的返回值

    ​
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    ​
    public class TestES_Callable {
        public static void main(String[] args) throws Exception{
            //创建一个线程池对象
            ExecutorService es = Executors.newCachedThreadPool();
    ​
            //计算1-100的和
            Callable<Integer> c1=new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    int sum = 0;
                    for (int i = 1; i <= 100; i++) {
                        sum += i;
                    }
                    return sum;
                }
            };
    ​
            //提交执行并接收返回值
            Future<Integer> future =es.submit(c1);
            System.out.println("查看返回值:"+future.get());
    ​
            //关闭线程池
            es.shutdown();
    ​
        }
    }

线程安全问题

  • 多个线程同时访问同一个临界资源时,有可能破坏其原子操作,从而导致数据缺失,就会发生线程安全问题

  • 临界资源:被多个线程同时访问的对象

  • 原子操作:访问临界资源对象时不可缺失或更改的操作步骤

互斥锁

  • 每个对象都默认拥有互斥锁,当开启互斥锁之后,线程会进入同步状态,只有同时拥有时间片和锁标记的线程才有资格执行自身内容,其他线程只有等正在执行的线程完成原子操作之后才能争抢资源

  • synchronized:开启互斥锁

同步方法

  • 原理:在被线程同时访问的方法上加锁

    访问修饰符 synchronized 返回值类型 方法名(参数列表){}
    ​
    import java.util.ArrayList;
    import java.util.List;
    ​
    /**
     * 操作集合属性的工具类
     * 1. 往集合中添加元素
     * 2. 查看集合元素
     */
    public class MyList {
        private List<Integer> list = new ArrayList<>();
    ​
        //添加元素
        //同步方法
        public synchronized void insert(int n){
            list.add(n);
        }
    ​
        //查看集合元素
        public void query(){
            //长度
            System.out.println("集合长度为:"+list.size());
            //查看内容
            for (Integer i : list) {
                System.out.print(i+"  ");
            }
        }
    ​
    }

同步代码块

  • 原理:访问临界资源的线程们自己加锁

    synchronized(临界资源对象){
        //访问语句
    }
    ​
    import com.bz.util.MyList;
    ​
    public class TestMyList {
        public static void main(String[] args) throws Exception{
            //创建一个工具类对象
            MyList m = new MyList();
            //线程1:添加1-5
            Thread t1 = new Thread(()->{
                synchronized (m) {
                    for (int i = 1; i <= 5; i++) {
                            m.insert(i);
                    }
                }
            });
            //线程2:添加6-10
            Thread t2 = new Thread(()->{
                //同步代码块
                synchronized (m){
                    for (int i = 6; i <= 10; i++) {
                        m.insert(i);
                    }
                }
            });
    ​
            t1.start();
            t2.start();
            //使t1和t2在主函数查看之前执行
            t1.join();
            t2.join();
    ​
            //查看集合内容
            m.query();
        }
    }
    ​
    /*
    * 线程1:张三上厕所
    *
    * 线程2:李四上厕所
    *
    *临界资源:厕所
    *原子操作:步骤:脱裤子-->蹲下去-->上厕所-->擦屁股-->提裤子-->站起来走人
    *
    *加锁的第一种:给厕所大门上锁(厕所管理者)
    *           上厕所的条件:时间片(资格)+钥匙(争抢)
    *加锁的第二种:给坑位上锁(自己)
    *           上厕所的条件:时间片+钥匙(不需要争抢,自己上锁)
    *
    * */

区别

  1. 同步方法线程需要争抢时间片和锁标记,效率较慢

  2. 同步代码块线程只需要争抢时间片,拥有时间片的线程默认拥有锁标记,效率较快(更推荐)

线程安全的集合类

悲观锁:悲观的认为线程一定会发生安全问题,所以统加锁

  • Vactor

  • Hashtable

  • Properties

乐观锁:乐观的认为线程不会发生安全问题,所以不加锁,等到安全问题真正发生时,再用算法结合极少量的synchronized处理问题

JDK5.0

都属于java.util.concurrent

  1. ConcurrentHashMap:CAS算法

    比较并交换:比较预期值和原有值,当其相等时才会将结果值放入内存

    int i=1;

    i++;

    原有值:1 预期值:1 结果值2

  2. CopyOnWriteArrayList

    • 原理:在对集合进行写操作(增删改)时,先复制出一个副本,在副本上完成写操作,如果副本中出现安全问题,则直接舍弃该副本,再次复制新的副本重复操作,直至副本中正常执行写操作,然后将集合地址转换成副本地址。

    • 特点:舍弃写的效率,提高读的效率。适用于读操作远多于写操作时

  3. CopyOnWriteArraySet

    • 原理:与CopyOnWriteArrayList一致,在写入时会执行去重操作

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值