JAVA多线程(二)

JAVA多线程(二)

  • 线程是宝贵的内存资源,单个线程约占1MB空间,过多的分配易造成内存溢出
  • 频繁的创建及销毁线程会增加虚拟机回收频率、资源开销、造成程序性能下降

线程池

  • 线程的容器,可设定线程分配的数量上限
  • 将预先创建的线程对象存入池中,并重用线程池中的线程对象
  • 避免频繁的创建或销毁

线程池原理

  • 将任务提交给线程池,由线程池分配线程、运行任务,并在当前任务结束后复用线程

创建线程池

  • Executor:线程池的顶级接口
  • ExecutorService:线程池接口,可通过submit(Runnable task)提交任务代码
  • Executors工厂类:通过此类可以获得一个线程池
  • 通过newFixedThreadPool(int nThreads)获取固定数量的线程池。参数:指定线程池中线程的数量
  • 通过newCachedThreadPool()获取动态数量的线程池,没有上线

Executors:创建线程池的工具类

  • 创建固定大小的线程池

  • 创建缓存线程池,由任务的多少来决定

  • 创建单线程池

  • 创建调度线程池 调度(周期,定时执行)

  • package com.wly.Executor;
    
    import java.util.concurrent.Executor;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    //Executor:线程池的根接口,execute()
    //ExecutorService:包含管理线程的一些方法submit
    //TheardPoolExecuter
        //ScheduledTheardPoolExecutor
    //Ex
    
    
    public class Demo {
        public static void main(String[] args) {
            //创建固定线程个数的线程池
    //        ExecutorService es= Executors.newFixedThreadPool(4);
            //创建缓存线程池,线程个数由任务个数决定
            ExecutorService es=Executors.newCachedThreadPool();
            //单线程池
    //        Executors.newSingleThreadExecutor();
            //调度线程池
    //        Executors.newScheduledThreadPool(4);
            //提交任务
            Runnable runnable=new Runnable() {
                private int ticket=100;
                @Override
                public void run() {
                    while (true){
                        if (ticket<=0){
                            break;
                        }
                        System.out.println(Thread.currentThread().getName()+"卖了第"+ticket+"票");
                        ticket--;
                    }
    
                }
            };
            //提交任务
            for (int i = 0; i <5; i++) {
                es.submit(runnable);
            }
            //关闭线程池
            es.shutdown();//等待所有任务执行完毕,关闭线程池
        }
    }
    

Callable接口

  • public interface Callable{

    public V call() throws Exception;

    }

  • JDK5加入,与Runnable接口类似,实现之后代表一个线程任务

  • Callable具有泛型返回值,可以声明异常

  • Callable接口中的call方法有返回值,Runnable接口里的run()方法没有返回值

  • Callable接口中的call方法有声明异常,Runable接口中的run()方法没有异常

  • package com.wly.Executor;
    
    import java.util.concurrent.*;
    
    //使用线程池
    public class DemoCallable2 {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            //创建线程池
            ExecutorService executorService=Executors.newFixedThreadPool(1);
            //提交方法
            Future<Integer> future=executorService.submit(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    System.out.println(Thread.currentThread().getName()+"开始计算");
                    int sum=0;
                    for (int i = 0; i <100; i++) {
                        sum+=i;
                    }
                    return sum;
                }
            });
            //获取任务的结果
            System.out.println(future.get());
            //关闭线程池
            executorService.shutdown();
        }
    }
    
    

Future接口

  • Future:表示将要完成任务的结果

  • 使用两个线程,并发计算1-50,50-100,再进行汇总

  • package com.wly.Executor;
    
    import java.awt.*;
    import java.util.concurrent.*;
    
    //使用两个线程,并发进行1~50,50~100,再进行汇总统计
    public class DemoCallable3 {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            ExecutorService es=Executors.newFixedThreadPool(2);
            //添加任务
            Future<Integer>future1= es.submit(new Callable<Integer>() {
    
                @Override
                public Integer call() throws Exception {
                    int sum=0;
                    for (int i = 0; i <=50 ; i++) {
                        sum+=i;
                    }
                    System.out.println("1~50");
                    return sum;
                }
            });
            Future<Integer>future2= es.submit(new Callable<Integer>() {
    
                @Override
                public Integer call() throws Exception {
                    int sum=0;
                    for (int i = 51; i <=100 ; i++) {
                        sum+=i;
                    }
                    System.out.println("51~100");
                    return sum;
                }
            });
            int sum=future1.get()+future2.get();
            System.out.println("结果是"+sum);
            //关闭线程池
            es.shutdown();
        }
    }
    
  • 表示ExecutorService.submit()所返回的状态,就是call()的返回值

  • 方法**V.get()**以阻塞形式等待Future中的异步处理结果(call()的返回值)

