创建多线程的四种方式

一、继承Thread类

步骤

  1. 定义一个类继承Thread类
  2. 重写run方法:里面写线程要运行的任务代码
  3. 创建Thread子类对象
  4. 调用start方法:开启线程并调用run方法

package com.zzq.thread;

public class MyThread {

	public static void main(String[] args) {

		// 创建Thread的子类对象,创建线程。
		Demo d1 = new Demo("张三");
		Demo d2 = new Demo("李四");
		d1.start(); // 开启线程,编号从0开始;
		d2.start();

		for (int i = 0; i < 10; i++) {
			System.out.println("主函数:" + Thread.currentThread().getName() + "...i=" + i);
		}
	}
}

// 定义一个类继承Thread
class Demo extends Thread {

	private String name;

	Demo(String name) {
		this.name = name;
	}

	@Override
	public void run() {
		show();
	}

	public void show() {
		for (int i = 0; i < 10; i++) {
			System.out.println(name + ":" + Thread.currentThread().getName() + "...i=" + i);
		}
	}
}

输出结果

主函数:main...i=0
李四:Thread-1...i=0
张三:Thread-0...i=0
李四:Thread-1...i=1
主函数:main...i=1
李四:Thread-1...i=2
李四:Thread-1...i=3
李四:Thread-1...i=4
李四:Thread-1...i=5
李四:Thread-1...i=6
张三:Thread-0...i=1
张三:Thread-0...i=2
张三:Thread-0...i=3
李四:Thread-1...i=7
主函数:main...i=2
李四:Thread-1...i=8
张三:Thread-0...i=4
张三:Thread-0...i=5
李四:Thread-1...i=9
主函数:main...i=3
主函数:main...i=4
主函数:main...i=5
主函数:main...i=6
主函数:main...i=7
主函数:main...i=8
主函数:main...i=9
张三:Thread-0...i=6
张三:Thread-0...i=7
张三:Thread-0...i=8
张三:Thread-0...i=9

二、实现Runnable接口

步骤

  1. 定义子类实现Runnable接口
  2. 子类中重写run方法:将线程的任务代码封装到run方法中;
  3. 创建实现子类的对象
  4. 通过Thread类创建线程对象,并将该子类对象作为构造器的参数进行传递
  5. 调用Thread类的start方法

package com.zzq.thread;

public class MyRunnable {

	public static void main(String[] args) {

		// 3.创建实现类对象
		Demo2 d = new Demo2();
		// 4.通过Thread类创建线程对象
		Thread t1 = new Thread(d);
		// 5. 调用start方法
		t1.start();

		for (int i = 0; i < 10; i++) {
			System.out.println("主函数:" + Thread.currentThread().getName() + "...i=" + i);
		}
	}
}

// 1. 定义子类实现Runnable接口
class Demo2 implements Runnable {

	// 2、子类中重写run方法:将线程的任务代码封装到run方法中;
	@Override
	public void run() {
		show();
	}

	public void show() {
		for (int i = 0; i < 10; i++) {
			System.out.println(Thread.currentThread().getName() + "...i=" + i);
		}
	}
}

输出结果

主函数:main...i=0
Thread-0...i=0
主函数:main...i=1
Thread-0...i=1
主函数:main...i=2
Thread-0...i=2
主函数:main...i=3
Thread-0...i=3
主函数:main...i=4
主函数:main...i=5
Thread-0...i=4
主函数:main...i=6
Thread-0...i=5
主函数:main...i=7
Thread-0...i=6
主函数:main...i=8
Thread-0...i=7
主函数:main...i=9
Thread-0...i=8
Thread-0...i=9

三、实现Callable接口

步骤

  1. 创建Callable的实现类
  2. 重写call方法,将线程的任务代码封装到call方法中
  3. 创建Callable接口实现子类的对象;
  4. 创建FutureTask的对象,将此Callable接口实现类的对象作为构造器的参数进行传递
  5. 创建Thread对象,并调用start()。将FutureTask的对象作为参数传递到Thread类的构造器中
  6. 获取Callable中call方法的返回值。 get() 返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值

package com.zzq.thread;

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

public class MyCallable {

	public static void main(String[] args) {
		// 3、创建Callable接口实现子类的对象
		Demo3 d = new Demo3();
		// 4、创建FutureTask的对象,将此Callable接口实现类的对象作为构造器的参数进行传递
		FutureTask futureTask = new FutureTask(d);
		// 5、创建Thread对象
		Thread thread = new Thread(futureTask);
		thread.start();
		Object sum;
		try {
			//6、获取Callable中call方法的返回值
			sum = futureTask.get();
			System.out.println("总和是:" + sum);
		}
		catch (InterruptedException e) {
			e.printStackTrace();
		}
		catch (ExecutionException e) {
			e.printStackTrace();
		}
	}

}

//1、创建Callable的实现类
class Demo3 implements Callable {

	//2、重写call方法,将线程的任务代码封装到call方法中
	@Override
	public Object call() throws Exception {
		// 遍历1-20打印出偶数之和
		int sum = 0;
		for (int i = 1; i <= 20; i++) {
			if ((i & 1) == 0) { // i%2 == 0
				System.out.println(i);
				sum += i;
			}
		}
		return sum;
	}

}

输出结果

2
4
6
8
10
12
14
16
18
20
总和是:110

四、使用线程池

步骤

  1. 提供指定线程数量的线程池
  2. 执行指定的线程的操作,需要提供实现Runnable接口或Callable接口实现类的对象
    1. Runnable
    2. Callable
  3. 关闭连接池

package com.zzq.thread;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class MyExecutors {

	public static void main(String[] args) {
		//1、提供指定线程数量的线程池
		ExecutorService executors = Executors.newFixedThreadPool(10);
		//2、执行指定的线程的操作,需要提供实现Runnable接口或Callable接口实现类的对象
		Future future = executors.submit(new demo4());// 适用于Callable
		try {
            Thread.sleep(5000);// 先延迟5秒
            System.out.println("输出结果"+future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }finally {
        	 //3. 关闭连接池
        	executors.shutdown();
		}
	}

}

class demo4 implements Callable {

	// 2. 重写call方法,将此线程需要执行的操作声明在call中
	@Override
	public Object call() throws Exception {
		// 遍历1-20打印出奇数之和
		int sum = 0;
		for (int i = 1; i <= 20; i++) {
			if ((i & 1) != 0) {
				System.out.println(i +"   "+ Thread.currentThread().getName());
				sum += i;
			}
		}
		return sum;
	}
}

输出结果

1   pool-1-thread-1
3   pool-1-thread-1
5   pool-1-thread-1
7   pool-1-thread-1
9   pool-1-thread-1
11   pool-1-thread-1
13   pool-1-thread-1
15   pool-1-thread-1
17   pool-1-thread-1
19   pool-1-thread-1
输出结果100

总结

1、start()负责启动线程,什么时候执行由CPU时间片分配调度,run()是线程具体执行的方法。

2、Callable比Runnable的优势

call()可以有返回值,返回值通过 FutureTask 进行封装。通过future的get方法获取返回值
call()可以抛出异常,被外面的操作捕获,获取异常的信息;
Callable支持泛型

3、Thread类和Runnable接口都要 都需要重写run()方法

4、线程池好处

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

原文: https://www.cnblogs.com/benjieqiang/p/11376076.html

你可能感兴趣的:(JAVA)