Java------多线程基础知识学习

计算机多线程的作用及意义

  1. 多线程和多进程

    ​ 进程就是每一个正在进行的程序,进程是系统进行资源分配和调用的独立单位,每一个进程都拥有它自己独立的内存空间和系统资源。

    ​ 一个程序最少需要一个进程,而一个进程最少需要一个线程。关系是线程–>进程–>程序的大致组成结构。所以线程是程序执行流的最小单位,而进程是系统进行资源分配和调度的一个独立单位。以下我们所有讨论的都是建立在线程基础之上。

  2. 多线程的设计意义

    ​ 多线程的设计是为了提高CPU的利用效率。

    ​ 如果学习过单片机的话,那么就会明白在单片机中CPU是通过中断来调用的,如果有需求CPU才会去处理问题,然而一般来说处理一个需求所需要的时间是极短的,如果采用单线程设计那么大部分的时间CPU都是处于等待需求的状态,只有当这个任务结束之后,才能够去处理下一个任务,这样极大的浪费了资源,并且效率低下。

    ​ 比如说我要下载一个电影,我还想听歌,一般我们都是点了下载,就可以边听歌边等他下完了。如果是单线程那么我就只能点了下载然后就等着,直到电影下载完成我才可以听歌;这样估这电脑也活不了多久就被摔了;

  3. Java程序运行原理

    ​ Java命令会启动java虚拟机,启动JVM,等于启动了一个应用程序,也就是启动了一个进程。
    ​ 该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 main 方法。
    ​ 所以 main方法运行在主线程中。

  4. JVM的启动是多线程的吗:

    ​ JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的

    Java程序中多线程的实现方式

    ​ 由于线程是依赖进程而存在的,所以我们应该先创建一个进程出来。而进程是由系统所创建的,所以我们应该去掉用系统功能而创建一个进程。但是Java不能直接调用系统的功能,因此在Java中线程的开启方式是Java去调用C/C++写好的多线程程序,由C/C++去调用系统功能创建进程。是不是很麻烦,不过Java将这些都打包成了类提供给我们使用。

    方式一:继承Thread类
    • 代码演示

    • 线程的命名

      • 使用set方法

      ​ public final String getName()//获取线程名称
      ​ public final void setName(String name)//设置线程名称

      • 使用构造方法
    public class MyTest {
        public static void main(String[] args) {
            MyThread myThread = new MyThread();
            myThread.setName("一号线程");
            myThread.start();
    
            //第二种线程命名方式
            MyThread3 thread3 = new MyThread3("二号线程");
            //将线程优先级设为最高
            thread3.setPriority(Thread.MAX_PRIORITY);
            thread3.start();
    
    
        }
    }
    
    public class MyThread extends Thread{
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName()+"===="+i);
            }
    
        }
    }
    
    public class MyThread3 extends Thread{
        public MyThread3(String name) {
            super(name);
        }
    
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName() + "====" + i);
            }
    
        }
    }
    
  • 这个类是一个线程类,那么在这个类中我们可不可以写一些其他的方法呢?

​ 我们可以在写其他的方法,那么其他方法中封装的代码不一定需要被我们线程执行但是run方法里面封装的应该是必须被线程执行的代码。

  • run方法中的代码的书写原则: 一般是比较耗时的代码。

  • 如何获取主线程?

    可以使用方法 public static Thread currentThread()返回对当前正在执行的线程对象的引用。 拿到引用之后我们就可以使用public final void setName(String name)//设置主线程名称的名字:

Thread.currentThread().setName("刘备");
线程调度及获取和设置线程优先级
  • 线程的执行
    假如我们的计算机只有一个 CPU,那么 CPU 在某一个时刻只能执行一条指令,
    线程只有得到 CPU时间片,也就是使用权,才可以执行指令。那么Java是如何对线程进行调用的呢?

  • 线程有两种调度模型:
    **分时调度模型 ** 所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片
    抢占式调度模型 优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些。

    从刚才的例子可以看出***Java使用的是抢占式调度模型。***

  • 如何设置线程的优先级

    public final int getPriority() //获取线程的优先级
    public final void setPriority(int newPriority)//设置线程的优先级

    • 代码演示
    public class MyTest {
        public static void main(String[] args) {
            //在Java中如何开启一个线程,利用 Thread 这个线程类
            //线程 是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。
            MyThread th1 = new MyThread();
            th1.setName("刘亦菲");/对线程进行命名
            th1.setPriority(1);
            th1.start();//开启线程的方法
            MyThread th2 = new MyThread();
            th2.setPriority(Thread.MAX_PRIORITY);
            th2.setName("林青霞");
            th2.start();
            //Java中多个线程,执行是随机性的,因为我Java采用的线程调度模型是抢占式调度,线程优先级高的优先使用CPU的执行权
            //优先级一样,就是机抢占
    
        }
    }
    

    public class MyThread extends Thread {
        @Override
        public void run() { //需要线程来执行的方法
            //一般run方法里面写的就是耗时操作
            for (int i = 0; i < 100; i++) {
                //System.out.println(this.getName()+"=="+i);
    
                System.out.println(Thread.currentThread().getName()+"===" + i);
    
            }
        }
    }
    
