多线程(认识和创建)

什么线程:一个程序内部的一条执行路径。简单的说就是在做事情

什么是单线程:程序内部只有一条单独的执行路径。简单的说就是只做一件事情

什么是多线程:从软硬件上实现多条执行流程的路径。简单的说就是同时做多件事情

线程可以减少队列阻塞带来的影响,提高CPU的利用率

进程与线程

进程:正在运行的程序(软件)就是一个独立的进程

线程:线程是属于进程的,一个进程中可以同时运行很多个线程

如何创建多线程

一、继承 Thread

  • 定义一个子类继承线程类java.lang.Thread,重写run()方法
  • 创建子类的对象
  • 调用子类对象的start()方法启动线程(底层会自动去执行run方法)
public class Demo1 {
    public static void main(String[] args) {
        //需求:创建两个线程,分别用于打印10个A和10个B,最后观察下输出顺序
        A a = new A();
        B b = new B();
        a.start();
        a.setName("线程A");
        b.start();
        b.setName("线程B");
        //main线程
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}
class A extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            //Thread.currentThread()   ---->获取当前线程对象
            System.out.println(Thread.currentThread().getName()+":" + i);
        }
    }
}
class B extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            //Thread.currentThread()   ---->获取当前线程对象
            System.out.println(Thread.currentThread().getName()+":" + i);
        }
    }
}

二、实现 Runnable 接口

  • 定义一个线程任务类实现Runnable接口,重写run()方法
  • 创建任务类对象
  • 把任务类对象交给Thread处理
    • public Thread(Runnable target)
  • 调用线程对象的start()方法启动线程
public class Demo1 {
    public static void main(String[] args) {
        //需求:创建两个线程,分别用于打印10个A和10个B,最后观察下输出顺序
        A a = new A();
        B b = new B();
        Thread At = new Thread(a,"A**");
        Thread Bt = new Thread(b,"B**");
        At.start();
        Bt.start();
        //Thread Ct = new Thread(new Runnable(){});
        //可使用Lambda实现,他是一个函数式接口
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName()+":C"+i);
            }
        },"C**").start();

    }
}
class A implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+":A"+i);
        }
    }
}
class B implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+":B"+i);
        }
    }
}

三、实现 Callable<T> 接口

  • 创建任务对象
    • 定义一个类实现Callable接口,重写call()方法,封装要做的事情,和要返回的数据
    • Callable类型的对象封装成FutureTask(线程任务对象)
      • public FutureTask<>(Callable call)Callable对象封装成FutureTask对象
      • public V get() throws Exception 获取线程执行call()方法返回的结果
  • 把线程任务对象交给Thread对象。
  • 调用Thread对象的start()方法启动线程。
  • 线程执行完毕后,可以通过FutureTask对象的的get()方法去获取线程任务执行的结果
public class Demo1 {
    public static void main(String[] args) throws Exception {
        //需求:启动两个子线程,分别计算100之内的奇数的和和偶数的和,然后在主线程中再做个汇总,得到总和
        A a = new A();
        B b = new B();
        FutureTask<Integer> f1 = new FutureTask<>(a);
        FutureTask<Integer> f2 = new FutureTask<>(b);

        Thread t1 = new Thread(f1, "A");
        Thread t2 = new Thread(f2, "B");

        t1.start();
        t2.start();
        //  FutureTask获取结果时 在无结果返回时,会一直等待结果
        // 如果有多个线程的返回值在一起使用,只有当最后一个返回值返回之后才会在去执行
        Integer i1 = f1.get();
        Integer i2 = f2.get();
        System.out.println("总和:"+i1+i2);
    }
}
class A implements Callable<Integer>{
    @Override
    public Integer call() throws Exception {
        int sum=0;
        for (int i = 0; i <= 100; i+=2) {
            sum+=i;
        }
        return sum;
    }
}
class B implements Callable<Integer>{
    @Override
    public Integer call() throws Exception {
        int sum=0;
        for (int i = 1; i <= 100; i+=2) {
            sum+=i;
        }
        Thread.sleep(3000);
        System.out.println("睡醒了,给你");
        return sum;

    }
}

上面三种实现方式的优缺点

Thread、Runnable、Callable<T> 的优缺点

Thread:编码简单,但它是继承的,无法继承其他类,不利于功能的扩展。

Runnable:虽然它是使用接口的,但在使用时会多出一个对象

Callable<T>:是一个接口,扩展性强;执行完毕后可以获得线程的返回结果,但编码复杂一些

方式

优点

缺点

继承Thread类

编程比较简单,可以直接使用Thread类中的方法

扩展性较差,不能再继承其他的类,不能返回线程执行的结果

实现Runnable接口

扩展性强,实现该接口的同时还可以继承其他的类。

编程相对复杂,不能返回线程执行的结果

实现Callable接口

扩展性强,实现该接口的同时还可以继承其他的类。可以得到线程执行的结果

编程相对复杂

四、使用线程池创建线程

此处为语雀内容卡片,点击链接查看:https://www.yuque.com/mohuizhu/hz562v/isdfzg05vu693i8q

常用方法

Thread提供的常用方法

说明

public void run()

线程的任务方法

public void start()

启动线程

public String getName()

获取当前线程的名称,线程名称默认是Thread-索引

public void setName(String name)

为线程设置名称

public static Thread currentThread()

获取当前执行的线程对象

public static void sleep(long time)

让当前执行的线程休眠多少毫秒后,再继续执行

public final void join()...

让调用当前这个方法的线程先执行完!

Thread提供的常见构造器

说明

public Thread(String name)

可以为当前线程指定名称

public Thread(Runnable target)

封装Runnable对象成为线程对象

public Thread(Runnable target, String name)

封装Runnable对象成为线程对象,并指定线程名称

使用线程需要注意的事项

  • ThreadRunnable重写run()方法后是使用 Thread中的start()方法启动
  • start()方法底层是一个本地方法,它底层会调用run()方法。
  • 如果直接调用run(),那么他就是一个普通方法了。
  • 主线任务应该放在start()之后,如过不是,它将会先执行完主线任务后才会开启其他线程
  • join()让调用当前这个方法的线程先执行完,当同时又A、B两个线程,在B线程使用A线程中的join()方法,那么在A线程抢到CPU后才会先执行A线程
public class Demo1 {

    //需求: 通过下面任务,我们来学习sleep和join方法
    //1. 创建A、B两个线程类,分别打印1~5
    //2. 在主线程序中创建A、B两个子线程
    //3. 在主线程序中开启A、B两个子线程
    //4. 让A线程每打印一次,都要暂停3秒钟
    //5. B线程要等A线程打印完毕,再开始打印
    public static void main(String[] args) throws InterruptedException {
        A a = new A();
        B b = new B();
        Thread t1 = new Thread(a);
        Thread t2 = new Thread(b);
        t1.start();
        t2.start();
        t1.join();//抢到CPU后,先执行完成
    }

}
class A extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("A"+i);
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
class B extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("B"+i);
        }
    }
}

并发与并行

并发 : CPU分时轮询的执行线程

  • CPU同时处理线程的数量有限
  • CPU会轮询为系统的每个线程服务,由于CPU切换的速度很快,给我们的感觉这些线程在同时执行,这就是并发.

并行 : 同一个时刻同时在执行

  • 在同一个时刻上,同时有多个线程在被CPU处理并执行。(多核CPU才会出现并行现象)
  • 21
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值