简介
程序:即program,它是一个特定的任务,用某一程序语言编写的一组指令集合,是一段静态的代码。
进程:即process,它是一次执行的过程,或称为正在运行的一个程序,是一个动态的过程,有一整从创建到销毁的生命周期。
线程:即thread,它是进程的细化,是程序内部的一条自执行路径;有以下特点:
- 一个进程可以有多个线程。
- 一个进程同一时间并行执行多个线程,就是支持多线程。
- 线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小。
- 一个进程中的多个线程共享相同的内存单元/内存地址空间,它们从同一个堆中分配对象;可以访问相同的变量和对象,这就使得线程间的通信简便、高效,多个线程操作共享的系统资源可能会带来安全隐患。
单核cpu:是一种假的多线程,在同一个时间单元内,它只能自执行一个线程的任务,通过切换线程来执行不多线程的任务,因为cpu时间切换短所以感觉不出来。
多核cpu:cpu多核能同时运行的线程数较单核更多,有利于同时运行多个程序。执行速度不同,更加流畅。
并行:多个cpu同时执行多个任务。
并发:一个cpu(采用时间切片)同时执行多个任务。
线程创建
通过继承Thread类,然后重写run方法创建
package org.example;
import java.text.DateFormat;
class PrimeThread extends Thread {
long mainPrime;
PrimeThread(long mainPrime) {
this.mainPrime = mainPrime;
}
@Override
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName()+"run..." + DateFormat.getDateTimeInstance().format(System.currentTimeMillis()));
try {
Thread.sleep(mainPrime);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
public class ThreadMain{
public static void main(String[] args) {
PrimeThread primeThread = new PrimeThread(2000);
primeThread.start();
}
}
start方法的作用:开始执行当前线程,java虚拟机当前线程的run方法。
run方法的作用:线程类的run方法是Runnable接口的一个抽象方法,由java虚拟机直接调用的,不会创建的新线程。
start()方法和run()方法的区别:start方法在java.lang.Thread类中定义;而run方法在java.lang.Runnable接口中定义,必须在实现类中重写。
线程的创建过程:当调用start方法时,程序会创建一个新的线程,然后由java虚拟机直接调用run方法,如果直接调用run方法,则不会创建线程,而是一个普通的方法调用。
实现Runnable接口
package org.example;
import java.text.DateFormat;
class RunnableThread implements Runnable {
long mainPrime;
RunnableThread(long mainPrime){
this.mainPrime=mainPrime;
}
@Override
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName()+"run..." + DateFormat.getDateTimeInstance().format(System.currentTimeMillis()));
try {
Thread.sleep(mainPrime);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
public class RunnableMain {
public static void main(String[] args) {
new Thread(new RunnableThread(2000)).start();
}
}
使用实现Runnable接口好处:可以将线程(Thread)和线程任务(RunnableThread)分离。
实现Callable接口
package org.example;
import java.text.DateFormat;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
class CallableThread implements Callable {
long mainPrime;
CallableThread(long mainPrime) {
this.mainPrime = mainPrime;
}
@Override
public Object call() throws Exception {
while (true) {
System.out.println(Thread.currentThread().getName()+"run..." + DateFormat.getDateTimeInstance().format(System.currentTimeMillis()));
try {
Thread.sleep(mainPrime);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
public class CallableMain {
public static void main(String[] args) {
CallableThread callableThread = new CallableThread(2000);
//将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
FutureTask futureTask = new FutureTask<>(callableThread);
//将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
new Thread(futureTask).start();
}
}
FutureTask:FutureTask类实现了RunnableFuture接口,而RunnnableFuture接口继承了Runnable和Future接口,所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。FutureTask可以用来包装Callable或者Runnbale对象。因为FutureTask实现了Runnable接口,所以FutureTask也可以被提交给Executor。
线程中的常用方法
start:开始执行当前线程,java虚拟机当前线程的run方法。
run:线程类的run方法是Runnable接口的一个抽象方法,由java虚拟机直接调用的,不会创建的新线程。
setName:设置线程名字,必须在线程启动前设置,例如:
Thread thread = new Thread(futureTask);
thread.setName("thread1");
thread.start();
getName:获取当前线程的名称,也可以通过线程的构造方法设置,例如:
PrimeThread primeThread = new PrimeThread(2000);
primeThread.setName("thread2");
primeThread.start();
System.out.println(primeThread.getName());
getId:获取当前线程的id,例如:
PrimeThread primeThread = new PrimeThread(2000);
primeThread.setName("thread2");
primeThread.start();
System.out.println(primeThread.getId());
currentThread:获取当前线程的native静态方法,通过该方法会返回一个线程对象,源代码:
public static native Thread currentThread();