线程 Thread.class
1. 线程的六种状态
- NEW
尚未启动的线程处于此状态。 - RUNNABLE
在Java虚拟机中执行的线程处于此状态。 - BLOCKED
被阻塞等待监视器锁定的线程处于此状态。 - WAITING
正在等待另一个线程执行特定动作的线程处于此状态。 - TIMED_WAITING
正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。 - TERMINATED
已退出的线程处于此状态。
Thread.State getState() // 返回此线程的状态。
2. 线程的常用方法
long getId() // 返回此线程的标识符。
static Thread currentThread() // 返回对当前正在执行的线程对象的引用。String getName() // 返回此线程的名称。
void setName(String name) // 将此线程的名称更改为等于参数 name 。void setPriority(int newPriority) // 更改此线程的优先级。
int getPriority() // 返回此线程的优先级。boolean isAlive() // 测试这个线程是否活着。
boolean isInterrupted() // 测试当前线程是否中断。void start() // 导致此线程开始执行; Java虚拟机调用此线程的run方法。
void interrupt() // 中断这个线程。void join() // 等待这个线程死亡(用来控制线程结束的顺序,让调用这个方法的线程先执行完)
// 调用该方法的线程将会一直占用处理机,把其它线程都阻塞,直至该线程结束,再执行其它线程
// 同样使用join()也会产生异常InterruptExceptionstatic void sleep(long millis) // 使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)
// sleep执行后会产生异常InterruptedException,需要捕捉或抛出
// 每个对象都有一把锁,sleep不会释放这把锁static void yield() // 对调度程序的一个暗示,即当前线程愿意产生当前使用的处理器。
// 礼让线程,让当前正在执行的线程暂停,从运行状态转换为就绪状态
// 礼让不一定成功,可能CPU依旧重新调度该线程
public Thread() // 分配一个新的Thread对象。
public Thread(String name) // 创建一个新的Thread对象,并指定线程名称
public Thread(Runnable target) // 封装Runnable对象为线程对象
public Thread(Runnable target, String name) // 封装Runnable对象为线程对象,并指定线程名称
3. 线程的创建
3.1. 继承Thread类
继承Thread类,重写run()方法,调用start()启动线程
public class TestThread extends Thread {@Overridepublic void run() {// 线程运行逻辑}
}public class Main {public static void main(String [] args) {TestThread testThread = new TetsThread();testThread.strat();}
}
3.2. 实现Runnable接口
继承Thread类,重写run()方法,调用start()启动线程
public class TestThread implements Runnabel {@Overridepublic void run() {// 线程运行逻辑}
}public class Main {public static void main(String [] args) {TestThread testThread = new TetsThread();// Thread thread = new Thread(testThread);// thread.start();// 更简便的写法new Thread(testThread).start();}
}
- 继续Thread类虽然启动线程的方式更简单,但是具有单继承局限性;
- 实现Runnable接口,启动线程需要使用new一个Thread类来启动,但是可以避免单继承局限性(用于实现一份资源,多个代理)
public class TestThread implements Runnable {// 票数private int ticketNums = 10;// 模拟抢票@Overridepublic void run() {while (true) {if (ticketNums <= 0) {break;}System.out.println(Thread.currentThread().getName() + "-->拿到了第" + ticketNums-- +"票" );}}public static void main(String[] args) {TsetThread ticket = new TestThread();// 虽然有多个对象,但是都共用一份资源new Thread(ticket, "a").start();new Thread(ticket, "b").start();new Thread(ticket, "c").start();}
}
3.3. 实现Callable接口
实现Callable接口,定义接口返回值类型,重写call方法并抛出异常
相比Runnable
接口,Callable
接口允许线程执行任务并返回结果,它的call()
方法还可以自定义抛出异常。
public class TestCallable implements Callable<Boolean> {// 定义成员变量// 定义构造器@Overridepublic Boolean call() throws Exception {// 编写线程需要执行的任务return true;}public static void main(String[] args) throws Exception {// 创建线程池ExecutorService ser = Executors.newFixedThreadPool(1);// 提交Callable任务,并获取Future对象Future<Boolean> future = executor.submit(new TestCallable());// 获取线程执行结果Boolean result = future.get();System.out.println(result);// 关闭线程池executor.shutdown();}
}
Callable
创建线程的底层原理
- 当通过
**ExecutorService**
的**submit()**
方法提交**Callable**
任务时,线程池会将**Callable**
任务封装成一个**FutureTask**
对象。 **FutureTask**
对象实现了**RunnableFuture**
接口,而**RunnableFuture**
接口继承自**Runnable**
接口,因此**FutureTask**
对象也可以作为**Runnable**
任务被线程执行。- 线程池会选择一个空闲的线程执行
**FutureTask**
对象中的**run()**
方法,而**run()**
方法实际上会调用**Callable**
对象的**call()**
方法执行任务。 - 当任务执行完成后,
**FutureTask**
对象会保存任务的执行结果,其他线程可以通过**Future**
对象的**get()**
方法获取执行结果。
注意:
- 多线程中,主线程调用完
start()
之后,会继续执行自己路线,并不用等子线程执行完了再继续执行下去;
- 线程启动之后,并不一定立即执行,只是进入了线程就绪状态,具体运行时间由CPU调度安排(也就是说在多线程环境下,即使多个线程依次start但真实执行顺序却不一定按序);
补充:
函数式接口:
- 接口中只包含唯一一个抽象方法,就称为函数式接口,例如
Runnable
接口就是一个函数式接口;对于函数式接口可以直接用lambda
表达式直接创建该接口的对象; - 编写函数式强制接口里面只能有一个抽象方法,使用时需带有这个注释
@FunctionalInterface
,该注解可用于一个接口的定义上。一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错.
@FunctionalInterface
public interface Runnable {/*** When an object implementing interface <code>Runnable</code> is used* to create a thread, starting the thread causes the object's* <code>run</code> method to be called in that separately executing* thread.* <p>* The general contract of the method <code>run</code> is that it may* take any action whatsoever.** @see java.lang.Thread#run()*/public abstract void run();
}
package text1;public class TestLambda {// 3. 静态内部类static class Like2 implements ILike {@Overridepublic void lambda() {System.out.println("静态内部类接口实例化");}}public static void main(String[] args) {ILike like = new Like();like.lambda();like = new Like2();like.lambda();// 4. 局部内部类class Like3 implements ILike {@Overridepublic void lambda() {System.out.println("局部内部类接口实例化");}}like = new Like3();like.lambda();// 5. 匿名内部类like = new ILike() {@Overridepublic void lambda() {System.out.println("匿名内部类接口实例化");}};like.lambda();// 6. lambda表达式简化匿名内部类like = () -> {System.out.println("匿名内部类接口实例化");};like.lambda();// 7. lambda表达式简化like = () -> System.out.println("lambda表达式简写");like.lambda();}}// 1. 定义一个函数式接口
interface ILike {void lambda();
}// 2. 实现类
class Like implements ILike {@Overridepublic void lambda() {System.out.println("1");}
}
Lambda表达式
- 用于简化函数式接口的实例对象,本质其实是一个接口的实例对象;
- 语法:
Lambda表达式的标准格式为:(参数类型 参数名称) ‐> { 代码语句 }
- 小括号里面放参数列表
- 箭头用来连接参数列表和方法体
- 箭头后面的大括号内放方法体
- 语法省略规则:
- 参数列表中的所有参数类型都可以省略,但需要同时省略;若只有一个参数,小括号都可以同时省略,如
(String s)
简写成s
- 如果方法体里只有一行代码,就可以直接省略大括号
{}
和语句结尾的分号;
- 参数列表中的所有参数类型都可以省略,但需要同时省略;若只有一个参数,小括号都可以同时省略,如
如:hh( (String s)-> {System.out.println(s) } )
,因为在hh这个方法的参数列表中提前指明了接口类型 , 所以写成Lambda形式,是能被系统自动识别出是对谁的简写。
4. 线程的关闭
线程关闭可以使用设置标志位flag
的方法,设定当线程方法体中达成了某种变化使得标志位改变,从而使线程停止;
不推荐使用JDK已废弃的stop()
方法和destroy()
方法
@Override
public void run() {while (flag) {// 线程执行内容}
}