Java多线程——多线程的四种实现方式

Java多线程——多线程的四种实现方式

前言:

  本篇文章有几个必要重要的知识点

   1、启动线程的注意事项

   2、继承Thread类与实现Runnable接口的关系

   3、Callable与Thread、Runnable的关系

一、直接继承Thread,覆写run()方法

		class MyThread1 extends Thread{
		    private String title;
		
		    public MyThread1(String title){
		        this.title = title;
		    }
		
		    @Override
		    public void run() {
		        for (int i = 0;i<10;i++) {
		            System.out.println(title+":"+i);
		        }
		    }
		}

  这里覆写的run()方法相当于主线程的main()方法,是一个线程的入口方法。

  启动线程的注意事项(具体流程请参考:线程启动函数start()源码解析)

   1、无论是哪种方式实现多线程,线程启动一定调用Thread类提供的start()方法<而不是run()方法>

   2、线程start()方法只能调用一次,再次调用就会抛出线程非法状态(启动)异常(java.lang.IllegalThreadStateException)

   3、start()方法的具体流程:start(判断当前线程是不是首次创建,Java方法)->调用start0()方法(JVM)->通过JVM进行资源调度,系统分配->回调run()方法(Java方法)执行线程的具体操作任务。

   4、run()方法与start()的区别:如果线程直接调用run()方法,当且仅当run()只是一个普通方法;如果线程是通过start()方法(JVM回调)调用的run()方法,则可以实现线程的开启。

二、实现Runnable接口,覆写run()方法

  由于在Java语言中,类具有单继承局限,因此实现Runnable接口可以使得:多个线程共享一个Runnable对象,通过实现Runnable接口的方式实现多线程更能体现出“共享”的概念(创建一个Runnable接口对象,并传入到多个new Thread(Runnable)中)

		class MyRunnable implements Runnable{
		
		    private String title;
		    private Integer ticket = 10;
		
		    public MyRunnable(String title){
		        this.title = title;
		    }
		
		    @Override
		    public void run() {
		        for (int i=0;i<10;i++) {
		            System.out.println(this.title+"卖了一张票,还剩"+(ticket--)+"张票");
		        }
		    }
		}

  最终一定要使用start()来开启线程——通过Thread(Runnable),此时的Runnable可以使用匿名内部类/lambda表达式(只有一个run()方法)

		// 调用已存在的Runnable对象启动一个线程
		new Thread(runnable).start();
	
		// 使用匿名内部类启动一个线程
		new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        }).start();

		// 使用lambda表达式启动一个线程(非常简便)
		new Thread(()-> System.out.println(Thread.currentThread().getName())).start();

  继承Thread类与实现Runnable接口的关系

Java多线程——多线程的四种实现方式_第1张图片

   1、继承具有单继承局限,而实现接口不限个数。

   2、Thread类与自定义线程类(实现了Runnable接口),是一个典型的代理设计模式,Thread类负责辅助真实业务操作(资源调度,创建线程并启动),自定义线程类负责真实业务的实现(run方法具体要做啥事)

   3、使用Runnable接口实现的多线程程序类可以更好的描述共享的概念。多个线程可以共同享用一个Runnable对象。

三、实现Callable接口,覆写call()方法

   当线程有返回值时,只能通过实现Callable实现多线程 ,Callable接口在juc(java.util.concurrent)包下。

			// 覆写call()方法,而不是run()方法可
			V call() throws Exception;

  举个栗子

	class MyCallable implements Callable{
	
    	private int ticket = 10;
    	
	    @Override
	    public String call() throws Exception {
	        while(this.ticket>0){
	            System.out.println(Thread.currentThread().getName()+",剩余票数:"+this.ticket--);
	        }
	        return "票卖完了";
	    }
	}

  Callable接口与Thread、Runnable的关系

Java多线程——多线程的四种实现方式_第2张图片

   从图中可以看到

   ① Future接口中get()方法的作用是: 取得Callable接口的返回值。

   ②RunnableFuture接口即继承了Runnable接口又继承了Future接口(接口的多继承),因此RunnableFuture接口拥有所继承的所有方法。

   ③ FutureTask(Callable实现多线程的核心类)即是Runnable的子类,又是RunnableFuture的子类,又可以传入Callable(FutureTask的构造方法)——连通三者,因此FutureTask的作用为:

    a)将Callable转为FutureTask
    b)将FutureTask转为Thread(因为FutureTask间接实现了Runnable接口,相当于是Runnable)
    c)使用Thread的start()方法启动线程——兜兜转转启动线程还是只能使用Thread的start()方法

   因此,使用Callable接口的方式启动线程变为了:

	public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 先取得Callable对象
        MyCallable myCallable = new MyCallable();
        
        // 将Callable对象转换为FutureTask对象
        FutureTask futureTask = new FutureTask<>(myCallable);
        
        // 由于FutureTask对象实现了RunnableFuture接口,
        // 而RunnableFuture接口又是Runnable接口的子接口,
        // 因此可以将FutureTask对象当做Runnable接口的实现类传给Thread
        Thread thread1 = new Thread(futureTask);
        Thread thread2 = new Thread(futureTask);
        
        // 一切的一切,最终的最终,开启线程还得是Thread的start方法
        thread1.start();
        thread2.start();

		// 打印线程的返回值
        // 由于FutureTask间接实现了Future接口
        // 因此可以使用FutureTask的get()方法获取线程的返回值
        System.out.println(futureTask.get());
    }

	// 打印结果如下:
	Thread-0,剩余票数:10
	Thread-0,剩余票数:9
	Thread-0,剩余票数:8
	Thread-0,剩余票数:7
	Thread-0,剩余票数:6
	Thread-0,剩余票数:5
	Thread-0,剩余票数:4
	Thread-0,剩余票数:3
	Thread-0,剩余票数:2
	Thread-0,剩余票数:1
	票卖完了              // 线程的返回值

四、线程池

 线程池及Executor框架

你可能感兴趣的:(Java多线程,面试题总结)