线程休眠
  • 线程休眠: public static void sleep(long millis) 线程休眠

  • 代码演示

    public class MyTest {
        public static void main(String[] args) throws InterruptedException {
    
            System.out.println("这是一段广告");
            Thread.sleep(5000);
            System.out.println("bbbbb");
            //Thread(String name) 分配新的 Thread 对象。
            MyThread th1 = new MyThread("林青霞");
            th1.start();
        }
    }
    
    public class MyThread extends Thread {
        public MyThread(String name) {
            super(name);
        }
    
        @Override
        public void run() { //需要线程来执行的方法
            //一般run方法里面写的就是耗时操作
            for (int i = 0; i < 100; i++) {
                try {
                    Thread.sleep(2000);//让线程休眠
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //System.out.println(this.getName()+"=="+i);
                System.out.println(Thread.currentThread().getName() + "===" + i);
    
            }
        }
    }
    
线程加入
  • 加入线程: public final void join()
    • 加入线程执行完毕之后,其他线程才能够再次执行
    • 注意事项:在线程启动之后,再调用方法
    • 案例演示
public class MyTest {
    public static void main(String[] args) throws InterruptedException {
        MyThread th1 = new MyThread("刘备");
        MyThread th2 = new MyThread("关羽");
        MyThread th3 = new MyThread("张飞");
        th1.start();
        th1.join();
        th2.start();
        th2.join();
        th3.start();
    }
}

public class MyThread extends Thread {
    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() { //需要线程来执行的方法
        //一般run方法里面写的就是耗时操作
        for (int i = 0; i < 100; i++) {

            System.out.println(Thread.currentThread().getName() + "===" + i);

        }
    }
}

线程礼让
  • public static void yield(): 暂停当前正在执行的线程对象,并执行其他线程。
  • 案例演示线程礼让
ublic class MyTest {
    public static void main(String[] args) {

        MyThread th1 = new MyThread("刘备");
        MyThread th2 = new MyThread("关羽");
        th1.start();
        th2.start();

    }
}

public class MyThread extends Thread {
    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() { //需要线程来执行的方法
        //一般run方法里面写的就是耗时操作
        for (int i = 0; i < 100; i++) {
            Thread.yield();//线程礼让
            System.out.println(Thread.currentThread().getName() + "===" + i);

        }
    }
}

按照我们的想法,这个礼让应该是一个线程执行一次,但是通过我们的测试,效果好像不太明显。这个礼让是要暂停当前正在执行的线程,这个暂停的时间是相当短的,如果在这个线程暂停完毕以后,其他的线程还没有抢占到CPU的执行权,那么这个时候这个线程应该再次和其他线程抢占CPU的执行权.

守护线程
  • 守护线程: public final void setDaemon(boolean on):

    将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。

    • 注意事项:该方法必须在启动线程前调用。
  • 代码演示

    public class MyTest {
        public static void main(String[] args) {
            Thread.currentThread().setName("刘备");
            System.out.println("主线程开始了");
            System.out.println("主线程开始了");
            System.out.println("主线程开始了");
            System.out.println("主线程开始了");
    
    
            //A:
            //守护线程:
            //public final void setDaemon ( boolean on):
            //将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。
            //该方法必须在启动线程前调用。
            //当用户线程,执行完之后,那么守护线程,必须里面死亡
            MyThread th1 = new MyThread("张飞");
            MyThread th2 = new MyThread("关羽");
            //设置为守护线程 该方法必须在启动线程前调用。
            th1.setDaemon(true);
            th2.setDaemon(true);
            th1.start();
            th2.start();
    
            System.out.println("主线程结束了");
            System.out.println("主线程结束了");
            System.out.println("主线程结束了");
            System.out.println("主线程结束了");
            System.out.println("主线程结束了");
    
        }
    }
    
    
    public class MyThread extends Thread {
        public MyThread(String name) {
            super(name);
        }
        @Override
        public void run() { //需要线程来执行的方法
            //一般run方法里面写的就是耗时操作
            for (int i = 0; i < 100; i++)     
                System.out.println(Thread.currentThread().getName() + "===" + i);
    
            }
        }
    }
    
    
