多线程

2-创建线程的4中方式

2021-07-07 473 0

简介 继承Thread类、实现Runnable接口、实现Callable接口、线程池4种方式都可以创新线程。

JDK8文档中的描述:

There are two ways to create a new thread of execution.

One is to declare a class to be a subclass of Thread. This subclass should override the run method of class Thread. An instance of the subclass can then be allocated and started.


1. 创建线程

    多线程的创建,方式一:继承于Thread

     1. 创建一个继承于Thread类的子类

     2. 重写Thread类的run() --> 将此线程执行的操作声明在run()

     3. 创建Thread类的子类的对象

     4. 通过此对象调用start()  Causes this thread to begin execution; the Java Virtual Machine calls the run method of this thread. (调用子类中重写的run方法)

        4.1 易错点: 直接调用run()方法,并没有创建新线程,还是主线程 main 执行

        4.2 重复 start, 报异常 IllegalThreadStateException 需要重新创建一个线程的对象,重新start(),才可以再启动一个线程

   

 

//CreateThread.java
package com.ylaihui.thread;
class ProcessNumber extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if( i % 5 == 0)
                System.out.println(i);
        }
    }
}

 

public class CreateThread {
    public static void main(String[] args) {
        ProcessNumber processNumber = new ProcessNumber();
        processNumber.start();// 子线程执行 run() 方法中的代码
        // 主线程执行以下代码
        for (int i = 100; i < 200; i++) {
            if(i % 5 == 0)
                System.out.println(i);
        }
    }
}


 

2. 匿名子类匿名对象的方式创建线程


//CreateThreadSubclass.java
package com.ylaihui.thread;

 

public class CreateThreadSubclass {
    public static void main(String[] args) {
        new Thread(){
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        }.start();

 

        new Thread(){
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        }.start();

 

        System.out.println(Thread.currentThread().getName());
    }
}


3. Thread类中的常用方法

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

    

    run(): 线程在被调度时执行的操作,子类继承Thread类,并重写run方法

    

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

    

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

    

    public static native Thread currentThread(); 返回当前线程。在Thread子类中就是this,通常用于主线程和Runnable实现类

    

    static void yield(): 线程让步,让当前线程释放CPU执行权,让另外的线程执行。可能让位不成功,继续执行当前线程

      暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程

      若队列中没有同优先级的线程,忽略此方法

    

    join() : 当某个程序执行流中调用其他线程的 join() 方法时, 调用线程将被阻塞,直到 join() 方法加入的线程执行完为止

      在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b完全执行完以后,线程a才结束阻塞状态。

      低优先级的线程也可以获得执行

    

    static void sleep(long millis): (指定时间:毫秒)

      令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行,时间到后重排队。

    当某线程A处于Sleep状态时,另一个线程B调用了B.interrupt()方法,打断了A的Sleep过程,则A的Sleep会抛出异常InterruptedException。使用Catch后,线程不会等待Sleep时间,而是会立即执行。Thread.interrupt()方法不会中断一个正在运行的线程。它的作用是,在线程阻塞时抛出一个中断信号,这样线程就得以退出阻塞的状态。更确切的说,如果线程被Object.wait,Thread.join和Thread.sleep三种方法之一阻塞,那么,它将接收到一个中断异常(InterruptedException),从而提早地终结被阻塞状态。

    

    stop(): 强制线程生命期结束,不推荐使用,已经过时,不再使用。

    

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

    

    upfile



upfile


4. 创建线程方式二-实现Runnable接口

    The other way to create a thread is to declare a class that implements the Runnable interface. That class then implements the run method. An instance of the class can then be allocated, passed as an argument when creating Thread, and started. The same example in this other style looks like the following:

     创建多线程的方式二:实现Runnable接口

     1. 创建一个实现了Runnable接口的类

     2. 实现类去实现Runnable中的抽象方法:run()

     3. 创建实现类的对象

     4. 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象

     5. 通过Thread类的对象调用start()

    

    

//CreateThreadRunnableInf.java
package com.ylaihui.thread;

 

