1、继承Thread类
① 创建一个继承于Thread类的子类
② 重写Thread类的run( )—>将此线程执行的操作声明在run( )中
③ 创建Thread类的子类的对象
④ 通过此对象调用start( )
创建方式:创建多个线程,需要new多个子类对象
public class MyThread extends Thread {
@Override
public void run() {
//遍历一百以内的偶数
for (int i = 0; i <= 100; i++) {
if (i % 2 == 0){
System.out.println(i);
}
}
}
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start();
}
}
线程调度细节
第一步执行start( )
public synchronized void start() {
start0();
}
private native void start0();
第二步 start0是一个本地方法,由jvm机来调用 底层是由C/C++实现
真正实现多线程的效果的是 start0( ) 而不是run( )
start()方法调用了start0( )方法以后,该线程并不会立马执行,只是将该线程变成了可运行状态,具体什么时候执行,取决于CPU统一调度
结论
-
- 运行程序时就相当于开启一个进程,main就是一个主线程
-
- 在main线程中启动一个子线程,main线程不会阻塞,会继续执行,主线程和子线程是交替执行的
-
- 子线程调用start()才是启动线程
-
- 子线程直接调用run(),就是调用一个普通的run方法,执行run方法的是main线程
-
- main会堵塞到这,直到执行完run方法才会继续往下执行
2、实现Runnable接口
①创建一个实现了Runnable接口的类
②实现类去实现Runnable中的抽象方法:run()
③创建实现类的对象
④将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
⑤通过Thread类的对象调用start()
创建方式:创建多个线程,需要new多个Thread类对象
public class MyThread2 implements Runnable{
@Override
public void run(){
System.out.println("hello,thread");
}
public static void main(String[] args) {
MyThread2 myThread1 = new MyThread2();
Thread t1 = new Thread(myThread1);
Thread t2 = new Thread(myThread1);
}
}
优先选择:实现Runnable接口的方式
原因:
1.实现的方式没有类的单继承性的局限性
2. 实现的方式更适合来处理多个线程有共享数据的情况
3、实现Callable接口
- 1.创建一个Callable接口的实现类
- 2.实现call方法,将此线程需要执行的操作声明在call()中
- 3.创建Callable接口实现类的对象
- 4.将此Callable接口实现类的对象作为参数传递到FutureTask构造器中,创造FutureTask的对象
- 5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
public class Thread3 implements Callable<String> {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> futureTask = new FutureTask<String>(new Thread3());
Thread thread = new Thread(futureTask);
thread.start();
String result = futureTask.get();
System.out.println("result = " + result);//result = hello,线程
}
@Override
public String call() {
return "hello,线程";
}
}
创建线程的方式三:实现Callable接口.–>JDK5.0新增
如何理解实现Callable接口的方式创建多线程比实现Runnable接口创建多线程方式强大?
- call()可以有返回值
- call()可以抛出异常,被外面的操作捕获,获取异常的信息
- callable是支持泛型的
4、通过线程池创建线程
class NumberThread implements Runnable{
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
if (i % 2 == 0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
class NumberThread1 implements Runnable{
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
if (i % 2 != 0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
public class ThreadPool {
public static void main(String[] args) {
//1. 提供指定线程数量的线程池
ExecutorService service = Executors.newFixedThreadPool(10);
ThreadPoolExecutor service1 = (ThreadPoolExecutor)service;
//设置线程池的属性
// System.out.println(service.getClass());
// service1.setCorePoolSize(15);
// service1.setKeepAliveTime();
//2. 执行指定的线程的操作.需要提供实现Runnable接口或Callable
service.execute(new NumberThread());//适合使用于Runnable
service.execute(new NumberThread1());//适合使用于Runnable
// service.submit(Callable callable);//适合使用于Callable
//3. 关闭连接池
service.shutdown();
}
}
好处:
- 1.提高响应速度(减少了创建新线程的时间)
- 2.降低资源消耗(重复利用线程池中线程,不需要每次都创建)
- 3.便于线程管理