【Java】多线程(1) java.thread---线程的创建

1. 什么是线程?

线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。

1.1 多任务和多线程

  • 多任务(multitasking),这是操作系统的一种能力,看起来可以同时运行多个程序。例如您可以同时运行多个应用程序(下载电影,玩游戏、听歌)。并发执行的进程数并不受限于CPU的数目。CPU会给每个进程分配时间片段,给人并行处理的感觉。

  • 多线程程序在更低一层扩展了多任务的概念:单个程序看起来在同时完成多个任务。每个任务在一个线程( thread)中执行,线程是控制线程的简称。如果一个程序可以同时运行多个线程,则称这个程序是多线程的( multithreaded)。

1.2 进程与线程(Process与Thread)

本质的区别在于每个进程都拥有自己的一整套变量,而线程则共享数据。

  • 地址空间:

线程共享本进程的地址空间,而进程之间是独立的地址空间。

  • 资源:

线程共享本进程的资源如内存、I/O、cpu等,不利于资源的管理和保护,而进程之间的资源是独立的,能很好的进行资源管理和保护。

  • 健壮性:

多进程要比多线程健壮,一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。

  • 执行过程:

每个独立的进程有一个程序运行的入口、顺序执行序列和程序入口,执行开销大。

但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制,执行开销小。

  • 可并发性:

两者均可并发执行。

  • 切换时:

进程切换时,消耗的资源大,效率高。所以涉及到频繁的切换时,使用线程要好于进程。同样如果要求同时进行并且又要共享某些变量的并发操作,只能用线程不能用进程。

  • 其他:

线程是处理器调度的基本单位,但是进程不是。

2 线程的创建(Thread、Runnable、Callable)

2.1 Thread方法创建

创建线程方式一:

继承thread类,

重写run()方法,

调用start()开启线程。

public class Threadtest1 extends Thread{
    @Override
    public void run() {
        //run方法线程体
        for(int i=0;i<20;i++){
            System.out.print("R"+i+" ");
        }
    }

    public static void main(String[] args) {
        //main线程主线程
        //创建一个线程对象
        Threadtest1 threadtest1 = new Threadtest1();
        //调用start方法开启线程
        threadtest1.start();
        for (int i=0;i<30;i++){
            System.out.print("M"+i+" ");
        }
    }

}

结果:

M0 M1 M2 M3 M4 M5 M6 M7 M8 M9 M10 M11 M12 M13 M14 M15 M16 M17 M18 M19 M20 M21 M22 M23 M24 M25 M26 M27 M28 M29 R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 
Process finished with exit code 0

线程不一定立即执行,由CPU调度安排

2.2 Runnable方法创建

创建线程方式二:

实现runnable方法,

重写run()方法,

执行线程丢入runnable接口实现类,

调用start方法。

public class ThreadTest3_Runable implements Runnable{

    @Override
    public void run() {
        for (int i=0;i<10;i++){
            System.out.print("R"+i+" ");
        }
    }

    public static void main(String[] args) {
        ThreadTest3_Runable t = new ThreadTest3_Runable();
        //创建线程对象,通过线程对象来开启我们的线程
        new Thread(t).start();
        for (int i =0;i<10;i++){
            System.out.print("M"+i+" ");
        }

    }
}

2.3 Callable方法创建

不常用,用到了线程的池化。

2.3.1 Executor

执行者工厂方法

  • newCachedThreadPool方法构造一个线程池,会立即执行各个任务,如果有空闲线程可用,就使用现有空闲线程执行任务;

  • 如果没有可用的空闲线程,则创建一个新线程newfixedThreadPool方法构造一个具有固定大小的线程池。

  • 如果提交的任务数多于空闲线程数,就把未得到服务的任务放到队列中。当其他任务完成以后再运行这些排队的任务。newSinoleThreadExecutor是一个退化了的大小为1的线程池.由一个线程顺序地执行所提交的任务(一个接着一个执行)。

