当前位置: 首页 > article >正文

6、多线程

一、线程创建方法

1、继承Thread类
  1. 自定义线程类,继承(extends)Thread

  2. 重写run()方法

  3. 创建线程对象,调用start()方法启动线程

    MyThread thread1 = new MyThread();
    thread1.start();
    
2、实现Runnable类(常用,因为接口可以多继承)
  1. 自定义线程类RunnableTest,实现(implements)Runnable类

  2. 重写run()方法

  3. 创建线程对象p,new Thread(p,“线程名称”).start()方法启动线程(线程名称可省略,主要用于区分多个相同对象)

    RunnableTest p=new RunnableTest();	
       Thread thread = new Thread(p);	
       thread.start;
    
3、实现Callable类、通过FutureTask获取返回值
public class MyCallable2 implements Callable<String> {
    @Override
    public String call() throws Exception {
        for (int i = 0; i < 10; i++) {
            System.out.println("子线程 ");
        }
        return "我爱中国";
    }
}


public static void main(String[] args) throws ExecutionException, InterruptedException {
        //FutureTask的泛型是MyCallable2类中call()的返回值类型
        FutureTask<String> myCallable2FutureTask = new FutureTask<>(new MyCallable2());
        Thread thread = new Thread(myCallable2FutureTask);
        thread.start();
        System.out.println("获取线程返回值,会阻塞线程,导致程序不往后执行:" + myCallable2FutureTask.get());
		//当前,下面的for会等待子线程中代码执行完毕后才会开始执行,原因:被前一行myCallable2FutureTask.get()阻塞了
        for (int i = 0; i < 10; i++) {
            System.out.println(" main ");
        }
    }

二、常用方法

1、线程休眠:thread.sleep();

2、线程礼让(停止当前线程,重新让CPU调度):thread.yield();

3、线程插队(强制优先执行p这个线程):thread.join();

4、线程状态获取:thread.getState();

5、获取线程名称:thread.currentThread.getName();

6、获取线程优先级(设置用set,1-10之间,先设置再启动,优先级是占比相对大,不是绝对优先):thread.getPriority();

7、守护线程(默认位false,即用户线程,设为true时,是守护线程,守护其他线程结束便结束) :thread.setDaemon(true);

8、同步监视器,隐式锁,synchronizedOjb{增删改方法}

三、线程的生命周期

  • 新建
  • 可运行
  • 被终止
  • 阻塞
  • 无限等待
  • 计时等待

四、锁

1、隐式锁:synchronized
public class Demo01 {
    public static void main(String[] args) {
        /**
         * 1、同步代码块
         * 格式:
         *      synchronized(同步锁对象){
         *          操作共享资源的代码
         *      }
         *      
         * 2、同步方法
         * 格式:
         *      修饰符 synchronized 返回值类型 方法名称(形参列表) {
         *              操作共享资源的代码
         *      }
         */

        Runnable runnable = new Runnable() {
            int ticket = 100;

            @Override
            public void run() {
                while (true){
                    /*
                    synchronized
                    1、确保多个线程使用的是同一个锁对象即可,锁对象可以是任意对象。
                    2、如果多个线程共用同一个runnable对象,runnable的run方法内可以使用this作为锁对象。
                    3、对于静态方法建议使用字节码(类名.class)对象作为锁对象,其他方法也能使用字节码(类名.class)对象作为锁对象
                    */

                    //1、同步代码块
                    synchronized (this){
                        if(ticket>0){
                            System.out.println(Thread.currentThread().getName()+" 卖出了第 "+ticket+" 张票");
                            ticket--;
                        }
                        else {
                            break;
                        }
                    }
                }
            }

            //2、同步方法
            private synchronized void tellticket(){
                if(ticket>0){
                    System.out.println(Thread.currentThread().getName()+" 卖出了第 "+ticket+" 张票");
                    ticket--;
                }

            }
        };


        Thread thread1 = new Thread(runnable, "窗口1");
        Thread thread2 = new Thread(runnable, "窗口2");
        Thread thread3 = new Thread(runnable, "窗口3");
        thread1.start();
        thread2.start();
        thread3.start();

    }
}
2、显式锁,ReentrantLock,手动开启关闭锁。
public class Demo03 {
    public static void main(String[] args) {
        /**
         * Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来构建Lock锁对象
         *      void lock():获得锁
         *      void unlock():释放锁
         */

        ReentrantLock lock = new ReentrantLock();
        Runnable runnable = new Runnable() {
            int ticket = 100;

            @Override
            public void run() {
                while (true) {
                    try {
                        //获得锁
                        lock.lock();
                        if (ticket > 0) {
                            System.out.println(Thread.currentThread().getName() + " 卖出了第 " + ticket + " 张票");
                            ticket--;
                        } else {
                            break;
                        }
                    } finally {
                        //释放锁
                        lock.unlock();
                    }

                }
            }
        };


        Thread thread1 = new Thread(runnable, "窗口1");
        Thread thread2 = new Thread(runnable, "窗口2");
        Thread thread3 = new Thread(runnable, "窗口3");
        thread1.start();
        thread2.start();
        thread3.start();

    }
}

