1.创建和运行线程
1.1直接使用Thread
public static void main(String[] args) {
Thread t = new Thread(){
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" run...");
}
};
t.start();
}
1.2 使用 Runnable 配合 Thread
public static void main(String[] args) {
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " run...");
}
};
Thread t = new Thread(r);
t.start();
}
原理 使用Thread和Runnable方法之间的关系
- 方法1是把线程和任务合并在一起,方法2是把线程和任务分开
- 使用Runnable让任务类脱离Thread的继承体系,更加灵活
// 查看Thread的源码run()方法,如果target存在,则会调用target的run方法,其中target即为传入的实现Runnable接口的对象,调用该对象的run方法。而若使用Thread,则会重写该方法,执行重写的run方法
// private Runnable target;
@Override
public void run() {
if (target != null) {
target.run();
}
}
1.3 FutureTask配合Thread
- 实例程序
FutureTask 可以接收Callable类型的参数,用来处理有返回结果的情况。(前面两种方法无法接收返回结果)
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建任务对象
FutureTask<Integer> task = new FutureTask<>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName() + " run...");
return 233;
}
});
//启动线程
new Thread(task).start();
//主线程阻塞,同步等待task执行完毕的结果
Integer result = task.get();
System.out.println(result);
}
- 原理 FutureTask的原理分析
- FutureTask实现了Runnable接口,重写了run方法。故可以传入至Thread的构造方法中。
//FutureTask实现了Runnable接口
public class FutureTask<V> implements RunnableFuture<V>{}
public interface RunnableFuture<V> extends Runnable, Future<V> {}
//重写了run方法
public void run() {
//...
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
//在FutureTask的run方法中调用实现Callable接口的call方法
result = c.call();
ran = true;
}
//...
}
} finally {
//...
}
- 在FutureTask的构造方法中传入实现Callable接口的对象
// 可以发现Callable接口的call方法有返回值类型,可以返回结果
@FunctionalInterface
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
2.Java线程常用方法
2.1 start和run方法
- 直接调用run是在主线程中执行run,没有启动新的线程
- 使用start是启动新的线程,通过新的线程间接执行run中的代码
//该程序测试run和start方法的区别
public static void main(String[] args) throws ExecutionException, InterruptedException {
Thread t = new Thread(){
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" run...");
}
};
//t.run();
t.start();
}
2.2 sleep方法
- 调用sleep会让当前线程从Running进入Timed Waiting状态
- 其他线程可以使用 interrupt方法打断正在睡眠的线程,此时sleep方法会抛出InterruptedException
- 睡眠结束后的线程未必会立刻得到执行,需要被调度器调度
- 可以使用 TimeUnit的sleep方法,可读性更好
//该程序测试sleep的一些特性
public static void main(String[] args) throws ExecutionException, InterruptedException {
Thread t = new Thread(){
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " run...");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t.start();
//用 TimeUnit 的 sleep 代替 Thread 的 sleep
TimeUnit.SECONDS.sleep(1);
//进入 Timed Waiting 状态
System.out.println(t.getState());
//使用 interrupt 方法打断正在睡眠的线程,
t.interrupt();
}
- result
Thread-0 run...
TIMED_WAITING
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:340)
at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
at com.yqj.concurrent2.CreateThread$1.run(CreateThread.java:21)
2.3 yield 和 线程优先级
- 作用说明
yield:
- 调用yield会让当前线程从Running进入Runnable就绪状态,然后调度执行其他线程
- 具体的实际情况依赖于操作系统的任务调度器
线程优先级:
- 线程优先级会提示调度器优先调度该线程,仅起到提示的作用,调度器可以忽略
- 如果cpu比较忙,优先级高的线程会获得更多的时间片;但cpu闲的时候,优先级几乎没有作用
- 实例程序
//该程序,测试yield和线程优先级的设置,是否会对线程的执行起到影响
public static void main(String[] args) {
Runnable task1 = new Runnable() {
int count = 0;
@Override
public void run() {
while (true) {
System.out.println("t1: "+ (++count));
}
}
};
Runnable task2 = new Runnable() {
int count = 0;
@Override
public void run() {
while (true){
// Thread.yield();
System.out.println(" t2: "+ (++count));
}
}
};
Thread t1 = new Thread(task1,"t1");
Thread t2 = new Thread(task2,"t2");
// t1.setPriority(Thread.MIN_PRIORITY);
// t2.setPriority(Thread.MAX_PRIORITY);
t1.start();
t2.start();
}
2.4 join方法
使用join的目的是需要等待其他线程执行结果返回,再执行本线程
//该程序的主线程需要等待多个线程的结果返回,同步操作,可以获取到num1和num2修改后的结果,最后的时间测得和最长线程花费的时间一致
public class TestJoin {
static int num1 = 0;
static int num2 = 0;
public static void main(String[] args) throws InterruptedException {
Runnable task1 = new Runnable() {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
num1 = 10;
}
};
Runnable task2 = new Runnable() {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
num2 = 20;
}
};
Thread t1 = new Thread(task1, "t1");
Thread t2 = new Thread(task2, "t2");
long start = System.currentTimeMillis();
t1.start();
t2.start();
//等待另外两个线程返回结果
t1.join();
t2.join();
long end = System.currentTimeMillis();
System.out.println("num1="+num1+" num2="+num2);
System.out.println("time="+(end-start));
}
}
- result
num1=10 num2=20
time=2008
2.5 有时效的join方法
//当到达规定的时间,指定的线程还没有返回结果,则直接向后执行。若在规定时间之内,指定的线程返回了结果,则直接继续即可
public class TestJoin {
public static void main(String[] args) throws InterruptedException {
Runnable task = new Runnable() {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " run..");
}
};
Thread t = new Thread(task);
t.start();
//当到达1秒后,还未返回,则继续执行
t.join(1000);
System.out.println("main");
}
}
2.6 interrupt方法
- 实例程序
-
使用 interrupt 方法可以打断 sleep、wait 和 join 的线程
-
打断sleep的线程,会清空打断状态
-
打断正常运行的线程,不会清空打断状态
//该程序测试打断sleep和正常运行线程后,打断标记的状态
public class TestInterrupt {
public static void main(String[] args) throws InterruptedException {
// taskSleepInterrupt();
taskCommonInterrupt();
}
//测试打断sleep状态的线程
public static void taskSleepInterrupt() throws InterruptedException {
Runnable task = new Runnable() {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
//false 清空了打断的标记
System.out.println(Thread.currentThread().isInterrupted());
e.printStackTrace();
}
}
};
Thread t = new Thread(task);
t.start();
TimeUnit.SECONDS.sleep(2);
t.interrupt();
}
//测试打断正常运行的线程
public static void taskCommonInterrupt(){
Runnable task = new Runnable() {
@Override
public void run() {
while (true){
Thread thread = Thread.currentThread();
if (thread.isInterrupted()){
//true 不清空标记
System.out.println(thread.isInterrupted());
System.out.println("interrupt...");
break;
}
}
}
};
Thread t = new Thread(task);
t.start();
t.interrupt();
}
}
- 设计模式 两阶段终止
//两阶段终止,用于单独的服务器线程在后台持续运行记录日志等信息,当收到关闭线程的消息后,做最后的处理,退出线程
public class TestInterrupt {
public static void main(String[] args) throws InterruptedException {
Runnable task = new Runnable() {
@Override
public void run() {
while (true){
Thread thread = Thread.currentThread();
if (thread.isInterrupted()){
System.out.println("do the last things...");
break;
}
try {
TimeUnit.MILLISECONDS.sleep(50);
System.out.println("run...");
} catch (InterruptedException e) {
//二阶段终止
thread.interrupt();
e.printStackTrace();
}
}
}
};
Thread t = new Thread(task);
t.start();
TimeUnit.SECONDS.sleep(1);
//一阶段终止
t.interrupt();
}
- 打断park线程
//使用interrupt方法可以打断park的线程,此时查看打断状态为true,当打断状态为true后,无法再使用park让线程阻塞,需要清空标志为false才可以正常再park线程
public class TestInterrupt {
public static void main(String[] args) throws InterruptedException {
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println("park...");
LockSupport.park();//阻塞
System.out.println("unpark...");
//true
System.out.println(Thread.currentThread().isInterrupted());
}
};
Thread t = new Thread(task);
t.start();
TimeUnit.SECONDS.sleep(1);
t.interrupt();
}
}
2.7 守护线程
默认情况下,Java 进程需要等待所有线程都运行结束,才会结束。有一种特殊的线程叫做守护线程,只要其它非守护线程运行结束了,即使守护线程的代码没有执行完,也会强制结束
//该程序当使用setDaemon设置守护线程后,其他线程结束后,该线程也会随之结束,不会再等待剩余的时间
public class TestDaemon {
public static void main(String[] args) throws InterruptedException {
Runnable task = new Runnable() {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("run...");
}
};
Thread t = new Thread(task);
t.setDaemon(true);
t.start();
TimeUnit.SECONDS.sleep(1);
System.out.println("main end...");
}
}
3.线程的六种状态(基于Java枚举类)
image-20210609215254437.png//六种状态测试
public class TestState {
public static void main(String[] args) throws InterruptedException {
//NEW
Thread t1 = new Thread("t1") {
@Override
public void run() {
}
};
//RUNNABLE
Thread t2 = new Thread("t2") {
@Override
public void run() {
while (true){
}
}
};
//TERMINATED
Thread t3 = new Thread("t3") {
@Override
public void run() {
}
};
//TIMED_WAITING
Thread t4 = new Thread("t4") {
@Override
public void run() {
synchronized (TestState.class){
try {
TimeUnit.SECONDS.sleep(60);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
//WAITING
Thread t5 = new Thread("t5") {
@Override
public void run() {
try {
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
//BLOCKED
Thread t6 = new Thread("t6") {
@Override
public void run() {
synchronized (TestState.class){
try {
TimeUnit.SECONDS.sleep(60);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
TimeUnit.SECONDS.sleep(1);
System.out.println(t1.getState());
System.out.println(t2.getState());
System.out.println(t3.getState());
System.out.println(t4.getState());
System.out.println(t5.getState());
System.out.println(t6.getState());
}
}
4.小结
- 线程创建的三种方式
- 线程重要api,如:start,run,sleep,yield,join,interrupt等
- 线程的六种状态
网友评论