多线程之四种创建方式

1前言:没有前言
2 不得不提的一个类——Thread
2.1Thread类的特性

  • 每个线程都是通过某个特定Thread对象的run()方法来完成操作的,经常把run()方法的主体称为线程体。
  • 通过该Thread对象的start()方法来启动这个线程,而非直接调用run()

2.2Thread类的构造器

  • Thread():创建新的Thread对象
  • Thread(String threadname):创建线程并指定线程实例名
  • Thread(Runnable target):指定创建线程的目标对象,它实现了Runnable接口中的run方法
  • Thread(Runnable target, String name):创建新的Thread对象

2.3Thread类的有关方法

  • void start(): 启动线程,并执行对象的run()方法

  • run(): 线程在被调度时执行的操作

  • String getName(): 返回线程的名称

  • void setName(String name):设置该线程名称

  • static Thread currentThread(): 返回当前线程。也就是获取运行该方法的线Thread.currentThread();

  • static void yield():线程让步。解释说明:暂停当前正在执行的线程对象,并执行其他线程。在多线程的情况下,由CPU决定执行哪一个线程,而yield()方法就是暂停当前的线程,让给其他线程(包括它自己)执行,具体由谁执行由CPU决定。

  • join() :等待调用join()方法的线程执行结束,程序才会继续执行下去,这个方法适用于:一个执行程序必须等待另一个线程的执行结果才能够继续运行的情况。
    注意:join()方法只有在start()方法之后才可以生效。

  • static void sleep(long millis):(指定时间:毫秒) ①令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行,时间到后重排队。②抛出InterruptedException异常。③使用在同步代码块中不释放锁标记

  • stop(): 强制线程生命期结束,不推荐使用

  • boolean isAlive(): 返回boolean,判断线程是否还活着

3创建线程的4中方式
3.1 学习创建线程的方式前必须注意的地方:

  • 如果自己手动调用run()方法,那么就只是普通方法,没有启动多线程模式。

  • run()方法由JVM调用,什么时候调用,执行的过程控制都有操作系统的CPU
    调度决定。

  • 想要启动多线程,必须调用start方法。

  • 一个线程对象只能调用一次start()方法启动,如果重复调用了,则将抛出以上
    的异常“IllegalThreadStateException”。

3.2 方式一:继承Thread类

  • 定义子类继承Thread类。
  • 子类中重写Thread类中的run方法。
  • 创建Thread子类对象,即创建了线程对象。
  • 调用线程对象start方法:启动线程,调用run方法。
//类1
public class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println(this.getName()+i);
            try {
                MyThread.sleep(1000);

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
 }2
 public class MyThread2 extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println(this.getName()+i);
            try {
                MyThread2.sleep(1000);
                System.out.println("睡了一秒");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

//运行
  public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.setName("我是继承了Thread类的线程");
        myThread.start();
        MyThread2 myThread2 = new MyThread2();
        myThread2.setName("我是继承了Thread类的线程二");
        myThread2.start();
        System.out.println("主线程结束"+ Thread.currentThread() );
    }

方式二:实现Runnable接口

  • 定义子类,实现Runnable接口。
  • 子类中重写Runnable接口中的run方法。
  • 通过Thread类含参构造器创建线程对象。
  • 将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中。
  • 调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法。
//类1
public class MyRunnable2 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+i);
        }
      
    }
}

