Java多线程详解

最后更新:2020年10月23日09点47分

目录

一、并发与并行

1、并发

2、并行

二、程序、线程与进程

1、程序

2、进程

3、线程

注意:

三、线程调度

1、分时调度

2、抢占式调度

四、主线程

1、主线程

2、单线程程序

五、创建多线程程序的第一种方法(继承Thread类)

1、创建多线程程序

2、实现步骤

3、start方法

4、注意

5、代码示例

自定义的线程类:

Test测试类:

执行结果(交替执行):

六、多线程原理

1、随机打印

2、内存图解

3、执行时机

七、Thread类的常用方法

1、获取线程名称

方法一:使用getName方法

方法二:

2、设置线程名称(了解)

3、sleep

作用:

代码示例:

八、创建多线程程序的第二种方法(实现Runnable接口)

1、实现步骤

2、代码示例

RunnableImpl:

Test测试类:

运行结果:

九、Thread和Runnable的区别

Thread和Runnable的区别 = 使用Runnable接口的好处:

十、匿名内部类方式实现线程的创建

1、继承Thread类的方式

2、实现Runnable接口的方式

十一、创建多线程程序的第三种方法(实现Callable接口)

1、实现步骤

2、代码实现

3、运行结果

4、实现Callable接口的好处

十二、Thread底层原理——静态代理模式

1、Thread实现了Runnable接口


一、并发与并行

1、并发

指的是多个事件在同一时间段内发生(多个事件交替执行);

 

2、并行

指的是多个时间在同一时刻发生(多个事件同时执行)(CPU是多核的情况下才能并行);

 

二、程序、线程与进程

1、程序

程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念;

 

2、进程

①是指一个内存中运行的应用程序,每个进程都有独立的内存空间,一个应用程序可以同时运行多个进程;

②进程也程序的一次执行过程,是系统运行程序的基本单位;

③系统运行一个程序即是进程的创建、运行到消亡的过程;

 

3、线程

线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程;

②一个进程可以有多个线程,这个应用程序可以称为多线程程序;

注意:

很多多线程是模拟出来的,真正的多线程指的是有多个CPU,即多核,如服务器;

一个CPU在一个时间点只能执行一个线程,但由于各线程之间切换的速度极快,所以模拟出来的多线程会令人感到有同时执行的错觉;

简言之:一个程序至少有一个进程,一个进程可以有多个线程;

 

三、线程调度

1、分时调度

所有线程轮流获得CPU的使用权,平均分配每个线程占用CPU的时间;

 

2、抢占式调度

优先让优先级高的线程使用CPU,如果线程优先级相同那么或随机选一个(线程随机性),java使用的为抢占式调度;

 

四、主线程

1、主线程

执行主方法(main)的线程;

 

2、单线程程序

①程序只有一个线程;

②程序从main方法从上到下一次执行;

 

五、创建多线程程序的第一种方法(继承Thread类)

1、创建多线程程序

Thread类是描述多线程的类,要想常见多线程类就必须继承Thread类;

 

2、实现步骤

①创建Thread类的子类;

②重写Thread类中的run方法,设置线程任务(开启线程要做什么);

③创建Thread类的子类对象;

④调用Thread类中的start方法,开启新线程(调用start方法),执行run方法;

 

3、start方法

①start方法的调用结果是两个线程并发地运行——当前线程(从调用返回给 start 方法)和另一个线程(执行其 run 方法);

②多次启动一个线程是非法的,特别是当线程已经结束执行后,不能再重新启动;

③如果不调用start方法,而是调用run方法,就意味着不是开启新线程,只是调用一个类的普通方法,都在主线程执行;

 

4、注意

java程序属于抢占式调度,优先级高的线程优先执行,同一优先级的线程随机执行;

 

5、代码示例

自定义的线程类:

package study.thread;
//定义一个类继承Thread类
public class MyThread extends Thread {
    //重写Thread类的run方法,做要做的事情
    @Override
    public void run() {
        super.run();
        //输出run(0-9)
        for(int i=0; i<10; i++){
            System.out.println("run"+i);
        }
    }
}

Test测试类:

package study.thread;

public class Test {
    public static void main(String[] args) {
        //实例化一个自定义线程的对象
        MyThread myThread = new MyThread();
        //调用start方法,会自动执行run方法,开启一个新的线程
        myThread.start();//此处若写run方法,则执行run方法内容,并未开启新线程
        //在主方法中也输出main(0-9)
        for(int i=0; i<10; i++){
            System.out.println("main"+i);
        }
    }
}

执行结果(交替执行):

main0
run0
main1
run1
main2
run2
main3
run3
main4
main5
main6
main7
main8
main9
run4
run5
run6
run7
run8
run9

 

六、多线程原理

1、随机打印

 

2、内存图解

 

3、执行时机

开启子线程之后,子线程并不一定是立即执行,而是要看CPU是如何调度的,CPU调度了才执行;

 

七、Thread类的常用方法

1、获取线程名称

方法一:使用getName方法

自定义的线程类:

package study.thread;
//定义一个类继承Thread类
public class MyThread extends Thread {
    //重写Thread类的run方法,做要做的事情
    @Override
    public void run() {
        super.run();
        System.out.println(getName());
    }
}

Test测试类:

package study.thread;

public class Test {
    public static void main(String[] args) {
        new MyThread().start();
        new MyThread().start();
        new MyThread().start();
    }
}

运行结果:

Thread-1
Thread-2
Thread-0

