Java多线程并发笔记(1.1)线程与线程的实现、启动

前言

记录学习过程
Java线程并发
开发工具:IDEA
环境:jdk1.8

目录

  1. 概念
  2. 创建线程
    2.1.继承Thread类
    2.2.实现Runnable接口
    2.3.匿名内部类
  3. 线程优先级
  4. 线程的其他操作方法
  5. 总结

线程

概念

曾经在操作系统就学过相关概念
操作系统是计算机的管理者,它负责任务的调度,资源的分配管理,硬件的使用,而我们评测编程的程序就是在操作系统之上运行的
进程:就是一个任务或者程序的动态执行过程,是操作系统进行资源调度分配的一个独立的单位,是程序运行的载体
线程:进程越来越复杂,就有了线程的概念,一个程序或者任务需要实现多个子任务才能完成,这些子任务就是线程,总的说线程是进程的执行单元,是程序执行流的最小单元,是处理器调度和分派的基本单位

早期计算机简单,进程是程序执行的最小单位,随着计算机的发展,进程的切换开销越来越大,就需要一个更小的单元–线程,一个进程可以有一个或多个线程

最直观的:这就叫进程
在这里插入图片描述

创建线程

在这里插入图片描述
常见的创建线程的方法:继承Thread类、实现Runnable接口,匿名内部类
更高级的方法:ExecutorService、Callable< Class>、Future 有返回值线程,基于线程池创建线程,这些是下一个学习的目标

继承Thread类

创建线程:

package com.company.Thread;

public class OpenThread {
    static class CreatThread1 extends Thread{
        public void run(){
            for(int i=0;i<30;i++)
                System.out.println(i+":运行线程1");
        }
    }
    static class CreatThread2 extends Thread{
        public void run(){
            for(int i=0;i<300;i++)
                System.out.println(i+":运行线程2");
        }
    }
    public static void main(String[] args){
        CreatThread1 thread1=new CreatThread1();
        CreatThread2 thread2=new CreatThread2();
        //使用线程的启动方法start
        thread1.start();//启动线程1
        thread2.start();//启动线程2
        for(int i=0;i<3000;i++)
            System.out.println(i+":运行主程序");
    }
}

在这里插入图片描述
注意:启动线程的唯一方法就是通过 Thread 类的 start()实例方法,start()方法是一个 native 方法,它将启动一个新线
程,并执行 run()方法

运行顺序不一样,这就涉及到多线程的并发问题

通过继承Thread类实现线程类缺点在与不能继承其他父类了
其实Thread 类本质上是实现了 Runnable 接口的一个实例,代表一个线程的实例
查看源码:
在这里插入图片描述

实现Runnable接口

package com.company.Thread;

public class OpenThread {
    static class CreatThread1 implements Runnable{
        public void run(){
            for(int i=0;i<30;i++)
                System.out.println(i+":运行线程1");
        }
    }
    static class CreatThread2 implements Runnable{
        public void run(){
            for(int i=0;i<300;i++)
                System.out.println(i+":运行线程2");
        }
    }
    public static void main(String[] args){
        CreatThread1 thread1=new CreatThread1();
        CreatThread2 thread2=new CreatThread2();
        //使用线程的启动方法start
        //启动线程需要实例化Thread,并传入我们的实例
        new Thread(thread1).start();//启动线程1
        new Thread(thread2).start();//启动线程2
        for(int i=0;i<3000;i++)
            System.out.println(i+":运行主程序");
    }
}

在这里插入图片描述

线程运行抢占cpu

匿名内部类

匿名内部类的方法只是改变了写法而已
关于匿名内部类的知识:JavaSE笔记(2.4)Java基础 - 内部类、Lambda表达式

package com.company.Thread;

public class OpenThread {
    static class CreatThread1 implements Runnable{
        public void run(){
            for(int i=0;i<30;i++)
                System.out.println(i+":运行线程1");
        }
    }
    static class CreatThread2 implements Runnable{
        public void run(){

        }
    }
    public static void main(String[] args){
        
        CreatThread1 thread1=new CreatThread1() {
            public void run() {
                for (int i = 0; i < 300; i++)
                    System.out.println(i + ":运行线程2");
            }
        };
        CreatThread2 thread2=new CreatThread2();
        //使用线程的启动方法start
        //启动线程需要实例化Thread,并传入我们的实例
        new Thread(thread1).start();//启动线程1
        new Thread(thread2).start();//启动线程2
        for(int i=0;i<3000;i++)
            System.out.println(i+":运行主程序");
    }
}