//类2
public class MyRunnable implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+i);
        }

    }

    public static void main(String[] args) {
        //对两个实现类分别启动一个线程
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.setName("我是实现runnable接口创建的线程");
        thread.start();

        MyRunnable2 myRunnable2 = new MyRunnable2();
        Thread thread2 = new Thread(myRunnable2);
        thread2.setName("我是实现runnable接口创建的线程二");
        thread2.start();
        //对一个实现类启动两个线程

        MyRunnable myRunnable3 = new MyRunnable();
        Thread thread3 = new Thread(myRunnable3);
        thread3.setName("我是实现runnable接口创建的线程三");
        Thread thread4 = new Thread(myRunnable3);
        thread4.setName("我是实现runnable接口创建的线程四");
        thread3.start();
        thread4.start();
       //用匿名内部类的形式实现多线程
        Thread thread5 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println(Thread.currentThread().getName() + i);
                }
            }
        },"我是实现runnable接口创建的线程五");
        thread5.start();


      //用lambada表达式形式实现的多线程
        new Thread(()->{
            for (int i = 0; i < 100; i++) {
                  System.out.println(Thread.currentThread().getName() + i);
                }
        },"我是实现runnable接口创建的线程六").start();

    }
}

方式三:实现Callable接口

 Future接口

  • 可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等。
  • FutrueTask是Futrue接口的唯一的实现类
  • FutureTask 同时实现了Runnable, Future接口。它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值
public class MyCallable implements Callable {
    @Override
    public Object call() throws Exception {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+i);
        }
        return "实现Callable接口的实现类完成多线程执行完毕";
    }


    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyCallable myCallable = new MyCallable();
        FutureTask<String> result = new FutureTask<String>(myCallable);
        Thread thread = new Thread(result);
        thread.setName("实现Callable接口实现多线程");
        thread.start();
        //等所有线程执行完,获取值
        String s = result.get();
        System.out.println("s = " + s);
        //返回值:s = 实现Callable接口的实现类完成多线程执行完毕
    }
}

方式四:使用线程池
JDK 5.0起提供了线程池相关API:ExecutorService 和 Executors
 ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor

  • void execute(Runnable command) :执行任务/命令,没有返回值,一般用来执行Runnable
  • Future submit(Callable task):执行任务,有返回值,一般又来执行Callable
  • void shutdown() :关闭连接池

 Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池

  • Executors.newCachedThreadPool():创建一个可根据需要创建新线程的线程池
  • Executors.newFixedThreadPool(n); 创建一个可重用固定线程数的线程池
  • Executors.newSingleThreadExecutor() :创建一个只有一个线程的线程池
  • Executors.newScheduledThreadPool(n):创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
public class MyThreadPool {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //没有返回值的线程池1
        ExecutorService service = Executors.newFixedThreadPool(5);
        service.execute(new MyRunnable());
        service.execute(new MyRunnable());
        service.shutdown();
        //没有返回值的线程池2
        ExecutorService service1 = Executors.newFixedThreadPool(5);
        service1.execute(new MyRunnable());
        service1.execute(new MyRunnable());
        service1.shutdown();


        //有返回值的线程池1
        ExecutorService service3 = Executors.newFixedThreadPool(5);
        Future<?> result1 = service3.submit(new MyCallable());
        Object o = result1.get();
        System.out.println("o = " + o);
        //返回值:o = 实现Callable接口的实现类完成多线程执行完毕
        Future<?> result2 = service3.submit(new MyCallable());
        Object o3 = result2.get();
        System.out.println("o3 = " + o3);
        //返回值:o3 = 实现Callable接口的实现类完成多线程执行完毕
        service3.shutdown();

    }
}

4创建线程方式的对比

  • Runnable 和 callable方式:[推荐]
    优点:①线程类在实现Runnable/callable的前提下还可以去继承其他类.更加面向对象
    ②Callable 是JDK1.5版本出来的.JDK1.8加强[Lamdba/函数式接口],Runbale JDK1.8也支持函数式编程
    ③Runable和Callable更加适合处理共享资源的情况
    劣势:编程稍稍麻烦一下.而且获取当前线程需要使用Thread类的静态方法currentThread()方法

  • Thread 方式
    优点: 编程简单.可以直接获取当前线程
    劣势:java单继承.无法继承其他类.有局限性

  • 使用线程池
    优点:①提高响应速度(减少了创建新线程的时间)
    ②降低资源消耗(重复利用线程池中线程,不需要每次都创建)
    ③便于线程管理
    缺点缺点

  • github源码

  • 完结

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值