线程创建的四种方法

线程创建的四种方法:

创建线程的常用四种方法:

第一种   继承Thread类

第二种  实现Runnable接口

第三种  实现Callable接口

第四种  通过线程池如用Executor框架


创建线程的常用四种方法:

  1.  继承Thread类
  2. 实现Runnable接口
  3. 实现Callable接口
  4. 线程池方法创建如用Executor框架

        通过继承Thread类或者Runnable接口、Callable接口都可以实现多线程,不过实现Runnable接口与实现Callable接口的方式基本相同,只是Callable接口里定义的方法有返回值,可以声明抛出异常而已。

 采用实现Runnable、Callable接口的方式创建线程的优缺点:

    优点:线程类只是实现了Runnable或者Callable接口,还可以继承其他类。这种方式下,多个线程可以共享一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好的体现了面向对象的思想。

    缺点:编程稍微复杂一些,如果需要当我当前该线程,则必须使用Thread.currentThread()方法

采用继承Thread类的方法创建线程的优缺点:

     优点:编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获取当前线程

     缺点:因为线程类已经继承了Thread类,Java语言是单继承的,所以不能再继承其他父类。

采用线程池创建线程的优点:

      1、提高响应速度----减少了创建新线程的时间

      2、降低资源消耗----重复利用线程池中线程,不需要每次都创建

      3、便于线程管理

第一种   继承Thread类

通过继承Thread类来创建并启动多线程的一般步骤如下

1、定义Thread类的子类,并重写该类的run()方法,该方法的方法体就是线程需要完成的任务,run()方法也称为线程执行体。

2、创建Thread子类的实例,也就是创建了线程对象

3、启动线程,即调用线程的start()方法

//1. 创建一个继承于Thread类的子类
class MyThread extends Thread{
   // 2. 重写Tread类的run()
    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            if(i%2==0){
               // System.out.println(i);
                //输出线程名
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }
}
public class ThreadTest {
    public static void main(String[] args) {
        //3. 创建Tread类的子类的对象
        MyThread t = new MyThread();
        //4. 通过此对象调用start(): A :启动当前线程    B: 调用当前线程的run()方法
        t.start();
       // 第一个问题:  这是调方法 还是在主线程中执行的  我们不能直接调用run()的方式启动线程
       // t.run();
        //第二个问题:再启动一个线程 不可以让已经start()了的线程去执行 不然会报错IllegalThreadStateException
      //  t.start();
        MyThread t2 = new MyThread();
        t2.start();
        // 证明线程有交互性
        for (int j = 0; j < 50; j++) {
            if(j%2!=0){
                System.out.println(Thread.currentThread().getName()+":"+j+"*****");
            }
        }
        //创建Thread类的匿名子类的方式
        new Thread(){
            @Override
            public void run() {
                for (int k = 0; k <10 ; k++) {
                    System.out.println(Thread.currentThread().getName()+": LP");
                }
            }
        }.start();
    }
}

运行部分截图:

第二种  实现Runnable接口

通过实现Runnable接口创建并启动线程一般步骤如下:

1、定义Runnable接口的实现类,一样要重写run()方法,这个run()方法和Thread中的run()方法一样是线程的执行体

2、创建Runnable实现类的实例,并用这个实例作为Thread的target来创建Thread对象,这个Thread对象才是真正的线程对象

3、第三部依然是通过调用线程对象的start()方法来启动线程

// 1、创建一个类实现Runnable接口
class Number implements Runnable{
    // 2. 重写Runnable接口的run()
    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            if(i%2==0){
                // System.out.println(i);
                //输出线程名
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }
}
public class ThreadRunnable {
    public static void main(String[] args) {
        // 3、创建实现Runnable接口类的对象
        Number number=new Number();
        // 4、将对象作为Thread类的参数
        Thread thread=new Thread(number,"线程1");
        thread.start();
    }
}

运行结果部分截图:

第三种  实现Callable接口

1、创建Callable接口的实现类,并实现call()方法,然后创建该实现类的实例(从java8开始可以直接使用Lambda表达式创建Callable对象)。

2、使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值

