Java非线程池池线程创建方法

目录

前言

方式一:继承java.lang.Thread类

方式二:实现java.lang.Runnable接口

方式三:通过Callable、FutureTask实现有返回结果的多线程


 

前言

支持多线程是Java语言的一大特点,多线程在实际开发中也有者举足轻重的地位。在说多线程之前先简单提一嘴程序、进程、线程之间的区别。

 

  • 程序:程序即是一段存储在硬盘中的静态代码,等待CPU的调用。

     

  • 进程:程序被调入内存并被CPU执行时便成了进程,可以说进程就是正在运行的程序,是动态的。进程存在生命周期,而且作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域

     

  • 线程:“进程是资源分配的最小单位,线程是CPU调度的最小单位”。进程可进一步细化为线程,是一个程序内部的一条执行路径。若一个进程同一时间并行执行多个线程,就是支持多线程的。每个线程拥有独立的运行栈和程序计数器,但一个进程中的多个线程共享相同的内存单元/内存地址空间。

     

     


 

方式一:继承java.lang.Thread类

 

先来了解一下Thread类常用的几个构造器

//创建新Thread对象
public Thread()
//创建新的Thread对象,并指定线程名
public Thread(String name)
//创建一个新的Thread对象,使其以target作为其运行对象,它实现了Runnable接口中的run方法
public Thread(Runnable target)
//创建一个新的Thread对象,使其以target作为其运行对象,并以指定的名称作为其名称
public Thread(Runnable target, String name)

    

  • 每个线程都是通过某个特定Thread对象的run()方法来执行各种操作的

  • 但只有通过该Thread继承类对象的start()方法才能启动这个线程,而非使用run()方法

 

 

    方式一步骤可以简单概括如下

  1.    创建子类继承Thread类。
  2.   重写Thread类中的run方法。
  3.   创建Thread子类对象,即创建了线程对象。
  4.   启动多线程调用线程对象start方法:启动线程,调用Thread中的run方法进行操作。

 

示例代码:

class MultiThread extends Thread{
    private String str;
    public MultiThread(String str){
        this.str=str;
    }
​
    @Override
    public void run(){//重写Thread类run方法
        for(int i=0;i<10;i++){
            System.out.println(this.str+": "+i);
        }
    }
}

我们通过一个测试类,来测试一下多线程

