目录
创建线程:
线程的常用方法;
给线程指定名字
线程的生命周期
程序(软件):数据和指令的集合。
进程:正在运行的程序,会在内存中分配空间。
线程:进程中的多条路径。
多线程是指有多条线程并发的执行。
并发:多条线程在同一时间段内交替执行。
并行:多条线程同时执行。
创建线程:
1.继承:extends Thread类 继承的子类需要重写run方法
2.实现:implements Runnable接口
注意:无论是继承还是实现,直接调用重写过的run方法是无法开启多线程的,jvm中默认start()方法开启多线程,start()方法会默认调用重写的run()方法。
3.线程创建方式的优缺点:
① 继承方式写法简单。实现方式写法相对比较复杂
② 继承与实现都是通过 start 方法才能开启线程。实际开发中,使用实现形式要多于继承形式。
使用继承来开启多线程
public class Demo5 {
public static void main(String[] args) {
ExtendsThread et=new ExtendsThread();
et.start();
for(int j=0;j<100;j++) {
System.out.println("=========j"+j);
}
}
}
class ExtendsThread extends Thread{
@Override
public void run() {
for (int i = 65; i <91; i++) {
System.out.println((char)i);
}
}
}
使用实现Runnable接口来开启多线程:代码示例:
package com.Allen.Thread;
public class Demo6 {
public static void main(String[] args) {
ImplementsRunnable ir=new ImplementsRunnable();
Thread td=new Thread(ir);
td.start();
for (int j = 0; j < 1000; j++) {
System.out.println("====j"+j);
}
}
}
class ImplementsRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("***********i"+i);
}
}
}
实现Runnable接口来开启多线程还可以使用匿名对象 代码示例:
package com.Allen.Thread;
public class Demo6 {
public static void main(String[] args) {
Thread td=new Thread(new ImplementsRunnable()); //这里使用的是匿名对象
td.start();
for (int j = 0; j < 1000; j++) {
System.out.println("====j"+j);
}
}
}
class ImplementsRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("***********i"+i);
}
}
}
线程的常用方法;
1.getName()返回线程的名称
2.currentThread()获取当前线程的引用对象。
线程的名称 main方法所在的线程为主线程 ,线程名为main
自定义的线程:Thread-0,Thread-1 默认格式:Thread-index 其中index从0开始 线程的名称可以设置
3.setName () 设置线程的名称
4.getpriority()/setPriority() 返回线程的优先级/设置线程的优先级
线程的优先级:从1到10,默认为5,最高优先级为10,最低优先级为1
线程如果优先级越高,抢占CPU时间片的可能性越大,默认一个线程创建时,优先级为5.
5.isDaemon()/setDeamon(true) 判断该线程是否为守护线程/设置线程为守护线程,参数值为true。
守护线程是用来守护用户线程,为用户线程进行准备或者守护的工作。
随着用户线程的消亡,守护线程无论是否执行完都会随着用户线程消亡。
6.join()等待该线程的终止,相当于用户调用。
7sleep(ms) 休眠,毫秒值
8.start()开启线程的方法,会默认调用run()方法,进行线程的开启。
9.yield()退出当前正在执行的线程,让给其他线程执行,线程的让步。
给线程指定名字
线程的生命周期
线程的生命周期包含5个阶段,包括:新建、就绪、运行、阻塞、销毁。
新建:就是刚使用new方法,new出来的线程;
就绪:就是调用的线程的start()方法后,这时候线程处于等待CPU分配资源阶段,谁先抢的CPU资源,谁开始执行;
运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态,run方法定义了线程的操作和功能;
阻塞:在运行状态的时候,可能因为某些原因导致运行状态的线程变成了阻塞状态,比如sleep()、wait()之后线程就处于了阻塞状态,
这个时候需要其他机制将处于阻塞状态的线程唤醒,比如调用notify或者notifyAll()方法。唤醒的线程不会立刻执行run方法,它们要再次等待CPU分配资源进入运行状态;
销毁:如果线程正常执行完毕后或线程被提前强制性的终止或出现异常导致结束,那么线程就要被销毁,释放资源;新建状态
只有当我们调用了 start() 方法之后,该线程才会被创建出来,进入Runnable状态。只有当我们调用了 start() 方法之后,该线程才会被创建出来
Thread t1 = new Thread();
就绪状态
调用start()方法后,JVM 进程会去创建一个新的线程,而此线程不会马上被 CPU 调度运行,进入Running状态,这里会有一个中间状态,就是Runnable状态,你可以理解为等待被 CPU 调度的状态
t1.start()
用一张图表示如下:那么处于Runnable状态的线程能发生哪些状态转变?
Runnable状态的线程无法直接进入Blocked状态和Terminated状态的。只能处在Running状态的线程,
换句话说,只有获得CPU调度执行权的线程才有资格进入Blocked状态和Terminated状态,Runnable状态的线程要么能被转换成Running状态,要么被意外终止。
运行状态
当CPU调度发生,并从任务队列中选中了某个Runnable线程时,该线程会进入Running执行状态,并且开始调用run()方法中逻辑代码。
那么处于Running状态的线程能发生哪些状态转变?
被转换成Terminated状态,比如调用 stop() 方法;
被转换成Blocked状态,比如调用了sleep, wait 方法被加入 waitSet 中;
被转换成Blocked状态,如进行 IO 阻塞操作,如查询数据库进入阻塞状态;
被转换成Blocked状态,比如获取某个锁的释放,而被加入该锁的阻塞队列中;
该线程的时间片用完,CPU 再次调度,进入Runnable状态;
线程主动调用 yield 方法,让出 CPU 资源,进入Runnable状态
阻塞状态
Blocked状态的线程能够发生哪些状态改变?
被转换成Terminated状态,比如调用 stop() 方法,或者是 JVM 意外 Crash;
被转换成Runnable状态,阻塞时间结束,比如读取到了数据库的数据后;
完成了指定时间的休眠,进入到Runnable状态;
正在wait中的线程,被其他线程调用notify/notifyAll方法唤醒,进入到Runnable状态;
线程获取到了想要的锁资源,进入Runnable状态;
线程在阻塞状态下被打断,如其他线程调用了interrupt方法,进入到Runnable状态;
终止状态
一旦线程进入了Terminated状态,就意味着这个线程生命的终结,哪些情况下,线程会进入到Terminated状态呢?
线程正常运行结束,生命周期结束;
线程运行过程中出现意外错误;
JVM 异常结束,所有的线程生命周期均被结束。