3、使用FutureTask对象作为Thread对象的target创建并启动线程(因为FutureTask实现了Runnable接口)

4、调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

class cal implements Callable{
    private int num=0;
    @Override
    public Integer call() throws Exception {
        for (int i = 0; i <100 ; i++) {
            if(i%2==0){
                // System.out.println(i);
                //输出线程名
                System.out.println(Thread.currentThread().getName()+":"+i);
                num+=i;
            }
        }
        return num;
    }
}

public class CallableThread {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 将接口Callable实现类的对象作为FutureTask的参数
        FutureTask futureTask=new FutureTask(new cal());
        // 将FutureTask对象作为Thread的参数
        new Thread(futureTask, "窗口1").start();
        // 还可以获取Callable中call方法的返回值
        System.out.println("num的值:"+futureTask.get());
    }
}

运行结果部分截图:

第四种  通过线程池如用Executor框架

        1.5后引入的Executor框架的最大优点是把任务的提交和执行解耦。要执行任务的人只需把Task描述清楚,然后提交即可。这个Task是怎么被执行的,被谁执行的,什么时候执行的,提交的人就不用关心了。具体点讲,提交一个Callable对象给ExecutorService(如最常用的线程池ThreadPoolExecutor),将得到一个Future对象,调用Future对象的get方法等待执行结果就好了。Executor框架的内部使用了线程池机制,它在java.util.cocurrent 包下,通过该框架来控制线程的启动、执行和关闭,可以简化并发编程的操作。因此,在Java 5之后,通过Executor来启动线程比使用Thread的start方法更好,除了更易管理,效率更好(用线程池实现,节约开销)外,还有关键的一点:有助于避免this逃逸问题——如果我们在构造器中启动一个线程,因为另一个任务可能会在构造器结束之前开始执行,此时可能会访问到初始化了一半的对象用Executor在构造器中。


/** 1、继承Thread类创建线程
 *  1) 定义Thread类的子类,并重写该类的run()方法,该方法的方法体就是线程需要完成的任务,run方法也称为线程执行体。
 *  2) 创建Thread子类的实例,也就是创建了线程对象
 *  3) 启动线程,调用线程的start()方法
 */
class MyThread1 extends Thread{
    @Override
    public void run() {
        // 重写run方法
        System.out.println(Thread.currentThread().getName()+"你开始拉屎?...\t"+new Date());
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"你结束拉屎?...\t"+new Date());
    }
}


/** 2、实现Runnable接口创建线程
 *  1) 定义Runnable接口的实现类,一样要重写run()方法,这个run()方法和Thread中的run()方法一样是线程的执行体
 *  2) 创建Runnable实现类的实例,并用这个实例作为Thread的target来创建Thread对象,这个Thread对象才是真正的线程对象
 *  3) 第三步依然是通过调用线程对象的start()方法来启动线程
 */
class MyThread2 implements Runnable{
    @Override
    public void run() {
        // 重写run方法
        System.out.println(Thread.currentThread().getName()+"你开始吃饭?...\t"+new Date());
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"你结束吃饭?...\t"+new Date());
    }
}

/** 3、实现Callable接口创建线程
 *
 */
class MyThread3 implements Callable {
    private Integer sum=0;

    @Override
    public Integer call() throws Exception {
        for(int i=0;i<=100;i++){
            Thread.sleep(100);
            System.out.println(Thread.currentThread().getName()+"--->"+i);
            sum+=i;
        }
        return sum;
    }
}
public class ThreadPool {
    public static void main(String[] args) {
        // 1、提供指定的线程数量的线程池
        ExecutorService executorService= Executors.newFixedThreadPool(10);

//        ThreadPoolExecutor service=(ThreadPoolExecutor) executorService;
//        // 设置线程池的属性
//        service.setCorePoolSize(15);

        executorService.execute(new MyThread1()); // 执行Runnable接口的实现类
        executorService.execute(new MyThread2()); // 执行Runnable接口的实现类
        Future submit = executorService.submit(new MyThread3());// 执行Callable接口的实现列
        try {
            System.out.println(submit.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
// 关闭线程
        executorService.shutdown();
    }
}

 运行结果部分截图:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值