020.day20 线程概述 多线程优缺点 线程的创建 线程常用方法 生命周期 多线程同步...

多线程

一、线程概述

1. 进程

正在执行的应用程序(java.exe),一个可执行的程序一次运行的过程

  • 独立性:不同进程之间相互独立
  • 动态性:是一直活动的
  • 并发性:多个进程可以在单个处理器上同时运行

    2. 线程

    线程是程序中的一个执行流,每个线程都有自己的专有寄存器,但代码区是共享的,即不同的线程可以执行同样的函数

  • 各线程在运行过程中可能会出现资源竞争
  • CPU在不同的线程之间切换
  • 每个线程的执行机会相对随机

    3. 进程与线程

  • 一个线程只能属于一个进程
  • 一个进程可以有多个线程,至少有一个主线程
  • 同一进程的所有线程共享系统分配给该进程的资源
  • 线程是指进程内的一个执行单元,也是进程内的可调度实体
  • 系统创建进程时需要重新分配资源,而创建线程相对容易,因此效率更高

    4. 多线程

    多线程是指程序中包含多个执行流,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务

5. 主线程

任何一个Java程序启动时,一个线程立刻运行,执行main方法(程序的入口),这个线程称为程序的主线程

  • 主线程是产生其它子线程的线程
  • 通常必须最后关闭,因为它要执行其它子线程的关闭工作

    二、多线程优缺点

    (1)优点

  • Java支持编写多线程程序
  • 可以并发执行多个任务
  • 最大限度的减少CPU的闲置时间,提高CPU的利用率

    (2)缺点

  • 线程越多占用内存越多
  • 线程之间对共享资源访问会互相影响
  • 线程过多会导致控制复杂化

    三、线程的创建

    Thread类也实现了Runnable接口

1. 继承Thread类

Thread类中的run方法本身并不执行任何操作,需要继承Thread类后重写该方法

  • MyThread
// TODO 线程实现实现方式:继承Thread类
        System.out.println(111);
        // 1.声明一个定义的线程对象 - 父类引用指向子类对象
        Thread thread1 = new MyThread();
        thread1.setName("子线程1");
        // 2.使一个线程进入到就绪状态:start()方法
        // 线程启动后会调用相应的run方法
        thread1.start();
        // 直接调用run方法相当于执行一个普通类下的普通方法
        // 此时程序会等待方法调用完成后按顺序执行
         thread1.run();
        System.out.println(222);
        // 多个子线程在执行时,执行的机会相对随机
        Thread thread2 = new MyThread("子线程2");
        thread2.start();
         thread2.run();
        System.out.println(333);
  • ThreadTest
// 实现线程的方式 - 继承Thread类
public class MyThread extends Thread {
    
    // 显示声明空的构造方法
    public MyThread() {
        
    }
    
    // 子类中调用父类的构造方法
    public MyThread(String name) {
        // 在线程实例化时指定线程名称
        super(name);
    }
    
    // 重写run方法
    public void run() {
        for (int i = 0; i < 10; i++) {
            // Thread.currentThread()获得当前所在线程的对象
            if (i == 5) {
                if (Thread.currentThread().getName().equals("子线程1")) {
                    // 当某一个线程中出现异常时,不会影响另外一个线程
                    System.out.println(1/0);
                }
            }
            System.out.println(Thread.currentThread() + ":" + i);
        }
    }

}
  • 小练习
// TODO 多线程练习:相对独立的线程 - 继承Thread
        // 某个商城进行促销活动,商城内存在多个商家
        // -》类比思想:商场开门 - 主线程启动
        System.out.println("商场开门");
        // 每个商家售卖不同的商品,并有不同的库存
        Thread thread1 = new ShopThread("夏季T恤", 35, "李宁品牌服装店");
        Thread thread2 = new ShopThread("洁面神器", 50, "韩国护肤品牌店");
        Thread thread3 = new ShopThread("kindle", 30, "亚马逊直销店");
        // -》封装实体:能够刻画商家售卖商品以及表示库存
        // 描述该商城当天开门直到所有商家商品售空的过程
        thread1.start();
        thread2.start();
        thread3.start();
        thread1.join();
        thread2.join();
        thread3.join();
        // -》类比思想:商品售空,库存为0时线程正常停止
        // 使用两种方式实现
        System.out.println("商场关门");
// 多家店铺同时售卖商品
public class ShopThread extends Thread {

    public ShopThread() {

    }

    public ShopThread(String productName, Integer count, String name) {
        super(name);
        this.count = count;
        this.productName = productName;
    }

    // 售卖的商品
    private String productName;
    // 商品的库存
    private Integer count;

    public String getProductName() {
        return productName;
    }

    public void setProductName(String productName) {
        this.productName = productName;
    }

    public Integer getCount() {
        return count;
    }

    public void setCount(Integer count) {
        this.count = count;
    }

    // 使用相对随机的方式减少商品的库存
    public void run() {
        for (int i = count; i > 0;) {
            if ((--i) != 0) {
                // 通过线程的name属性刻画商家的名称
                System.out.println(Thread.currentThread().getName() + "卖出一件" + this.productName + ",仅剩" + i + "件");
            } else {
                System.out.println(Thread.currentThread().getName() + "商品已售完,关店");
            }
        }
    }

}

2. 实现Runnable接口

  • MyRunnable
// 自定义类实现Runnable接口
public class MyRunnable implements Runnable {
    // 重写run方法
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            // 获得当前线程的名称
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
}
  • RunnableTest
