多线程
1. 进程和线程
1.1 进程
进程:软件在运行时一种状态。会占用CPU、内存等号资源
正在运行的程序
进程的特点:
1.独立性
2.动态性
3.并发性
1.2 线程
线程:代码的执行路径,是进程的组成部分,一个进程可以有多个线程。每个线程都可以执行自己的内容或者多个线程共同执行一个任务
有了多线程,就可以同时干多件事情
线程是CPU的最小调度单位,CPU可以很快的在多个线程间实现切换。
运行时的线程,随时都可以被CPU给挂起。
线程的抢占发生在任意时期。
1.3 进程和线程的区别
1、一个程序,可以有多个进程
2、一个进程可以有多个线程。但是必须要有一个主线程
3、进程间不能共享资源,但是线程间可以共享资源。
2. 线程的体验和创建方式
敲黑板:java中的main方法其实就是主线程,main内部的代码执行都是主线程在执行。
我们自己手动创建线程,都叫子线程
代码演示 线程初体验
public static void main(String[] args) {
//1.创建线程接口的对象 匿名内部类-实例化 接口、抽象类
Runnable rb=new Runnable() {
@Override
public void run() {
System.err.println("我是子线程-"+
Thread.currentThread().getName());
}
};
//2.创建线程对象,并定义线程要做的事情
Thread td=new Thread(rb);
//3.启动线程
//td.start();//才是子线程在运行
td.run();//这样的话,代码的运行就是主线程运行。
System.err.println("主线程-"+Thread.currentThread().getName());
}
2.1 线程的说明
1.Java中的线程类:Thread
2.Java中的线程都需要接口,线程接口:Runnable
接口内部只有一个方法:run
public void run();
3.每次在写子线程的时候,都需要在run方法内部实现自己要干的事情
4.启动线程是用的start方法
2.2 线程的创建方式
日常使用线程,常用的三种创建方式。
1.实现Runnable接口
2.自定义类实现Thread类的子类,重写run方法
3.实现Callable接口
2.3 实现Runnable接口实现线程
1.采用匿名内部类 直接完成接口的实例化,并重写run方法
2.自定义类实现接口,重写run方法
代码演示 基于Runnable接口实现线程:实现Runnable接口
public class MyRunnable implements Runnable{
@Override
public void run() {
//子线程要做的事情
//通过子线程 实现数羊
for(int i=1;i<101;i++) {
System.err.println("子线程-数羊:第"+i+"只羊");
}
}
}
main方法测试
public static void main(String[] args) {
//1.创建接口的实例对象
MyRunnable mr=new MyRunnable();
//2.创建线程对象
Thread td=new Thread(mr);
//3.启动线程
td.start();
}
2.4 继承Thread类实现线程
代码演示:创建类继承Thread,并重写run方法
public class HelloThread extends Thread{
@Override
public void run() {
System.err.println(" 继承线程类 实现子线程");
}
}
public static void main(String[] args) {
//1.实例化自定义线程类对象
HelloThread ht=new HelloThread();
//2.启动线程
ht.start();
}
2.5 使用Callable实现线程
jdk新版本推出线程创建方式,线程可以带返回值。
1.实现Callable接口,并指定线程的返回值类型
2.创建FutureTask对象
3.创新线程对象,并传递任务对象
4.启动线程
5.获取线程的返回值
public class MyCallable implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.err.println("子线程,带返回值的");
return new Random().nextInt(10);
}
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
//1.创建 Callable接口的实现类对象
MyCallable mc=new MyCallable();
//2.创建任务对象
FutureTask<Integer> ft=new FutureTask<Integer>(mc);
//3.创新线程对象
Thread td=new Thread(ft);
//4.启动线程
td.start();
//5.获取线程的返回值 只有在线程执行结束之后才会返回
System.err.println("返回值:"+ft.get());
}
2.6 三种创建方式的区别
继承Thread类:
编写简单、单继承,所以这种类无法再继承其他类、无法实现多个线程的资源共享、扩展性无
实现Runnable接口:
编写复杂一点,接口可以多实现,可以实现多个线程的资源共享 推荐使用
实现Callable接口:
编码复杂,可以实现线程执行完之后进行值的返回
开发中,需要线程返回值,就使用Callable,不需要返回值的就可以Runnable5.8
3. 线程
3.1 线程的状态(生命周期)
线程有五大状态,分别是新建、就绪、运行、阻塞、销毁
新建:
当我们实例化线程对象的时候,线程就是新建状态
就绪:
当我们调用线程的start方法之后,线程就会进入就绪状态
处于该状态的线程,随时都可以获取CPU调度
运行:
线程获取CPU的调度之后,线程抢到了时间片,可以用来运行自己任务
阻塞:
当线程因为资源竞争,或主动方法调用,让线程进入到阻塞。
常见:sleep、wait、join等等
销毁:
当线程的run方法执行结束之后,就会进入到销毁状态
敲黑板:程序的结束就是指的内部多的所有线程全部进入到了销毁状态
4. 线程的分类
4.1 线程的种类
线程分为:用户线程和守护线程
Java中默认创建的线程就是用户线程。
代码演示:用户线程和守护线程
public static void main(String[] args) {
// 代码演示线程的类型 用户线程和守护线程
//默认的就是用户线程
Thread th=new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.err.println("子线程");
}
});
th.start();
//创建守护线程
Thread th2=new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.err.println("子线程--守护线程");
}
});
//设置当前的线程对象是否为守护线程,默认为false
th2.setDaemon(true);
//启动线程
th2.start();
}
4.2 用户线程和守护线程的区别
主线程和GC线程就是经典-用户线程和守护线程。GC线程守护主线程
守护线程的特点:当守护的用户线程销毁的时候,守护线程也会跟着消亡。无论守护线程是否执行结束都会随着用户线程一起销毁。
皇帝和妃子的故事:
皇帝–>用户线程
妃子们–>守护线程
代码演示:守护线程
public class LogRunable implements Runnable{
@Override
public void run() {
//死循环
while(true) {
//间隔3秒 打印日志……
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("守护线程:记录日志中……");
}
}
}
public static void main(String[] args) {
//演示 用户线程和守护线程
//创建用户线程
Thread td=new Thread(new Runnable() {
@Override
public void run() {
//拥有守护线程
Thread t=new Thread(new LogRunable());
//设置为守护线程 守护的是当前的用户线程
t.setDaemon(true);
t.start();
for(int i=1;i<10001;i++) {
if(i%33==0&& i%77==0) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.err.println("用户线程--"+i);
}
}
}
});
//启动用户线程
td.start();
}
当用户线程销毁的时候,会将自己的守护线程一起销毁!
5. 线程的优先级
5.1 优先级
线程的优先级就是线程获得CPU的概率,优先级越高,获取CPU的概率越大
从小到大,1-10之间。10是优先级最高,默认的优先级是5.
5.2 设置优先级
通过线程的setPriority 设置线程的优先级
通过线程的getPriority获取线程的优先级
设置优先级需要在start之前设置。
6. Lambda表达式
Lambda是JDK8之后支持的一种简写模式。可以快速的实现接口的实例化
语法格式:(参数)->{方法重写}
规则:
1.接口
2.接口中只能有一个抽象方法
3.Lambda表达式的结果是一个对象
代码演示
public interface IEat {
void eat();
}
public static void main(String[] args) {
//1.基于Lambda表达式完成接口的实例化
IEat ie=()->System.err.println("吃啥呢?");
ie.eat();
}