五、线程池

1、线程创建
  1. 使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象

    public static void main(String[] args) throws ExecutionException, InterruptedException {
            /**
             * ExecutorService的常用api:
             *      execute(Runnable runnable):执行任务/命令,没有返回值,一般用来执行 Runnable 任务
             *      Future<T> submit(Callable<T> task):执行任务,返回未来任务对象获取线程结果,一般拿来执行Callable任务
             *      shutdown():等任务执行完毕后关闭线程池
             *      shutdownNow():立刻关闭,停止正在执行的任务,并返回队列中未执行的任务
             */
    
            //线程池创建方式1
            ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
                    3,//核心线程数
                    5,//最大线程数
                    5,//临时线程保存时间
                    TimeUnit.HOURS,//时间单位
                    new LinkedBlockingQueue<>(10),//任务队列
                    Executors.defaultThreadFactory(),//创建工厂
                    new ThreadPoolExecutor.AbortPolicy()//拒绝策略
            );
    
            //poolExecutor.submit创建线程任务
            //当创建线程任务的数量>任务队列的数量+核心线程数时,才开始新增调用临时线程
            for (int i = 0; i < 14; i++) {
                Future<String> future = poolExecutor.submit(() -> {
                    System.out.println(Thread.currentThread().getName() + "线程  执行了");
                    return Thread.currentThread().getName() + " 返回了";
                });
                System.out.println("future.get() = " + future.get());
            }
    
            /*
            //poolExecutor.execute创建线程任务
            //当创建线程任务的数量>任务队列的数量+核心线程数时,才开始新增调用临时线程
            for (int i = 0; i < 14; i++) {
                poolExecutor.execute(() -> System.out.println(Thread.currentThread().getName()+"线程执行了"));
            }*/
    
            /*
            //线程池立刻关闭,停止正在执行的任务,返回未执行队列中
            List<Runnable> runnables = poolExecutor.shutdownNow();
            runnables.forEach(runnable -> System.out.println("runnable = " + runnable));
            */
    
            //线程池关闭
            poolExecutor.shutdown();
        }
    
  2. 使用Executors(线程池的工具类),调用方法返回不同特点的线程池对象
    public static void main(String[] args) {
            /**
             * newCachedThreadPool():
             *      线程数量随着任务增加而增加,如果线程任务执行完毕且空闲了一段时间则会被回收掉
             * newFixedThreadPool(int nThreads):
             *      创建固定线程数量的线程池,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程替代它
             * newSingleThreadExecutor():
             *      创建只有一个线程的线程池对象,如果该线程出现异常而结束,那么线程池会补充一个新线程
             * newScheduledThreadPool(int corePoolSize):
             *      创建一个线程池,可以实现在给定的延迟后运行任务,或者定期执行任务
             */
            ExecutorService executorService = Executors.newCachedThreadPool();
        }
    
2、线程池工作原理
  1. 当有新的任务交给线程池时,先由核心线程来处理。
  2. 当核心线程都在处理任务时,多的任务将存放到任务队列(LinkedBlockQueue)中。
  3. 当任务队列满了,核心线程都在处理任务,且处理任务的线程总数还没有达到最大线程数,线程工厂开始创建临时线程处理任务。
  4. 当核心线程和临时线程都在处理任务,且线程总数达到了最大线程数,,任务队列也满了,还有新的任务进来的就会开始拒绝策略。

六、其他