// 1. 创建一个实现了Runnable接口的类
class ThreadR implements Runnable {
    //2. 实现类去实现Runnable中的抽象方法:run()
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

 

public class CreateThreadRunnableInf {
    public static void main(String[] args) {
        // 3. 创建实现类的对象
        ThreadR t1 = new ThreadR();

 

        // 4. 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
        Thread t2 = new Thread(t1);
        Thread t3 = new Thread(t1);

 

        // 5. 通过Thread类的对象调用start()
        t2.start();  // 调用了线程Thread类中的target类的run方法
        t3.start();
    }
}


5. 创建线程的方式的比较

比较创建线程的两种方式。

开发中:优先选择:实现Runnable接口的方式

原因:1. 实现的方式没有类的单继承性的局限性,如果使用继承Thread类的方式,那么功能类不能再继承其他类。

     2. 实现的方式更适合来处理多个线程有共享数据的情况。

联系:public class Thread implements Runnable, Thread 类本身也是继承了Runnable接口,两种实现方式其实原理都相同

相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。


6. Callable接口方式创建线程

    与使用Runnable相比, Callable功能更强大些

    相比run()方法,可以有返回值

    方法可以抛出异常

    支持泛型的返回值

    需要借助FutureTask类,比如获取返回结果

    

    Future接口

    可以对具体Runnable、 Callable任务的执行结果进行取消、查询是否完成、获取结果等。

    FutrueTask是Futrue接口的唯一的实现类

    FutureTask 同时实现了Runnable, Future接口。它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值

    1.创建一个实现Callable的实现类

    2.实现call方法,将此线程需要执行的操作声明在call()

    3.创建Callable接口实现类的对象

    4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象

    5.FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()

    6.获取Callablecall方法的返回值


//CallableTest.java
package com.ylaihui.thread1;

 

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

 

class ProcNumber implements Callable{

 

    @Override
    public Object call() throws Exception {
        int sum = 0;
        for (int i = 0; i <= 10; i++) {
            sum += i;
        }
        return sum;
    }
}

 

public class CallableTest {
    public static void main(String[] args) {
        ProcNumber procNumber = new ProcNumber();
        FutureTask futureTask = new FutureTask(procNumber);

 

        new Thread(futureTask).start();

 

        try {
            System.out.println(futureTask.get());
        catch (InterruptedException e) {
            e.printStackTrace();
        catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}


7.使用线程池

 背景: 经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。

 思路: 提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。

 好处:

 提高响应速度(减少了创建新线程的时间)

 降低资源消耗(重复利用线程池中线程,不需要每次都创建,每次都销毁)

 便于线程管理


    1. corePoolSize:核心池的大小

    2. maximumPoolSize:最大线程数

    3. keepAliveTime:线程没有任务时最多保持多长时间后会终止

     

    JDK 5.0起提供了线程池相关API: ExecutorService 和 Executors

     ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor

     void execute(Runnable command) :执行任务/命令,没有返回值,一般用来执行Runnable

     <T> Future<T> submit(Callable<T> task):执行任务,有返回值,一般又来执行Callable

     void shutdown() :关闭连接池

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

     Executors.newCachedThreadPool():创建一个可根据需要创建新线程的线程池

     Executors.newFixedThreadPool(n); 创建一个可重用固定线程数的线程池

     Executors.newSingleThreadExecutor() :创建一个只有一个线程的线程池

     Executors.newScheduledThreadPool(n):创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。

    

    步骤:

    1. 提供指定线程数量的线程池

    设置线程属性

    2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象

    Runnable execute

    Callable submit

    3.关闭连接池


//ThreadPoolTest.java
package com.ylaihui.thread1;
 
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
 
class SumThread implements Runnable{
 
    @Override
    public void run() {
        int sum = 0;
        for (int i = 1; i <=10; i++) {
            sum += i;
        }
        System.out.println(sum);
    }
}
 
public class ThreadPoolTest {
    public static void main(String[] args) {
        // 创建线程池
        ExecutorService pool = Executors.newFixedThreadPool(10);
        System.out.println(pool.getClass());
        ThreadPoolExecutor p = (ThreadPoolExecutor) pool;
//        p.setCorePoolSize(20);
//        p.setKeepAliveTime();
//        p.setMaximumPoolSize(30);
        pool.execute(new SumThread()); // Runnable接口方式
 
//        pool.submit(); Callable接口方式
        pool.shutdown();
 
    }
}


点赞 0

文章评论

欢迎您:

纸上得来终觉浅,绝知此事要躬行!

112 文章 36780 浏览 3 评论

联系我

  •   QQ:    361352119
  •  Email:  lisimmy@sina.com
  • 微信: