一:通过Thread类:
继承Thread类的方法创建线程:
1:定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体代表了线程需要完成的任务。
2:创建Thread子类的实例,即创建了对象。
3:调用线程对象的start()方法来创建并启动线程。
例如:
/*
*
* @Author
* @Description 线程方法体
* @Date 14:19 2019-12-16
* @Param []
* @return void
**/
@Override
public void run(){
System.out.println("Hello Concurent World");
}
public static void main(String[] args) {
System.out.println("----创建线程-------");
SysController sysController = new SysController();
System.out.println("-----启动线程------");
sysController.start();
}
结果:

二:通过Runnable接口:
1:实现Runnable接口的实现类,并重写接口的run()方法,该run()方法的方法体就是线程执行体:
2:创建Runnable实现类的实例,并以此实例作为Thread类构造函数创建Thread对象,该Thread对象才是真正的线程对象。
例如:
public class SysController implements Runnable{
/*
*
* @Author
* @Description 线程方法体
* @Date 14:19 2019-12-16
* @Param []
* @return void
**/
@Override
public void run(){
System.out.println("Hello Concurent World");
}
public static void main(String[] args) {
System.out.println("----创建线程-------");
SysController sysController = new SysController();
Thread thread = new Thread(sysController);
System.out.println("-----启动线程------");
thread.start();
}
}
结果:
对于Runnable 接口源码:

run方法是无返回值的,没有参数,所以如果是多线程异步的情况,没有返回值没有办法区分哪个线程!!!!!
三:Callable接口(不常用):
实现Callable接口:
1:实现Callable接口的实现类,并实现Call()方法,该Call()方法将作为线程的执行体,且该Call()方法有返回值,再创建Callable实现类的实例。
2:使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的Call()方法的返回值。
3:使用FutureTask对象作为Thread对象的构造函数创建并启动新线程。
4:使用FutureTask对象的get()方法来获得子线程执行结束后的返回值。
例如:
public class SysController{
public static void main(String[] args) {
FutureTask task = new FutureTask(()->{
int count = 0;
for (int i = 0;i <= 100;i++){
count+=i;
}
return count;
});
//创建线程
Thread thread = new Thread(task);
//启动线程
thread.start();
try {
//获取线程返回值
System.out.println("1+2+3+4+5+6...."+task.get());
}catch (InterruptedException e){
e.printStackTrace();
}catch (ExecutionException e) {
e.printStackTrace();
}
}
}
结果:

四:线程生命周期:
1:新建:
new 关键字创建线程对象时的状态。
例如:
SysController sysController = new SysController();
2:就绪:
通过线程对象的start()方法启动线程时对应的状态,此时线程并不一定马上能进入运行状态,线程的运行由操作系统的调度程序控制。
这里涉及到一个问题:
start()方法线程一定会开启吗?
答:如果当前Cpu有空闲,可以马上开启,但是如果Cpu没有空闲,则不会得到Cpu的时间片则不会开启。
3:运行(是以时间片来执行的,把程序分为好几段,这段时间执行这个时间段的代码):
获取Cpu的执行权,线程正在执行需要执行的代码。
执行的是:
@Override
public void run(){
System.out.println("Hello Concurent World");
}
或者
@Override
public Object call() throws Exception {
return null;
}
4:阻塞:
Blocked称为阻塞状态,或者说线程已经挂起,原因是它在等待一个”锁“。也就是说run()和call()不能执行。
线程阻塞:
(1):线程在等待一个锁,当尝试进入一个Synchronized语句块/方法块时,锁已经被其他线程占有,就会被阻塞,直到另一个线程走完临界区或者发生了相应锁对象的wait()操作后,它才有机会去争夺进入临界区的权利
(2):处于阻塞状态的线程,即使对其调用thread.interrupt()也无法改变其阻塞状态,因为interrupt()方法只是设置线程的中断状态,不能唤醒处于阻塞状态的线程。
(3):ReentrantLock.lock()操作后进入的是Waiting状态,其内部调用的是LockSupport.park()方法。
5:等待:
分为:有限期等待和无限期等待。
无限期等待有这样几种情况:
处于这种状态的线程不会被分配CPU执行时间,它们要等待被其他线程唤醒;
没有设置timeout参数的Object.wait();
没有设置timeout参数的Thread.join();
LockSupport.park()。
有限期等待有这样几种情况:
处于这种状态的线程也不会被分配CPU执行时间,不过无需等待被其它线程显示的唤醒
Thread.sleep()方法;
设置了timeout参数的Object.wait();
设置了timeout参数的Thread.join();
LockSupport.parkNanos()方法;
LockSupport.parkUntill()方法;
有这样一个问题:
线程阻塞和线程等待区别:
"阻塞"状态是等待着获取到一个排他锁,进入"阻塞"状态都是被动的,离开"阻塞"状态是因为其他线程释放了锁,不阻塞了;
"等待"状态是在的等待一段时间或者唤醒动作的发生,进入"等待"状态是主动的。
6:结束:
run()方法和call()方法执行完成,线程正常结束;
线程抛出一个未捕获的Exception 或 Error;
直接调用线程的stop()方法来结束线程。
关系图:


本文详细介绍了在Java中创建线程的三种方法:通过Thread类、Runnable接口和Callable接口,以及线程从新建到结束的整个生命周期状态,包括新建、就绪、运行、阻塞、等待和结束。
3419

被折叠的 条评论
为什么被折叠?