线程的同步

  • 形容一次方法调用,同步一旦开始,调用者必须等待该方法返回

线程的异步

  • 形容一次方法调用,异步一旦开始,像是一次消息传递,调用在告知之后立刻返回,两者竞争时间片

Lock()接口

  • JDK5,与synchronized比较,显示定义,结构更灵活。
  • 提供更多实用性方法,功能更强大,性能更优越
  • 常用方法
    • void lock()//获得锁,如果锁被占用,则等待
    • boolean tryLock()//尝试获取锁(成功返回ture,失败返回false,不阻塞)
    • void unlock()//释放锁

重入锁

  • ReentrantLock:Lock接口实现类,与synchronized一样具有互斥锁功能

  • package com.wly.Executor;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class MylistLock {
        //创建锁
        private Lock lock = new ReentrantLock();
        private String[] str = {"A", "B", "", "", ""};
        private int count = 2;
    
        public void add(String value)  {
            lock.lock();
            try{
            str[count]=value;
            count++;
                System.out.println(Thread.currentThread().getName()+"添加了"+value);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            finally {
                lock.unlock();
            }
        }
        public String[] getStr(){
            return str;
        }
    }
    
  • package com.wly.Executor;
    
    import com.wly.Executor.MylistLock;
    
    import java.util.Arrays;
    
    public class TestMylist {
        public static void main(String[] args) throws InterruptedException {
    
            MylistLock list = new MylistLock();
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    list.add("hello");
                }
            };
            Runnable runnable2 = new Runnable() {
                @Override
                public void run() {
                    list.add("word");
                }
            };
            //创建线程
            Thread t1 = new Thread(runnable);
            Thread t2 = new Thread(runnable2);
            t1.start();
            t2.start();
            //
            t1.join();
            t2.join();
            System.out.println(Arrays.toString(list.getStr()));
        }
    
    }
    

重入锁使用

  • package com.wly.Executor;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class Ticket implements Runnable {
        private int ticket=100;
        private Lock lock=new ReentrantLock();
        @Override
        public void run() {
            while (true) {
                lock.lock();
                try {
                    if (ticket <= 0) {
                        break;
                    }
                    System.out.println(Thread.currentThread().getName() + "卖了" + ticket + "张票");
                    ticket--;
                } finally {
                    lock.unlock();
                }
            }
        }
    }
    
    
  • package com.wly.Executor;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class TestTicket {
        public static void main(String[] args) {
            Ticket ticket=new Ticket();
            ExecutorService es=Executors.newFixedThreadPool(4);
            for (int i = 0; i <4 ; i++) {
                es.submit(ticket);
            }
            es.shutdown();
        }
    }
    

读写锁

  • ReentrantReadWriteLock:

  • 一种支持一种多读的同步锁,读写分离,可以分别分配读锁,写锁

  • 支持多次分配读锁,使用多个读操作可以并发执行

  • 互斥规则:

    • 写——写:互斥,阻塞
    • 读——写:读阻塞写、写阻塞读
    • 读——读:不互斥、不阻塞
  • 在读操作远远高于写操作环境下,可以保障线程安全的情况下,提高运行效率。

  • package com.wly;
    
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    import java.util.concurrent.locks.ReentrantReadWriteLock.*;
    
    
    public class ReadWriteDemo {
        //创建读写锁
        private ReentrantReadWriteLock rr1=new ReentrantReadWriteLock();
        //获取读锁
        private ReadLock readLock=rr1.readLock();
        //获取写锁
        private WriteLock writeLock=rr1.writeLock();
        private String value;
        //读取方法
        public String getValue(){
            //使用读锁上锁
            readLock.lock();
            try {
                try{
                    Thread.sleep(1000);
    
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("读取=========="+value);
                return this.value;
            }finally {
                readLock.unlock();
            }
        }
        //写入方法
        public void setValue(String value){
            writeLock.lock();
            try{
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("写入"+value);
                this.value=value;
            }finally {
                writeLock.unlock();
            }
        }
    }
    
    
  • package com.wly;
    
    import java.util.Random;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class TestReadWrite {
        public static void main(String[] args) {
            ReadWriteDemo readWriteDemo = new ReadWriteDemo();
            //创建线程池
            ExecutorService es = Executors.newFixedThreadPool(20);
            Runnable read = new Runnable() {
                @Override
                public void run() {
                    readWriteDemo.getValue();
                }
            };
            Runnable write = new Runnable() {
                @Override
                public void run() {
                    readWriteDemo.setValue("张三" + new Random().nextInt(100));
                }
            };
            long start = System.currentTimeMillis();
            //2个写任务
            for (int i = 0; i < 2; i++) {
                es.submit(write);
            }
            //18个读取任务
            for (int i = 0; i < 18; i++) {
                es.submit(read);
            }
            es.shutdown();
            //isTerminated()为true表示线程池任务执行完毕
            while (!es.isTerminated()) {
            }
            long end=System.currentTimeMillis();
    
            System.out.println("用时:"+(end-start));
        }
    }
    

线程安全的集合

  • Collection体系中,除Vector以外的线程安全集合

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MBIEo186-1672213466536)(D:\笔记\JAVA多线程\IMG_1291(20220401-185857)].PNG)

  • Map体系

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jDUTp7tL-1672213466537)(D:\笔记\JAVA多线程\IMG_1292(20220401-191351)].PNG)