在这里插入图片描述
我们知道Lambda表达式是匿名类的简写版,我们可以继续简写

package com.company.Thread;

public class PersonThread {
    static class CreatThread1 {
        public void run() {
            for (int i = 0; i < 30; i++)
                System.out.println(i + ":运行线程1");
        }
    }
    public static void main(String[] args) {
        CreatThread1 creatThread1=new CreatThread1();
        new Thread(()->creatThread1.run()).start();
    }
}

甚至可以不用继承Thread类或者Runnable接口

其实还可以更简单

package com.company.Thread;

public class PersonThread {
    static class CreatThread1 {
        public void run() {
            for (int i = 0; i < 30; i++)
                System.out.println(i + ":运行线程1");
        }
    }
    public static void main(String[] args) {
        CreatThread1 creatThread1=new CreatThread1();
        new Thread(creatThread1::run).start();
    }
}

但是匿名类、Lambda表达式效率太差,虽然很炫

线程优先级

前面的写法没有设置优先级,所有线程都是默认普通优先级,就可以自由的去抢占cpu

当设置了优先级,就可以让线程根据优先级去占据cpu

优先级可以用从1到10的范围指定。10表示最高优先级,1表示最低优先级,5是普通优先级

通过Thread.setPriority()方法设置优先级
在这里插入图片描述

package com.company.Thread;

public class OpenThread {
    public final static int MIN_PRIORITY = 1;
    public final static int NORM_PRIORITY = 5;
    public final static int MAX_PRIORITY = 10;
    static class CreatThread1 implements Runnable{
        public void run(){
            for(int i=0;i<30;i++)
                System.out.println(i+":运行线程1");
        }
    }
    static class CreatThread2 implements Runnable{
        public void run(){
            for(int i=0;i<300;i++)
                System.out.println(i+":运行线程2");
        }
    }
    public static void main(String[] args){

        CreatThread1 thread1=new CreatThread1();
        CreatThread2 thread2=new CreatThread2();

        //使用线程的启动方法start
        //启动线程需要实例化Thread,并传入我们的实例
        //启动线程1
        Thread myThread1=new Thread(thread1);
        //设置优先级
        myThread1.setPriority(MAX_PRIORITY);
        myThread1.start();
        //启动线程2
        Thread myThread2=new Thread(thread2);
        myThread2.setPriority(MIN_PRIORITY);
        myThread2.start();

/*        for(int i=0;i<3000;i++)
            System.out.println(i+":运行主程序");*/
    }
}

在这里插入图片描述
设置了线程1为最高优先级,确实是先运行完线程1再运行线程2

那反过来会怎样?
在这里插入图片描述
线程2运行到两百多时,线程1也开始运行了
这是为什么?

1、java线程是通过映射到系统的原生线程上来实现的,所以线程的调度最终还是取决于操作系统,操作系统的优先级与java的优先级并不一一对应,如果操作系统的优先级级数大于java的优先级级数(10级)还好,但是如果小于得的话就不行了,这样会导致不同优先级的线程的优先级是一样的。
2、优先级可能会被系统自动改变,比如windows系统中就存在一个优先级推进器,大致功能就是如果一个线程执行的次数过多的话,可能会越过优先级为他分配执行时间

线程的调度最终还是要取决于操作系统的线程规划器

我们的程序最终还是要在操作系统中执行,而Thread2运行次数太多了,Thread1等待时间太长,操作系统越过优先级运行Thread1

线程的其他操作方法

线程休眠方法为:sleep();

改一下前面运行线程2的操作:

        //启动线程2
        Thread myThread2=new Thread(thread2);
        myThread2.setPriority(MAX_PRIORITY);
        //睡眠1秒
        try {
            myThread2.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        myThread2.start();

在这里插入图片描述
线程2优秀级最高,但是因为睡眠了1秒,线程1先运行了

暂停线程使用Thread中的suspend()方法;恢复暂停的线程使用resume()方法(过时)-- 容易造成死锁
可以去jdk文档查看这些方法
在这里插入图片描述
在这里插入图片描述

总结

复习了一下进程、线程的概念,线程的三种创建启动方式,线程优先级和一些线程的操作方法
接下来要学习多线程并发

发布了49 篇原创文章 · 获赞 0 · 访问量 1228

猜你喜欢

转载自blog.csdn.net/key_768/article/details/104275543