方法二:

自定义的线程类:

package study.thread;
//定义一个类继承Thread类
public class MyThread extends Thread {
    //重写Thread类的run方法,做要做的事情
    @Override
    public void run() {
        super.run();
        System.out.println(Thread.currentThread());
        //也可以这么写Thread.currentThread().getName();(链式编程)
        //输出的结果与方法一相同
    }
}

Test测试类:

package study.thread;

public class Test {
    public static void main(String[] args) {
        new MyThread().start();
        new MyThread().start();
        new MyThread().start();
    }
}

运行结果:

Thread[Thread-0,5,main]
Thread[Thread-1,5,main]
Thread[Thread-2,5,main]

2、设置线程名称(了解)

自定义的线程类:

package study.thread;
//定义一个类继承Thread类
public class MyThread extends Thread {
    public MyThread() {
        super();
    }
    //为线程设置名字的第二种方法
    public MyThread(String name) {
        super(name);
    }
    //重写Thread类的run方法,做要做的事情
    @Override
    public void run() {
        super.run();
        System.out.println(Thread.currentThread().getName());
    }
}

Test测试类:

package study.thread;

public class Test {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        //为线程设置名字的第一种方法
        myThread.setName("小明线程");
        myThread.start();//小明线程
        new MyThread("小李线程").start();
    }
}

运行结果:

小明线程
小李线程

3、sleep

作用:

使正在执行的线程,暂停特定的毫秒;

代码示例:

package study.thread;

public class TestSleep {
    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 61; i++) {
            System.out.println(i);
            //线程暂时1秒
            Thread.sleep(1000);
        }
    }
}

 

八、创建多线程程序的第二种方法(实现Runnable接口)

2019.9.21.10:19补充:

如果多线程类extends了另一个类,那就不能再extends第一种方法的Thread类了,所以可以通过实现Runnable接口实现多线程;

1、实现步骤

①创建Runable的实现类;

②该实现类重写run方法;

③创建实现类对象;

④new Thread(多线程实现类对象).start();

2、代码示例

RunnableImpl:

package study.thread;

public class RunnableImpl implements Runnable {
    @Override
    public void run() {
        System.out.println("Runnable接口实现类的run方法");
    }
}

Test测试类:

package study.thread;

public class RunnableImplTest {
    public static void main(String[] args) {
        RunnableImpl runnable = new RunnableImpl();
        new Thread(runnable).start();
    }
}

运行结果:

Runnable接口实现类的run方法

 

九、Thread和Runnable的区别

Thread和Runnable的区别 = 使用Runnable接口的好处:

①避免了单继承的局限性——一个类继承了Thread就不能继承其他类,但若使用Runnable接口实现多线程则不仅就可以继承其他类,也可以继承其他接口;

②增强了程序的扩展性,降低了程序的耦合性(解耦)

——实现Runnable接口的方式,把设置线程任务和开启线程进行了分离(解耦);

——实现Runnable重写run方法用来设置线程的任务;

——创建Thread类用来开启线程;

 

十、匿名内部类方式实现线程的创建

1、继承Thread类的方式

        //继承Thread类的方式
        new Thread(){
            @Override
            public void run() {
                super.run();
                System.out.println("继承Thread类的方式");
            }
        }.start();

2、实现Runnable接口的方式

        //实现Runnable接口的方式
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("实现Runnable接口的方式");
            }
        };
        new Thread(runnable).start();

 

十一、创建多线程程序的第三种方法(实现Callable接口)

(了解,工作三五年之后变得重要!)

1、实现步骤

①实现Callable接口,需要返回值类型;

②重写call方法,需要抛出异常;

③创建目标对象;

④创建执行服务;

⑤提交执行;

⑥获取结果;

⑦关闭服务

 

2、代码实现

package com.zb.thread;

import java.util.concurrent.*;

public class Dog implements Callable<Boolean> {
    @Override
    public Boolean call(){
        System.out.println("开始数狗:");
        for (int i = 0; i < 20; i++) {
            System.out.println("开始数狗,第" + i + "条狗!");
        }
        return true;
    }

    //主线程
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建执行服务
        ExecutorService service = Executors.newFixedThreadPool(1);//线程池大小
        //提交执行
        Future<Boolean> result = service.submit(new Dog());
        //获取结果
        Boolean aBoolean = result.get();
        //关闭服务
        service.shutdownNow();
        //打印结果
        System.out.println(aBoolean);
    }
}

 

3、运行结果

开始数狗:
开始数狗,第0条狗!
开始数狗,第1条狗!
开始数狗,第2条狗!
开始数狗,第3条狗!
开始数狗,第4条狗!
开始数狗,第5条狗!
开始数狗,第6条狗!
开始数狗,第7条狗!
开始数狗,第8条狗!
开始数狗,第9条狗!
开始数狗,第10条狗!
开始数狗,第11条狗!
开始数狗,第12条狗!
开始数狗,第13条狗!
开始数狗,第14条狗!
开始数狗,第15条狗!
开始数狗,第16条狗!
开始数狗,第17条狗!
开始数狗,第18条狗!
开始数狗,第19条狗!
true

 

4、实现Callable接口的好处

可以定义返回值;

可以抛出异常;

 

十二、Thread底层原理——静态代理模式

1、Thread实现了Runnable接口

class Thread implements Runnable

底层原理就是静态代理模式,Runnable是一个多线程接口,只有一个run方法,Thread代理了这个run方法,并添加了一些其他功能;

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值