实现多线程的3种方式
(1)继承Thread
package com.threadtest;
public class ThreadNormal extends Thread {
public ThreadNormal(String name) {
super(name);
}
@Override
public void run() {
System.out.println("线程\"" + Thread.currentThread().getName() + "\"开始执行!");
for(int i = 0; i < 100; i++) {
System.out.println(String.format("线程\"%s\"打印的数为:%s ", Thread.currentThread().getName(), i));
}
}
public static void main(String[] args) {
System.out.println("线程\"" + Thread.currentThread().getName() + "\"开始执行!");
ThreadNormal threadNormal = new ThreadNormal("线程1");
threadNormal.start();
for(int i = 0; i < 100; i++) {
System.out.println(String.format("线程\"%s\"打印的数为:%s ", Thread.currentThread().getName(), i));
}
}
}
(2) 实现Runnable接口
package com.threadtest;
public class ThreadByRunnable implements Runnable {
@Override
public void run() {
for(int i = 0; i < 100; i++) {
System.out.println(String.format("线程\"%s\"打印的数为:%s ", Thread.currentThread().getName(), i));
}
}
public static void main(String[] args) {
System.out.println("线程\"" + Thread.currentThread().getName() + "\"开始执行!");
ThreadByRunnable threadByRunnable = new ThreadByRunnable();
Thread thread = new Thread(threadByRunnable, "实现Runable接口线程1");
thread.start();
for(int i = 0; i < 100; i++) {
System.out.println(String.format("线程\"%s\"打印的数为:%s ", Thread.currentThread().getName(), i));
}
}
}
(3) 实现Callnable接口
package com.threadtest;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class ThreadByCallnable {
public static void main(String[] args) {
FutureTask<Integer> futureTask = new FutureTask<Integer>((Callable<Integer>)() -> {
int i = 0;
for(; i < 100; i++) {
System.out.println(String.format("线程\"%s\"打印的数为:%s ", Thread.currentThread().getName(), i));
try {
Thread.sleep(20);
} catch (InterruptedException e) {
}
}
return 487;
});
Thread thread = new Thread(futureTask, "Callable接口线程1");
thread.start();
for(int i = 0; i < 100; i++) {
System.out.println(String.format("线程\"%s\"打印的数为:%s ", Thread.currentThread().getName(), i));
if(i == 19) {
try {
System.out.println(futureTask.get());
}catch (InterruptedException e) {
} catch (ExecutionException e) {
}
}
try {
Thread.sleep(20);
} catch (InterruptedException e) {
}
}
}
}
注: FutureTask调用get方法时,会导致主线程阻塞,直到FutureTask线程执行完毕。
(4) 三种实现方式对比
采用实现Runable、Callable接口的方式创建多线程的优缺点
- 线程类通过实现Runable接口或Callable接口,还可以继承其他类。
- 在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。
劣势:编程稍稍复杂,如果需要访问当前线程,则必须使用Thread.currentThread()方法。
采用继承Thread类的方式创建多线程的优缺点:
劣势:因为线程类已经继承了Thread类,不能再继承其他类了。
优势:编写简单,如果需要访问当前线程,可以直接使用this即可。
(5) 线程的生命周期
线程要经过的阶段包括新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)、和死亡(Dead)。
(6) 控制线程
- join线程
某个线程执行过程中,调用了其他线程的join方法时,调用线程将被阻塞,知道被调的其他线程执行完为止,才能继续执行调用线程。
package com.threadtest;
public class ThreadByRunnable implements Runnable {
@Override
public void run() {
for(int i = 0; i < 100; i++) {
System.out.println(String.format("线程\"%s\"打印的数为:%s ", Thread.currentThread().getName(), i));
}
}
public static void main(String[] args) {
System.out.println("线程\"" + Thread.currentThread().getName() + "\"开始执行!");
ThreadByRunnable threadByRunnable = new ThreadByRunnable();
Thread thread = new Thread(threadByRunnable, "实现Runable接口线程1");
thread.start();
for(int i = 0; i < 100; i++) {
System.out.println(String.format("线程\"%s\"打印的数为:%s ", Thread.currentThread().getName(), i));
if(i == 19) {
try{
thread.join();
}catch (InterruptedException e) {
}
}
}
}
}
- 后台线程
特点:为其他线程提供服务,后台运行;所有前台线程死亡,后台线程自动死亡。
实现:调用Thread对象的setDaemon(true)
package com.threadtest;
public class ThreadByRunnable implements Runnable {
@Override
public void run() {
for(int i = 0; i < 100; i++) {
System.out.println(String.format("线程\"%s\"打印的数为:%s ", Thread.currentThread().getName(), i));
}
}
public static void main(String[] args) {
System.out.println("线程\"" + Thread.currentThread().getName() + "\"开始执行!");
ThreadByRunnable threadByRunnable = new ThreadByRunnable();
Thread thread = new Thread(threadByRunnable, "后台线程1");
thread.setDaemon(true);
thread.start();
for(int i = 0; i < 10; i++) {
System.out.println(String.format("线程\"%s\"打印的数为:%s ", Thread.currentThread().getName(), i));
}
}
}
3)线程睡眠sleep
让当前正在执行的线程暂停millis毫秒,并进入阻塞状态,该方法受到系统计时器和线程调度器的精度与准确度的影响。
- 线程让步yield
让当前线程暂停,不会阻塞,让线程调度器重新调度一下,只有和线程优先级相同或更高的线程才会获得执行的机会。 - 改变线程优先级
使用方法:setPriority(int newPriority)
特点:
优先级范围在1-10之间,Thread有三个静态常量,分别是MAX_PRIORITY(10)、MIN_PRIORITY(1)、NORM_PRIORITY(5).
优先级别越高获得执行机会就越多,优先级别越低获得执行机会就越少。
每个线程的默认优先级都与创建它的父线程优先级相同,默认情况下,main线程具有普通优先级,由main线程创建的子线程也具有普通优先级。
网友评论