public class RunnableTest {

    public static void main(String[] args) {
        // 将一个实现了Runnalbe接口的实现类的实例传入
        MyRunnable myRunnable1 = new MyRunnable();
        MyRunnable myRunnable2 = new MyRunnable();
        // 调用相应的构造方法,指定线程名
        Thread thread1 = new Thread(myRunnable1, "线程1");
        // 启动线程
        thread1.start();
        // 调用相应的构造方法,指定线程名      
        Thread thread2 = new Thread(myRunnable2, "线程2");
        // 启动线程
        thread2.start();
    }
}
  • 小练习
// TODO 多线程练习:相对独立的线程 - 实现Runnable
        System.out.println("商场开门");
        // 实例化Runnable接口的对象
        Runnable runnable1 = new ShopRunnable("夏季T恤", 350);
        Runnable runnable2 = new ShopRunnable("洁面神器", 500);
        Runnable runnable3 = new ShopRunnable("kindle", 300);
        // 通过线程控制目标对象
        Thread thread1 = new Thread(runnable1, "李宁品牌服装店");
        Thread thread2 = new Thread(runnable2, "韩国护肤品牌店");
        Thread thread3 = new Thread(runnable3, "亚马逊直销店");
        // 线程启动
        thread1.start();
        thread2.start();
        thread3.start();
        // 可以设置最长等待时长
        // 超过等待时长,继续运行当先线程内容
        // 不指定时长时,等待线程完全执行完毕再执行当前线程
        thread1.join(10);
        thread2.join(10);
        thread3.join(10);
        System.out.println("商场关门");
// 多家店铺同时售卖商品
public class ShopRunnable implements Runnable {

    public ShopRunnable() {
        
    }

    public ShopRunnable(String productName, Integer count) {
        this.count = count;
        this.productName = productName;
    }

    // 售卖的商品
    private String productName;
    // 商品的库存
    private Integer count;

    public String getProductName() {
        return productName;
    }

    public void setProductName(String productName) {
        this.productName = productName;
    }

    public Integer getCount() {
        return count;
    }

    public void setCount(Integer count) {
        this.count = count;
    }
    
    // 使用相对随机的方式减少商品的库存
    public void run() {
        for (int i = count; i > 0;) {
            if ((--i) != 0) {
                // 通过线程的name属性刻画商家的名称
                System.out.println(Thread.currentThread().getName() + "卖出一件" + this.productName + ",仅剩" + i + "件");
            }else {
                System.out.println(Thread.currentThread().getName() + "商品已售完,关店");
            }
        }
    }

}

3. 内部类方式创建

  • ThreadInner
public class ThreadInner {

    public static void main(String[] args) {
        // 使用匿名内部类实现线程执行具体内容
        new Thread("新线程") {
            
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(this.getName() + ":" + i);
                }
            }
        // 启动线程
        }.start();
    }
}
  • RunnableInner
public class RunnableInner {
    
    public static void main(String[] args) {
        // 实例化线程
        new Thread(new Runnable() {
            
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.currentThread().getName() + ":" + i);
                }
            }
        // 启动线程
        }, "新线程").start();
    }
}

四、线程常用方法

1. 设置线程名称

  • setName(String name)
  • Thread(String name)

    2. 设置线程休眠

  • sleep(long millis):进入阻塞状态,时间单位为毫秒

    3. 设置线程优先级

默认情况下线程的优先级与创建它的父线程具有相同的优先级,提升抢到时间片的概率,从而获得更多的执行机会

  • setPriority(int newPriority):参数范围为1-10,需要在执行start()方法前设置

    4. 等待某一线程终止

  • join():先执行start()方法,再执行join方法,当前线程执行完毕后,会继续执行其它线程

    5. 后台线程

    也称为守护线程或用户线程,如JVM的垃圾回收线程,必须在start()前执行,如果所有的前台线程都死亡,则会自动死亡

  • setDaemon(boolean on)

    五、生命周期

    线程启动后进入到就绪状态,多个线程在同时运行时,不断的在争抢CPU的时间片

1422552-20180808192058742-1150889869.png

  • 新生:实例化完成
  • 就绪:准备运行,暂时没有分配到时间片
  • 运行:分配到时间片,执行相关任务
  • 阻塞:由于某些特殊情况导致阻塞,会在合适的机会重新进入就绪状态
  • 死亡:线程结束(可以使用stop()方法强制结束)

    六、多线程同步

    把竞争访问的资源标识为private
    使用synchronized(同步的)关键字同步方法或代码块

  • Ticket
public class Ticket implements Runnable {

    // 使用private定义竞争资源
    private int ticket = 100;

    @Override
    public void run() {
        while (true) {
            // 对当前线程进行同步加锁
            synchronized (this) {
                ticket--;
                // 当车票售空时跳出循环
                if (ticket < 0) {
                    break;
                }
                System.out.println(Thread.currentThread().getName() + "购买,当前剩余票数:" + ticket);
            }
        }
    }
}
  • TicketTest
public class TicketTest {
    // 模拟两名旅客的抢票
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        Thread thread1 = new Thread(ticket,"旅客1");
        Thread thread2 = new Thread(ticket,"旅客2");
        thread1.start();
        thread2.start();
    }
}

转载于:https://www.cnblogs.com/yokii/p/9445151.html

表情包
插入表情
评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符
相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页