Collections中的工具方法

  • Collections工具类中提供了多个可以获得线程安全集合的方法
    • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-txTCH1l1-1672213466537)(D:\笔记\JAVA多线程\IMG_1293(20220401-192443)].PNG)
  • JDK1.2提供,接口统一,维护性高,但性能没有提升,均以synchonized实现

CopyOnWriteArrayList

  • 线程安全的ArrayList,加强版读写分离

  • 写有锁,读无锁,读写之间不阻塞,优于读写锁

  • 写入时,先copy一个容器副本,再添加新元素,最后替换引用

  • 使用方式与ArrayList无异

  • package com.wly.Collections;
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import java.util.concurrent.CopyOnWriteArrayList;
    
    public class Demo1 {
        public static void main(String[] args) {
            //方法一:
    //        ArrayList<String> arrayList=new ArrayList<>();
    //        List<String> synlist= Collections.synchronizedList(arrayList);
            //方法二:
            CopyOnWriteArrayList<String> arrayList=new CopyOnWriteArrayList<>();
            //创建线程
            for (int i = 0; i <5 ; i++) {
                int temp=i;
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (int j = 0; j <10 ; j++) {
                            arrayList.add(Thread.currentThread().getName()+"======"+temp+"====");
                            System.out.println(arrayList.toString());
                        }
                    }
                }).start();
            }
        }
    }
    
  • package com.wly.Collections;
    
    import java.util.Random;
    import java.util.concurrent.CopyOnWriteArrayList;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class Demo2 {
        public static void main(String[] args) {
            CopyOnWriteArrayList list=new CopyOnWriteArrayList();
            //使用多线程操作
            ExecutorService es=Executors.newFixedThreadPool(5);
            for (int i = 0; i < 5; i++) {
                es.submit(new Runnable() {
                    @Override
                    public void run() {
                        for (int j = 0; j <10 ; j++) {
                            list.add(Thread.currentThread().getName()+"-------"+new Random().nextInt(1000));
                        }
                    }
                });
            }
            //关闭线程池
            es.shutdown();
            while (!es.isTerminated()){
            }
            System.out.println("元素个数"+list.size());
            for (Object string:list
                 ) {
                System.out.println(string);
            }
    
        }
    }
    
    
  • package com.wly.Collections;
    
    import java.util.Random;
    import java.util.concurrent.CopyOnWriteArrayList;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class Demo2 {
        public static void main(String[] args) {
            CopyOnWriteArrayList list=new CopyOnWriteArrayList();
            //使用多线程操作
            ExecutorService es=Executors.newFixedThreadPool(5);
            for (int i = 0; i < 5; i++) {
                es.submit(new Runnable() {
                    @Override
                    public void run() {
                        for (int j = 0; j <10 ; j++) {
                            list.add(Thread.currentThread().getName()+"-------"+new Random().nextInt(1000));
                        }
                    }
                });
            }
            //关闭线程池
            es.shutdown();
            while (!es.isTerminated()){
            }
            System.out.println("元素个数"+list.size());
            for (Object string:list
                 ) {
                System.out.println(string);
            }
    
        }
    }
    

CopyOnEriteArraySet

  • 线程安全的Set,底层使用CopyOnWriteArrayList实现
  • 唯一不同在于,使用addIfAbsent()添加元素,会遍历数组
  • 如果元素存在,则不添加(不重复)

Queue接口队列

  • Collection的子接口,表示队列EIFO(First In First Out)先进先出

  • 常用方法:

    • 抛出异常

    • 方法说明
      boolean add(E e)顺序添加一个元素(达到上限后,再添加则会抛出异常)
      E emove()获得第一个元素并移除(如果对列没有元素,则抛出异常)
      E element()获得第一个元素但不移除(如果对列没有元素,则抛出异常)
    • 返回特殊值

    方法说明
    boolean offer(E e)顺序添加一个元素(到达上限后,再添加则会返回false)
    E poll()获得第一个元素并移除(如果队列没有元素时,则返回null)
    E peek()获得第一个元素但不移除(如果队列没有元素时,则返回null)

