zoukankan      html  css  js  c++  java
  • JAVA多线程的创建和使用

    线程的创建和使用

    多线程创建,方式一:继承Thread类

      1. 创建一个继承 Thread 的子类
      2. 重写 Thread 类中的 run() --> 此线程执行的操作申明在run()
      3. 创建 Thread 类的子类的对象
      4. 通过此对象调用 start()
    public class ThreadTest extends Thread{
    
        @Override
        public void run() {
            // 线程休眠 1秒
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            String odd = "";
            for (int i = 1; i <= 100; ++ i) {
                if (1 != (i & 1)) {
                    odd = odd + i + " ";
                }
            }
            System.out.println("1~100的偶数有:" + odd);
        }
        public static void main(String[] args) {
            ThreadTest t1 = new ThreadTest();
            t1.start();
            String even = "";
            for (int i = 1; i <= 100; ++ i) {
                if (1 == (i & 1)) {
                    even += i + " ";
                }
            }
            System.out.println("1~100的奇数为:" + even);
        }
    
    }
    
    
    • Thread中的常用方法:

        1. start() : 启动当前线程; 调用当前线程的 run()
        2. run() : 通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
        3. currentThread() : 静态方法,返回执行当前代码的线程
        4. getName() : 获取当前线程的名字
        5. setName() : 设置当前线程的名字
        6. yield() : 释放当前线程的执行权
        7. sleep(Long milltime) : 令当前活动线程在指定时间内放弃对CPU控制,使其他线程有机会被执行,时间到后重新排队。抛出异常InterruptedException异常
        8. join() : 在线程a中调用线程b的 join(),此时线程a就进入到阻塞状态,直到线程b完全执行完以后,线程a才结束阻塞状态
        9. stop() : 强制线程生命周期结束,不推荐使用
        10. isAlive() : 判断当前线程是否存活
    • 线程的调度

      • 调度策略

          1. 时间片
          2. 抢占式:高优先级的线程抢占CPU
      • Java的调度方法

          1. 同优先级线程组成先进先出队列(先到先服务),使用时间策略
          2. 对高优先级,使用优先调度的抢占式策略
      • 线程的优先级

        • 线程的优先级是来改变线程被cpu调度的概率,不是说优先级高就一定被先调度,取决于cpu。
        • MAX_PRIORITY: 10
        • MIN_PRIORITY: 1
        • NORM_PRIORITY: 5
      • 涉及方法

        • getPriority(): 返回线程的优先值

        • setPriority(int newPriority): 改变线程的优先级

        • Thread t1 = new Thread(window, "窗口1");
          
          t1.getPriority();
          t1.setPriority(Thread.MAX_PRIORITY);
          
      • 说明

        • 线程创建时继承父线程的优先级
        • 低优先级只是获得调度的概率低,并非一定是高优先级线程之后才被调用

    多线程创建,方式二:实现Runnable接口

      1. 创建一个实现了Runnable接口的类
      2. 实现类去实现Runnable中的抽象方法: run()
      3. 创建实现类对象
      4. 将此对象作为参数传递到Thread类的构造器中,创建Thread类对象
      5. 通过Thread类的对象调用 start()
    package 继承Thread;
    
    public class ThreadTest extends Thread{
    
        @Override
        public void run() {
            // 线程休眠 1秒
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            String odd = "";
            for (int i = 1; i <= 100; ++ i) {
                if (1 != (i & 1)) {
                    odd = odd + i + " ";
                }
            }
            System.out.println("1~100的偶数有:" + odd);
        }
        public static void main(String[] args) {
            ThreadTest t1 = new ThreadTest();
            t1.start();
            String even = "";
            for (int i = 1; i <= 100; ++ i) {
                if (1 == (i & 1)) {
                    even += i + " ";
                }
            }
            System.out.println("1~100的奇数为:" + even);
        }
    
    }
    

    多个线程共享一个静态资源(无锁)

    模拟买票,因为每个线程都有一个虚拟内存,当读到共享资源的时候,将共享资源复制到它虚拟内存内进行处理,然后将结构又赋值给当前的共享资源,这样会导致,当两个线程同时读到相同的票,让后造成重票问题, 当然如果没有出现网络延迟的话,或者处理速度非常快,一般发现不了。

    package 实现Runnable;
    
    public class WindowStore {
        public void main(String[] args){
            Window window = new Window();
    
            new Thread(window).start();
            new Thread(window).start();
            new Thread(window).start();
    
        }
    }
    
    class Window implements Runnable{
        private int tickets = 100;
    
        @Override
        public void run() {
            while (true) {
                if (tickets > 0) {
                    try {
                        Thread.sleep(100); // 模拟网络延时
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " 购买了第" + tickets-- + "票");
                }
            }
        }
    }
    
    
    

    在这里插入图片描述

    比较上面两种方式

    开发中: 优先选择:实现Runnable接口的方式。

    原因: 1. 实现的方式没有类的单继承性的局限性(java中类是不支持多继承,而且当前要使用多线程的类,可能需要继承它的父类,所以用实现方式更方便)。

    ​ 2.实现的方式更适合用来处理多个线程共享数据的情况(实现方式,是将同一个对象,当成参数传入到多个线程中,保证了数据的唯一,二继承的方式必须声明多个对象)。

    线程的分类

    Java 中的线程分为两类:一种是 守护线程, 一种是 用户线程

    • 它们在几乎每个方面都是相同的,唯一的区别是判断JVM何时离开。
    • 守护线程 是用来服务用户线程的,通过在 start() 方法前调用(thread.setDaemon(true))可以把一个用户线程变成一个守护线程。
    • Java 垃圾回收是一个典型的守护线程。
    • 若Jvm中都是守护线程,当前的jvm将退出。
    • 形象理解:兔死狗烹,鸟尽弓藏。

    创建多线程,方式三:实现Callable接口

    使用步骤

    • 创建一个实现的Callable的实现类
    • 实现call()方法,将此线程需要执行的操作声明在call()中
    • 创建Callable接口的实现类的对象
    • 将此Callable接口的实现类对象作为参数,传递到FutureTask构造器中,创建FutureTask的对象
    • 将FutureTask对象作为参数,传递到Thread类的构造器中,创建Thread对象,并start()
    • 获取Callablecall方法的返回值
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    // 创建一个实现的**Callable**的实现类
    public class NumTread implements Callable {
        // 实现**call**()方法,将此线程需要执行的操作声明在**call**()中
        @Override
        public Object call() throws Exception {
            int sum = 0;
            for (int i = 1; i <= 100; i++) {
                if (i % 2 == 0) sum += i;
            }
            return sum;
        }
    
        public static void main(String[] args) {
            // 创建**Callable**接口的实现类的对象
            NumTread numTread = new NumTread();
    		//将此**Callable**接口的实现类对象作为参数,传递到FutureTask构造器中,创建**FutureTask**的对象
            FutureTask<Integer> futureTask = new FutureTask<Integer>(numTread);
    		//将FutureTask对象作为参数,传递到**Thread**类的构造器中,创建**Thread**对象,并**start**()
            new Thread(futureTask).start();
            try {
                // get返回值就时Callable实现类的重写的对象。
                //获取**Callable**中**call**方法的返回值
                Integer sum = futureTask.get();
                System.out.println(sum);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
    
        }
    }
    
    
    • 与使用Runnable相比,Callable功能更强大些
      • 相比于run()方法,可以有返回值
      • call()方法可以抛出异常
      • 支持泛型返回值
      • 需要借助FutureTask类,比如获取返回结果

    创建多线程,方式四:使用线程池

    背景: 经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能的影响很大。

    思路: 提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回线程池中。可以避免频繁的创建和销毁、实现重复利用。类似生活中的公共交通工具。

    优点:

    • 提高响应速度(减少了创建新线程的时间)
    • 降低资源的消耗(重复利用线程池中的线程,不需要每次都创建)
    • 便于线程管理
      • corePoolSize: 设置线程池核心池的大小
      • maximumPoolSize:设置最大的线程数
      • keepAliveTime: 线程池中的线程没有任务时最多保持多少时间后会终止

    使用步骤

    • 提供指定数量的线程池

      ExecutorService service = Executors.newFixedThreadPool(10);// 能存十个线程的线程池
      
    • 执行指定的线程操作。需要提供实现的Runnable接口,或提供实现的Callable接口的实现类对象

      // Runnable实现类的对象,使用execute
      service.execute(new NumberThread());
      // Callable实现类的对象使用,submit
      service.submit(Callable callable);
      
    • 关闭线程池

      service.shutdown();
      

    测试代码

    import java.util.concurrent.Executor;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ThreadPoolExecutor;
    
    class NumberThread implements Runnable {
    
        @Override
        public void run() {
            for (int i = 1; i <= 100; i++) {
                if (i % 2 == 0) {
                    System.out.println(Thread.currentThread().getName() + ": " + i);
                }
            }
        }
    }
    
    class NumberThread1 implements Runnable {
    
        @Override
        public void run() {
            for (int i = 1; i <= 100; i++) {
                if (i % 2 != 0) {
                    System.out.println(Thread.currentThread().getName() + ": " + i);
                }
            }
        }
    }
    
    
    public class PoolThread {
        public static void main(String[] args) {
            ExecutorService service = Executors.newFixedThreadPool(10);
            ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
    
            //体现线程池的管理
            //service1.setCorePoolSize(10);
            
            // 一个线程遍历100以内的偶数,一个遍历一百以内的奇数
            service.execute(new NumberThread()); // 适用于Runnable
            service.execute(new NumberThread1()); // 适用于Runnable
            //service.submit();// 适用于Callable
    
            service.shutdown();// 关闭线程池
        }
    }
    
    

    创建多线程方式有4种

    追求吾之所爱
  • 相关阅读:
    sessionStorage 、localStorage 和 cookie 之间的区别 及共同点:
    [转]css文件和js文件后面带一个问号
    Asp.net中动态设置标题Title,Keyword,Descripton标签的方法
    【11.7校内测试】【倒计时3天】【不想写题解】
    【洛谷】1600:天天爱跑步【LCA】【开桶】【容斥】【推式子】
    【11.6校内测试】【倒计时4天】【找规律?思维】【二分+倍增】
    【洛谷】3960:列队【Splay】
    CF733F Drivers Dissatisfaction【链剖】【最小生成树应用】
    【11.5校内测试】【倒计时5天】【DP】【二分+贪心check】【推式子化简+线段树】
    【11.2晚校内测试】【装桶模拟】【单调栈】
  • 原文地址:https://www.cnblogs.com/rstz/p/14390978.html
Copyright © 2011-2022 走看看