public class ThreadTest {
    public static void main(String[] args) {
        var thread1 = new MultiThread("线程一");
        var thread2 = new MultiThread("线程二");
        var thread3 = new MultiThread("线程三");
​
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

 

打印结果如下:

 

我们可以清楚的看到不同的线程相互抢占CPU时间片


 

这里我们说一下线程的优先级

 

线程的优先级从 1-10,而且Java中分别定义了以下几个常量:

  1. MAX_PRIORITY:10

  2. NORM_PRIORITY:5

  3. MIN _PRIORITY:1

线程调度上实行的是抢占式(os决定,并非Java决定),对于高优先级获得调度的几率大,也就是说低优先级线程并非一定是在高优先级线程之后才被调用。

 

 


 

方式二:实现java.lang.Runnable接口

 

其实从源码上我们可以看到,Thread类实际上就实现了Runnable接口

 

而Runnable接口中只有一个run方法,而Thread中重写了这个抽象方法,这里体现了设计模式之一的代理模式

 

此时由于我们实现的是Runnable接口,而不再继承Thread父类了,那么对于此时我们定义的 MultiThread 类也就不再支持' start() '这个Thread继承方法

 

但是不使用start()方法是无法启动多线程,所以我们现在就需要用到public Thread(Runnable target) 这个构造方法了。

 

方式二步骤简单概括如下       

  1. 定义子类,实现Runnable接口。

  2. 子类中重写Runnable接口中的run方法。

  3. 通过Thread类含参构造器创建线程对象。

  4. Runnable接口的子类对象作为实际参数传递给Thread类的构造器中。

  5. 调用Thread类的start方法:开启线程,调用run方法执行操作。

 

示例代码:

class MultiThread implements Runnable{
    @Override
    public void run(){
        for(int i=0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+": "+i);
            //通过Thread类静态方法获取当前线程名
        }
    }
}
public class ThreadTest {
    public static void main(String[] args) {
​
        var target = new MultiThread();
        //这里我们就创建了一个Runnable实现类对象
        var thread1 = new Thread(target);
        var thread2 = new Thread(target);
        var thread3 = new Thread(target);
        //通过setName方法为线程命名
        thread1.setName("线程一:");//setName方法设置线程名
        thread2.setName("线程二:");
        thread3.setName("线程三:");
        //启动线程
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

 

结果如下,我们可以看到只需要一个Runnable实现类对象就可以实现多线程

 

 

 


 

方式三:通过Callable、FutureTask实现有返回结果的多线程

 

方式三实在jdk 1.5版本后才出现的,与前两种方式相比,最大的不同就是方式三在线程执行完毕后返回值,其中方式三实现多线程的的核心是Callable接口

 

我们先来瞧一眼源码中怎么写的

 

可以发现Callbale定义的时候可以设置一个泛型,此泛型的类型就是返回数据的类型,这样的的好处是可以避免向下转型所带来的安全隐患。

 

 

Runnable相比Callable中不再有run方法,而是用带返回值的call方法取代,而且API中也清楚地给出了说明:

“ Callable接口与Runnable类似,两者都是为实例可能由另一个线程执行的类而设计的。但是Runnable不会返回结果,也不能抛出选中的异常。”

 

在多线程创建上,方式三也与前两种方法有些不同,需要依赖FutureTask类。

 

FutureTask实现了两个接口:RunnableFuture所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值

 

(实际上FutureTask继承了RunnableFuture接口,RunnableFuture接口继承Runnable和Future,其实它们属于Excutor框架,文档中也说明了FutureTask类Future的一个基本实现类)

 

 

方式三步骤简单概括如下:

  1.     创建一个实现Callable接口的实现类
  2.     实现call方法,将此线程需要执行的操作声明在call()
  3.     创建Callable实现类的对象
  4.     Callable接口实现类的对象作为传递到FutureTask类的构造器中,创建FutureTask类的对象
  5.    FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()方法启动线程
  6.    (可以通过FutureTask的对象' get '方法获取线程中的call的返回值)

 

示例代码:

class MultiThread implements Callable {
    private int sum;
    @Override
    public Object call() throws Exception {
        for(int i=0;i<5;i++) {
            sum+=1;
            System.out.println(Thread.currentThread().getName() + ": " + i);
        }
        return sum;//返回累加和,这里运用了自动装箱,int自动包装成Integer类型
    }
}

 

public class ThreadTest {
    public static void main(String[] args) {
​
        var multiThread = new MultiThread();
        var futureTask1 = new FutureTask(multiThread);
        var futureTask2 = new FutureTask(multiThread);
        var futureTask3 = new FutureTask(multiThread);
​
        //我们创造了三个FutureTask对象
        var thread1 = new Thread(futureTask1);
        var thread2 = new Thread(futureTask2);
        var thread3 = new Thread(futureTask3);
​
        thread1.setName("线程一");
        thread2.setName("线程二");
        thread3.setName("线程三");
        //启动线程
        thread1.start();
        thread2.start();
        thread3.start();
​
        try {
            System.out.println(futureTask1.get());
        }catch ( Exception e ){
            e.printStackTrace();
        }
    }
}

 

与Runnable类似我们只需要创造一个Callable实现类对象,但一个FutureTask对象只能对应个一个线程运行结果如下。


 

实际上所有的多线程代码都是通过运行Threadstart()方法来运行的。因此,不管是扩展Thread类还是实现Runnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的,熟悉Thread类的API是进行多线程编程的基础!

 

 

(ps:实现Runnable接口可以避免java中的单继承问题,而且在共享数据上也会比继承Thread方式方便,而与Runnable相比Callbale又可以返回值)

 

以上就是今天的全部内容啦,小编累了小编要淦饭去了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值