一 Java中线程的实现方式
线程的创建方式一般回答为四种,当然也有说一种,三种,五种的,但是溯其本源是只有一种的,先赘述一下有哪四种,然后解释为什么其实只有一种。
第一种:继承Thread类 重写run方法 启动线程是调用start方法,这样就会创建一个新的线程。并执行线程的任务。如果直接调用run方法,这样会让当前线程执行run方法中的逻辑。
代码如下
第二种 实现Runnable接口 重写run方法
代码如下
第三种 当想 获得线程的执行结果时 可以实现Callable 重写call方法,配合FutuureTask
代码如下:
第四种 基于线程池 去构建线程
第五种 有人将 lambda重写run方法时候 称为第五种
那么追其底层,实际上都是基于 实现Runnable接口实现 我们打开idea 查看Thread类 我们会发现 实现了Runnable接口。
此方式将futureTask传递到了Thread类中的有参构造当中
那么查看FutureTask 如下 我们会发现实现了RunnableFuture接口。那么接着查看此接口
我们发现还是继承了Runnab接口。
只不过是在其run方法中写了一套逻辑 将返回结果返回
最后说一下线程池,我们查看ThreadPoolExecutor 中的worker类 (我们每次构建的线程都是worker) 也是实现了Runnable接口
所以溯其本源只有一种 说几种都是正确的关键看你怎么去陈述
二 java线程中的状态
一.线程的状态
1.1 从操作系统来说 共有五种状态 如图所示:
1.2 而java为我们准备了 六种状态 我们查看Thread类源码 如下
状态如图
new :此时刚刚把线程的内存地址分配好并没有启动 start方法还未执行,此时cpu无法调度
runnable :java中不区分就绪和运行统称为此状态,调用了start方法,此时cpu可能正在调度线程。也可能没掉度
blocked : 当持有synchronized锁失败的时候没有拿到锁,线程会阻塞,此时为此状态
waiting ;当调用wait,join等方法时,为此状态,需要手动唤醒。
timed_waiting : 当调用sleep等方法时,时间一到会返回大runnbble状态,不需要手动唤醒
terminated :run方法执行完毕,线程生命周期结束
下面为对应状态的小demo
public class Main {
public static void main(String[] args) {
Thread thread = new Thread(
);
System.out.println(thread.getState());
}
}
此时状态如图
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(
() -> {
while (true){
}
}
);
thread.start();
Thread.sleep(500);
System.out.println(thread.getState());
}
}
此时状态如图
public class Main {
public static void main(String[] args) throws InterruptedException {
Object obj = new Object();
Thread thread = new Thread(
() -> {
synchronized (obj){
}
}
);
synchronized (obj){
thread.start();
Thread.sleep(500);
System.out.println(thread.getState());
}
}
}
此时状态如图
public class Main {
public static void main(String[] args) throws InterruptedException {
Object obj = new Object();
Thread thread = new Thread(
() -> {
synchronized (obj) {
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
);
thread.start();
Thread.sleep(500);
System.out.println(thread.getState());
}
}
此时状态如图
public class Main {
public static void main(String[] args) throws InterruptedException {
Object obj = new Object();
Thread thread = new Thread(
() -> {
try {
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
);
thread.start();
Thread.sleep(500);
System.out.println(thread.getState());
}
}
此时状态如图
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(
() -> {
System.out.println(666);
}
);
thread.start();
Thread.sleep(100);
System.out.println(thread.getState());
}
}
此时状态如图
到此六种状态测试完毕
三 java如何停止线程
1.使用stop方法 :会强制让线程结束,由于太过暴力,不推荐使用
2.使用共享变量 :有的线程通过死循环保持一直执行,所以可以使用共享变量破坏死循环,让线程退出死循环,结束run方法。但是成本有些高,也不推荐。
3.使用interupt方式 :此方式类似于共享变量,在线程内有一个中断标记位。默认false 线程休眠时 修改标记位会抛出异常 InterruptedException
四 sleep和wait方法的区别
1.sleep时Thread类中的静态方法,wait是Object类中的方法
2.执行sleep后线程状态为TIMED_ WAITING,自动被唤醒、wait属于WAITING,需要手动唤醒。
3.sleep方法在持有锁时,执行,不会样放锁资源、wait在执行后,会辉放锁资源。
4.sleep可以在持有锁或者不持有锁时,执行。wait方法必须在只有锁时才可以执行
因为在synchronized底层中,对象头中的markword指向 ObjectMonitor,对ObjectMonitior中的属性进行操作(wait方法会将持有锁的线程从owner扔到Waitset集合中,这个操作是在修改QojectMonitor对象,如果没有持有synchronized锁的话,是无法操作ObjectiMlonitor对象的。)
五 并发编程的三大特性
1.原子性
2.可见性
3.有序性