​ 这3个方法返回实现了ExecutorService 接口的ThreadPolExecutor类的对象。

​ 如果线程生存期很短,或者大量时间都在阻塞,那么可以使用一个缓存线程池。不过,如果线程工作量很大而且并不阻塞,你肯定不希望运行太多线程。
​ 为了得到最优的运行速度,并发线程数等于处理器内核数。在这种情况下,就应当使用固定线程池,即并发线程总数有一个上限。

2.3.2 Callable接口

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

实现Callable方法,

重写cell()方法,

创建执行服务 ExecutorService 构造一个线程池,

提交执行 Future,

获取结果 ,.get(),

关闭服务 ser.shutdownNow();

Callable的好处
1、可以定义返回值。
2、可以抛出异常。
public class ThreadTest_Callable implements Callable {
    private String url;//图片地址
    private String name;//图片名

    public  ThreadTest_Callable(String url,String name){
        this.name = name;
        this.url = url;
    }
    //下载体
    @Override
    public Boolean call() {
       WebDownload downloadPicture = new WebDownload();
        downloadPicture.downloader(url,name);
        System.out.println("下载图片"+name);
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ThreadTest_Callable t1 = new ThreadTest_Callable("http://img0.dili360.com/pic/2021/04/30/608bc6f1691c03g55454690.jpg","1.jpg");
        ThreadTest_Callable t2 = new ThreadTest_Callable("http://img0.dili360.com//ga/M00/49/A2/wKgBy1nPGuOACFBbAABCTSwHDZQ733.jpg","2.jpg");
        ThreadTest_Callable t3 = new ThreadTest_Callable("http://img0.dili360.com//ga/M02/49/A2/wKgBy1nPDXGAWgvpAADIK-c2-PQ056.jpg","3.jpg");
        //创建执行服务
        ExecutorService ser = Executors.newFixedThreadPool(3);
        //提交执行
        Future<Boolean> r1 = ser.submit(t1);
        Future<Boolean> r2 = ser.submit(t2);
        Future<Boolean> r3 = ser.submit(t3);
        //获取结果
        boolean rs1 = r1.get();
        boolean rs2 = r2.get();
        boolean rs3 = r3.get();

        System.out.println(rs1);
        System.out.println(rs2);
        System.out.println(rs3);
        //关闭服务
        ser.shutdownNow();
    }
}
//利用commomsIO.jar
//下载器
class WebDownload{
    public void downloader(String url,String name){
        try{
            FileUtils.copyURLToFile(new URL(url), new File(name));
        }catch (IOException e){
            e.printStackTrace();
            System.out.println("下载图片异常");
        }
    }
}

2.4 基于线程池的execute(),创建临时线程

线程创建方式四:可以利用缓存策略使用线程池来创建线程

创建线程池

调用线程池的execute()方法

采用匿名内部类的方法,创建Runnable对象,并重写run()方法

public class EThread {
    public static void main(String[] args) {
        //创建线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(10);
        for(int i = 0;i<10;i++) {
            //调用execute()方法创建线程
            //采用匿名内部类的方法,创建Runnable对象,并重写run()方法
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());   
                }
            });
        }
    }
}

2.5 总结

1、 继承Thread类

  • 子类继承Thread类具备多线程能力

  • 启动线程:子类对象.start()

  • 不建议使用:避免OOP单继承局限性

2、 实现Runnable接口

  • 实现接口Runnable具有多线程能力
  • 启动线程:传入目标对象+Thread对象.start()
  • 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用

3、 实现Callable接口

  • 实现接口Runnable具有多线程能力
  • 启动线程:构建线程池,提交执行,获取结果,关闭服务。
  • 不常用,有返回值,可以抛出异常。

4、 基于线程池的execute()

  • 利用线程池创建临时线程
  • 调用线程池的execute()方法,采用匿名内部类的方法,创建Runnable对象,并重写run()方法。
  • 使用线程池可以不用线程的时候放回线程池,用的时候再从线程池取。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值