进入正题前先了解两个名词,进程和线程。
进程:可以理解为一个应用程序,具有独立的存储空间。
线程:可称之为轻量级进程,占用资源小存在于进程中。所有线程共享进程公共内存。
总之进程和线程是(可以帮你完成某项任务的)两种基本单元。Java并发编程主要还是从线程出发。
什么是多线程
每个Java应用中都至少包含一个线程(main线程)。程序中的多个线程是“同时”被操作,从而提高运行效率的。对于单核CPU来说“同时”只是假象,在同一时间点有且仅有一个线程可以被执行。操作系统通过切分CPU时间,使得不同的线程在不同的时间切片上得到/失去执行权。
多线程技术的优点
1.线程相对于进程来说更加轻量,线程创建花费更少的资源和时间
2.线程间共享所属进程的公共内存
3.线程间上下文切换较进程有明显的优势
4.线程间的访问较进程更加简便
(线程的优点同时也是掌握它的难点,由于内存共享我们需要线程安全机制来确保多线程间的数据同步,关于这一部分我们后续再讨论)
关于线程创建API提供了两种实现方式
1.实现java.lang.Runnable接口
2.继承父类java.lang.Thread
实现Runnable接口
我们可以通过封装runnable类来创建线程。而runnable类是通过实现java.lang.Runnable接口以及它的run方法获得的。当线程被调用时,run方法被执行。下面是实现Runnable接口的例子。
public class HeavyWorkRunnable implements Runnable {
@Override
public void run() {
System.out.println("Doing heavy processing - START "+Thread.currentThread().getName());
try {
Thread.sleep(1000);
//Get database connection, delete unused data from DB
doDBProcessing();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Doing heavy processing - END "+Thread.currentThread().getName());
}
private void doDBProcessing() throws InterruptedException {
Thread.sleep(5000);
}
}
继承Thread父类
我们可以继承java.lang.Thread父类并重写run方法来创建线程。当线程被调用时,run方法被执行。下面是继承Thread父类创建线程的例子。
public class MyThread extends Thread {
public MyThread(String name) {
super(name);
}
@Override
public void run() {
System.out.println("MyThread - START "+Thread.currentThread().getName());
try {
Thread.sleep(1000);
//Get database connection, delete unused data from DB
doDBProcessing();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("MyThread - END "+Thread.currentThread().getName());
}
private void doDBProcessing() throws InterruptedException {
Thread.sleep(5000);
}
}
测试类如下
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("Starting Runnable threads");
t1.start();
t2.start();
System.out.println("Runnable Threads has been started");
Thread t3 = new MyThread("t3");
Thread t4 = new MyThread("t4");
System.out.println("Starting MyThreads");
t3.start();
t4.start();
System.out.println("MyThreads has been started");
}
}
输出结果为
Starting Runnable threads
Runnable Threads has been started
Doing heavy processing - START t1
Doing heavy processing - START t2
Starting MyThreads
MyThread - START Thread-0
MyThreads has been started
MyThread - START Thread-1
Doing heavy processing - END t2
MyThread - END Thread-1
MyThread - END Thread-0
Doing heavy processing - END t1
当线程进入可执行状态(调用start方法)后,线程的执行顺序就由操作系统决定,当被分配到CPU时间时线程进入执行状态。尽管我们可以设定线程的优先度,但是它不总是管用(线程调度并不归JVM管理)。所以当我们多次运行上面的测试类时,线程起止的顺序不是确定不变的。
Runnable VS Thread
如果你仅仅想创建一个线程去执行某项简单的任务你可以选择继承Thread父类。如果你想让程序更具有扩展性,执行更加多功能的任务你应该选择实现Runnable接口。
上面的话怎么理解呢,我们都知道Java API中一个类仅能继承一个父类,但是却可以实现复数个接口。个人更推荐通过实现Runnable接口的方式来创建线程。
学习笔记3中我们将探讨线程休眠的概念。