中断线程
  • 中断线程

    ​ public final void stop(): 停止线程的运行 过时的方法

  • 清除线程的阻塞状态

    ​ public void interrupt(): 清除线程的阻塞状态,让线程继续运行

    • 代码演示
    public class MyTest {
        public static void main(String[] args) throws InterruptedException {
            MyThread th1 = new MyThread("张飞");
            th1.start();
            Thread.sleep(1000);
            th1.interrupt();//打断线程的阻塞状态,让线程继续运行
        }
    }
    
    
    public class MyThread extends Thread {
        public MyThread(String name) {
            super(name);
        }
        @Override
        public void run() { //需要线程来执行的方法
            //一般run方法里面写的就是耗时操作
            try {
                Thread.sleep(5000); //让线程休眠,其实是让线程处于了一种阻塞状态
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for (int i = 0; i < 100; i++) {
    
                System.out.println(Thread.currentThread().getName() + "===" + i);
    
            }
        }
    }
    
    

    运行可以看到该程序只在主线程阻塞1000ms子线程里面直接运行没有停留

Java开启多线程的方式2
  • 实现Runnable接口 这种方式扩展性强 实现一个接口 还可以再去继承其他类

    • 如何获得线程的名称
    • 如何给线程设置名称

    代码演示

    public class MyTest {
        public static void main(String[] args) {
            //1.  创建线程的另一种方法是声明实现 Runnable 接口的类。
            //2.  该类然后实现 run 方法。然后可以分配该类的实例,
            // 3. 在创建 Thread 时作为一个参数来传递并启动
            MyRunable myRunable = new MyRunable();
    
            //Thread(Runnable target)
            //分配新的 Thread 对象。
    
            //Thread(Runnable target, String name)
            //分配新的 Thread 对象。
            Thread th = new Thread(myRunable,"王菲");
            th.start();
            Thread th2 = new Thread(myRunable,"谢霆锋");
            th2.start();
        }
    }
    
    
    public class MyRunable implements Runnable { //Runable 任务
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                // System.out.println(this.getName+"==+i");
                System.out.println(Thread.currentThread().getName() + "==" + i);
            }
        }
    }
    
    
  • 实现接口方式的好处

    ​ 可以避免由于Java单继承带来的局限性。

Java开启多线程的方式3
  • 实现 Callable 接口。 相较于实现 Runnable 接口的方式,方法可以有返回值,并且可以抛出异常。
  • 执行 Callable 方式,需要 FutureTask 实现类的支持,用于接收运算结果。 FutureTask 是 Future 接口的实现类
  • 实现步骤
    • 创建一个类实现Callable 接口
    • 创建一个FutureTask类将Callable接口的子类对象作为参数传进去
    • 创建Thread类,将FutureTask对象作为参数传进去
    • 开启线程

代码演示:

public class MyTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
 
        //Callable 返回结果并且可能抛出异常的任务。实现者定义了一个不带任何参数的叫做 call 的方法。
        //
        //Callable 接口类似于 Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的。但是 Runnable 不会返回结果,并且无法抛出经过检查的异常。


        MyCallable myCallable = new MyCallable();
        FutureTask<Integer> integerFutureTask = new FutureTask<>(myCallable);
        Thread th = new Thread(integerFutureTask);
        th.start();

        //获取线程执行完之后,返回的结果

        Integer integer = integerFutureTask.get();
        System.out.println(integer);


    }
}

public class MyCallable implements Callable<Integer> {

    //需要线程来执行的方法
    @Override
    public Integer call() throws Exception {
        System.out.println(Thread.currentThread().getName()+"  aaaaaa");


        return 100;
    }
}

  • 使用Callable开启两个线程,让第一个线程求出1~ 100的和,第二个线程求出1~1000的和
public class MyTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
     //第一个线程
        MyCallable myCallable = new MyCallable(100);
        FutureTask<Integer> futureTask = new FutureTask<>(myCallable);
        Thread th = new Thread(futureTask);
        th.start();
        Integer integer = futureTask.get();
        System.out.println(integer);
//第二个线程
        MyCallable myCallable1 = new MyCallable(1000);
        futureTask = new FutureTask<>(myCallable1);
        Thread th2 = new Thread(futureTask);
        th2.start();
        Integer integer1 = futureTask.get();
        System.out.println(integer1);


    }
}

public class MyCallable implements Callable<Integer> {
    int num;
    public MyCallable(int num) {
        this.num=num;
    }

    @Override
    public Integer call() throws Exception {
        int sum=0;
        for (int i = 1; i <=num; i++) {
            sum+=i;
        }
        System.out.println(Thread.currentThread().getName());
        return sum;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值