创建线程的方式
1.继承 Thread 重写 run()方法
public class thread extends Thread {
@Override
public void run() {
System.out.println("== thread run()==");
}
public static void main(String[] args) {
thread thread = new thread();
thread.start();
}
}
2.实现Runnable接口,重写run()方法。
public class runnable implements Runnable {
@Override
public void run() {
System.out.println("==runnable run() =="+Thread.currentThread().getName());
}
public static void main(String[] args) {
runnable run = new runnable();
Thread th = new Thread(run);
th.start();
}
}
3.实现Callable接口,重写call(),有返回值的线程体。
public class callable implements Callable {
@Override
public Object call() throws Exception {
System.out.println("== callable call()==");
return "callable";
}
public static void main(String[] args) {
callable call = new callable();
FutureTask futureTask = new FutureTask(call);
Thread thread = new Thread(futureTask);
thread.start();
try {
Object o = futureTask.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
有返回值的任务必须实现 Callable 接口,无返回值的任务必须 Runnable 接口。执行 Callable 任务后,可以获取一个 Future 的对象,在该对象上调用 get 就可以获取到 Callable 任务 返回的 Object 了,FutureTask 是 实现了RunnableFuture ,RunnableFuture 接口继承了Runnable和Future。
生命周期
线程被创建执行后,不是一启动就进入执行状态,也不是一直处于执行状态。线程的生命周期可以分为五个部分。
创建(new)、就绪(runnable)、运行(running)、阻塞(blocked)、死亡(dead)。当线程启动后,不会一直占有着CPU独自运行,线程会在运行和阻塞中不断切换。
1.创建new
当使用new 来创建一个线程时,该线程处于创建状态,此时由JVM分配内存并初始化成员变量。
2.就绪Runnable
当线程对象调用start()方法后,该线程处于就绪状态,等待调度运行。
3.运行Running
当处于就绪状态的线程获取到CPU,开始执行run()方法的线程体时,该线程处于运行状态。
4.阻塞Blocked
线程因为某种原因放弃CPU使用权,让出时间片,暂时停止运行时,该线程处于阻塞状态。
阻塞情况分为几种:
4.1等待阻塞(wait - 等待队列)
运行的线程执行wait()方法后,该线程会被JVM,放到等待队列中(waitting queue)。
4.2同步阻塞(lock - 锁池)
比如运行的线程在获取同步锁时,若该同步锁被其他线程占用,则该线程会被JVM放到锁池中(lock pool)。
4.3其他阻塞(sleep/join)
运行的线程执行sleep(long)/join()方法时,该线程会被JVM置为阻塞状态。(I/O请求之类的)
5.死亡dead
死亡状态有以下几种情况
5.1正常方法结束
run()/call()方法结束。
5.2异常
抛出未被捕获的Exception or Error
5.3stop
调用该线程的stop()方法。小心给自己弄出个死锁。
sleep 与 wait 区别
1. sleep()方法,属于 Thread 类中的。而 wait()方法,则是属于 Object 类中的。
2. sleep()方法导致了程序暂停执行指定的时间,让出 cpu 该其他线程,但是他的监控状态依然 保持者,当指定的时间到了又会自动恢复运行状态。
3. 在调用 sleep()方法的过程中,线程不会释放对象锁。
4. 而当调用 wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此 对象调用 notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。
start 与 run 区别
1. start()方法来启动线程,真正实现了多线程运行。这时无需等待 run 方法体代码执行完毕, 可以直接继续执行下面的代码。
2. 通过调用 Thread 类的 start()方法来启动一个线程, 这时此线程是处于就绪状态, 并没有运行。
3. 方法 run()称为线程体,它包含了要执行的这个线程的内容,线程就进入了运行状态,开始运 行 run 函数当中的代码。 Run 方法运行结束, 此线程终止。然后 CPU 再调度其它线程。