ConcurrentLinkedQueue

  • 线程安全、可高效读写队列,高并发下性能最好的队列

  • 无锁、CAS比较交换算法,修改的方法包含三个核心参数(V,E,N)

  • V:要更新的变量、E:预期值、N:新值

  • 只有当V=E时,V=N;否则表示已被更新过,则取消当前操作

  • package com.wly.Collections;
    
    import java.util.LinkedList;
    import java.util.Queue;
    import java.util.concurrent.ConcurrentLinkedQueue;
    
    public class Demo3 {
        public static void main(String[] args) throws InterruptedException {
            ConcurrentLinkedQueue<Integer> queue=new ConcurrentLinkedQueue<>();
            //入队操作
            Thread t1=new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i <5 ; i++) {
                        queue.offer(i);
                    }
                }
            });
            Thread t2=new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 6; i <10 ; i++) {
                        queue.offer(i);
                    }
                }
            });
            //启动线程
            t1.start();
            t2.start();
            t1.join();
            t2.join();
            System.out.println("===================");
            //出队操作
            int size=queue.size();
            for (int i = 0; i <size ; i++) {
                System.out.println(queue.poll());
            }
        }
    }
    

BlockingQueue接口(阻塞队列)

  • Queue的子接口,阻塞队列,增加了两个线程状态为无期限等待的方法

  • 方法:

    • 方法说明
      void put(E e)将指定元素插入此队列中,如果没有可用空间,则等待
      E take()获取并移除此队列头部元素,如果没有可用元素,则等待
阻塞队列
  • ArrayBlockingQueue:数组结构实现,有界队列(手工固定上限)

  • LinkedBlockingQueue:链表结构实现,有界队列(默认上限Integer.MAX_VAULE)

  • package com.wly.Collections;
    
    import java.util.concurrent.ArrayBlockingQueue;
    //阻塞队列
    public class Demo4 {
        public static void main(String[] args) throws InterruptedException {
            //创建一个有界队列,添加元素
            ArrayBlockingQueue<String>queue=new ArrayBlockingQueue<>(5);
            //添加元素
            queue.put("aaa");
            queue.put("bbb");
            queue.put("ccc");
            queue.put("ddd");
            queue.put("eee");
            queue.take();
            System.out.println("已经添加了5个元素");//超过个数会一直等待
            queue.put("xxx");
            System.out.println("加入第6个元素");
            System.out.println(queue.toString());
    
        }
    }
    
    
生产者、消费者问题
  • package com.wly.Collections;
    
    import java.util.concurrent.ArrayBlockingQueue;
    
    //阻塞对列的使用
    //使用阻塞对列实现生产者,消费者
    public class Demo5 {
        public static void main(String[] args) throws InterruptedException {
            ArrayBlockingQueue<Integer> queue=new ArrayBlockingQueue<>(6);
            //创建两个线程
            Thread t1=new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i <30 ; i++) {
                        try {
                            queue.put(i);
                            System.out.println(Thread.currentThread().getName()+"生产了"+i);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
    
                    }
                }
            },"晨晨");
            Thread t2=new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i <30; i++) {
                        try {
                            Integer take = queue.take();
                            System.out.println(Thread.currentThread().getName()+"消费了"+i);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
    
                    }
                }
            },"明明");
            t1.start();
            t2.start();
    
    
    
        }
    
    }
    
    

ConcurrentHashMap

  • 初始容量默认增加16段(Segment),使用分段锁设计

  • 不对整个Map加锁,而是为每个Segment加锁

  • 当多个对象存入同一个Segment时,才需要互斥

  • 使用方式与HashMap无异

  • package com.wly.Collections;
    
    import java.util.concurrent.ConcurrentHashMap;
    
    public class Demo6 {
        public static void main(String[] args) {
            ConcurrentHashMap<String,String> hashMap=new ConcurrentHashMap<>();
            //使用多线程添加数据
            for (int i = 0; i <5 ; i++) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (int j = 0; j <10 ; j++) {
                            hashMap.put(Thread.currentThread().getName()+"==="+j,j+"");
                            System.out.println(hashMap);
                        }
                    }
                }).start();
            }
        }
    }
    
    
  • 当多个对象存入同一个Segment时,才需要互斥

  • 使用方式与HashMap无异

  • package com.wly.Collections;
    
    import java.util.concurrent.ConcurrentHashMap;
    
    public class Demo6 {
        public static void main(String[] args) {
            ConcurrentHashMap<String,String> hashMap=new ConcurrentHashMap<>();
            //使用多线程添加数据
            for (int i = 0; i <5 ; i++) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (int j = 0; j <10 ; j++) {
                            hashMap.put(Thread.currentThread().getName()+"==="+j,j+"");
                            System.out.println(hashMap);
                        }
                    }
                }).start();
            }
        }
    }
    
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值