Java线程实例教程
Java线程示例
欢迎来到Java线程示例。进程和线程是执行的两个基本单位。并发编程更关注于Java线程。
进程
一个进程是一个自包含的执行环境,可以看作是一个程序或应用。然而,一个程序本身就包含多个进程。Java运行环境作为一个单独的进程运行,其中包含不同的类和作为进程的程序。
线程
线程可以被称为轻量级进程。线程创建所需的资源较少,并存在于进程中,线程共享进程资源。
Java 线程示例
每个Java应用程序至少有一个线程,即主线程。虽然还有许多其他Java线程在后台运行,如内存管理、系统管理、信号处理等。但从应用程序的角度来看,主线程是第一个Java线程,我们可以从它创建多个线程。多线程指的是一个程序中同时执行两个或多个线程。单核处理器只能同时执行一个线程,而时间片是操作系统的特性,用于在不同的进程和线程之间共享处理器时间。
Java线程的优点
- 和进程相比,Java线程更轻量级,创建线程所需的时间和资源较少。
- 线程共享其父进程的数据和代码。
- 在线程间切换的开销通常比进程间的切换小。
- 线程间通信相对较为简便,而进程间通信更为复杂。
Java提供了两种在程序中创建线程的方式:
- 实现java.lang.Runnable接口。
- 继承java.lang.Thread类。
Java 线程例子 – 实现 Runnable 接口
为了使一个类可运行,我们可以实现java.lang.Runnable接口,并在public void run()方法中提供实现。要将这个类用作线程,我们需要通过传递这个可运行类的对象来创建一个Thread对象,然后调用start()方法以在一个独立的线程中执行run()方法。下面是一个通过实现Runnable接口的Java线程示例。
package com.Olivia.threads;
public class HeavyWorkRunnable implements Runnable {
@Override
public void run() {
System.out.println("执行重处理 - 开始 "+Thread.currentThread().getName());
try {
Thread.sleep(1000);
//获取数据库连接,删除数据库中未使用的数据
doDBProcessing();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("执行重处理 - 结束 "+Thread.currentThread().getName());
}
private void doDBProcessing() throws InterruptedException {
Thread.sleep(5000);
}
}
Java线程示例 – 扩展Thread类
我们可以继承java.lang.Thread类来创建自己的java线程类,并重写run()方法。然后我们可以创建它的对象并调用start()方法来执行我们自定义的java线程类的run方法。这里是一个简单的java线程示例,展示了如何扩展Thread类。
package com.Olivia.threads;
public class MyThread extends Thread {
public MyThread(String name) {
super(name);
}
@Override
public void run() {
System.out.println("MyThread - 开始 "+Thread.currentThread().getName());
try {
Thread.sleep(1000);
//获取数据库连接,删除数据库中未使用的数据
doDBProcessing();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("MyThread - 结束 "+Thread.currentThread().getName());
}
private void doDBProcessing() throws InterruptedException {
Thread.sleep(5000);
}
}
这里是一个测试程序,展示了如何创建并执行一个Java线程。
package com.Olivia.threads;
public class ThreadRunExample {
public static void main(String[] args){
Thread t1 = new Thread(new HeavyWorkRunnable(), "t1");
Thread t2 = new Thread(new HeavyWorkRunnable(), "t2");
System.out.println("启动Runnable线程");
t1.start();
t2.start();
System.out.println("Runnable线程已启动");
Thread t3 = new MyThread("t3");
Thread t4 = new MyThread("t4");
System.out.println("启动MyThread线程");
t3.start();
t4.start();
System.out.println("MyThread线程已启动");
}
}
以上Java线程示例程序的输出是:
启动Runnable线程
Runnable线程已启动
执行重处理 - 开始 t1
执行重处理 - 开始 t2
启动MyThread线程
MyThread - 开始 Thread-0
MyThread线程已启动
MyThread - 开始 Thread-1
执行重处理 - 结束 t2
MyThread - 结束 Thread-1
MyThread - 结束 Thread-0
执行重处理 - 结束 t1
一旦我们启动任何一个线程,它的执行取决于操作系统对时间片划分的实现,我们无法控制它们的执行顺序。然而,我们可以设置线程的优先级,但即使如此,也不能保证优先级较高的线程会先被执行。多次运行以上程序,你会发现线程的启动和结束没有任何规律可循。
可运行的 vs 线程
如果你的类除了作为线程运行之外,还提供了更多的功能,那么你应该实现Runnable接口来提供作为线程运行的方式。如果你的类的唯一目标是作为线程运行,那么你可以继承Thread类。实现Runnable接口是首选,因为Java支持实现多个接口。如果你继承Thread类,就不能继承其他类。
提示:正如你已经注意到的,线程没有返回任何值,但是如果我们希望我们的线程执行一些处理然后将结果返回给客户端程序,可以查看Java Callable Future。
更新:从Java 8开始,Runnable是一个函数式接口,我们可以使用Lambda表达式来提供它的实现,而不是使用匿名类。更多详情,请查看Java 8函数式接口。