1、管程法,生产者/消费者模式,消费者不直接拿生产者的数据,而是从缓冲区拿数据
public static void main(String[] args) {
        //管程法
        Buffer buffer=new Buffer();
        new Thread(new Producer(buffer)).start();
        new Thread(new Consumer(buffer)).start();
    }
    //生产者
    static class Producer implements Runnable{
        Buffer buffer;

        public Producer(Buffer buffer) {
            this.buffer = buffer;
        }

        @Override
        public void run() {
            for (int i = 1; i < 100; i++) {
                System.out.println("生产了第"+i+"个产品");
                buffer.add(new Product());
            }
        }
    }

    //消费者
    static class Consumer implements Runnable{
        Buffer buffer;

        public Consumer(Buffer buffer) {
            this.buffer = buffer;
        }

        @Override
        public void run() {
            for (int i = 1; i < 100; i++) {
                System.out.println("  消费者消费了   第"+i+"个产品");
                buffer.reduce();
            }
        }
    }

    //产品
    static class Product{
        int id;
    }

    //缓冲区
    static class Buffer {
        Product[]products=new Product[10];
        int count =0;
        //生产这进行新增
        public synchronized void add(Product product){
            if(count==products.length){
                //缓冲区满了,等待减少
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            products[count]=product;
            count++;
            System.out.println("      缓存区有"+count+"个产品");
            this.notifyAll();
        }

        //消费者进行减少
        public synchronized void reduce(){
            if(count==0){
                //缓冲区空了,等待新增
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            count--;
            System.out.println("      缓存区有"+count+"个产品");
            this.notifyAll();
        }
    }
2、信号灯法,生产者/消费者模式,根据一个变量去确定是等待,还是执行
public class ThreadTest3 {
    public static void main(String[] args) {
        TV tv=new TV();
        new Thread(new Seeer(tv)).start();
    }

}
//表演者
class Player implements Runnable{
    private TV tv;
    private String project;
    public Player(TV tv,String project) {
        this.tv = tv;
        this.project = project;
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            tv.play(project);
            if(i%2==0){
                tv.play("吉祥如意的一家");
            }
            else{
                tv.play(project);
            }
        }
    }
}
//观看者
class Seeer implements Runnable{
    private TV tv;
    public Seeer(TV tv) {
        this.tv = tv;
    }
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            tv.see();
        }
    }
}

class TV {
    private String project;
    private boolean flag=true;
    synchronized void play(String project){
        if(flag==true){
            System.out.println("表演者表演了<"+project+">");

            //Thread.currentThread().notifyAll();//这里只能用this,用这个会报错
            this.flag=false;
            this.project=project;
            this.notifyAll();
        }
        else {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    synchronized void see(){
        if(flag==false){
            System.out.println("    观看者看了  "+project);
            this.flag=true;
            this.notifyAll();
        }
        else {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

http://www.kler.cn/a/301642.html

相关文章:

  • 【阅读记录-章节1】Build a Large Language Model (From Scratch)
  • 人工智能与SEO优化中的关键词策略解析
  • 31DNS设置
  • 【Linux系统编程】第四十六弹---线程同步与生产消费模型深度解析
  • 工作时发现自己手写SQL能力很低,特此再来学习一遍SQL
  • Vuex vs Pinia:新一代Vue状态管理方案对比
  • 如何使用python运行Flask开发框架并实现无公网IP远程访问
  • 力扣刷题之2555.两个线段获得的最多奖品
  • 装杯 之 Linux 指令1
  • 哈希表及算法
  • xLSTM模型学习笔记
  • 高性能计算机A100会带有BMC功能 ;BMC;SSH
  • Thinkphp5实现一周签到打卡功能
  • 前端算法(持续更新)
  • Linux_kernel移植rootfs10
  • 普发Pfeiffer TCP600TCP5000手侧
  • Python——贪吃蛇
  • JAVA基础:抽象类,接口,instanceof,类关系,克隆
  • 在深度学习计算机视觉的语义分割中,Boundary和Edge的区别是?
  • NX—UI界面生成的文件在VS上的设置
  • 【网络】DNS
  • 备忘录模式memento
  • C语言初识编译和链接
  • 01 Docker概念和部署
  • 【论文速读】| SEAS:大语言模型的自进化对抗性安全优化
  • Arm GIC-v3中断原理及验证(通过kvm-unit-tests)