首页 > 编程语言 >Java学习笔记(十五)

Java学习笔记(十五)

时间:2023-08-20 14:35:06浏览次数:30  
标签:Java Thread void 笔记 线程 十五 run main public

第九章 多线程

9.1 多线程

这里只是讲一下多线程基础,后面Java高级会讲juc、多线程高级等


1、什么是多线程?

同一个程序同时做多个事情。


程序:为了完成某个任务,功能,而选择一种编程语言(例如:Java)编写的一组指令的集合。

进程:当程序启动时,操作系统会给这个程序分配一块独立的内存空间,以及相关的资源,

    每一个程序启动后有一个独立的进程对它进行管理。每一个进程都有自己唯一编号。

    操作系统是以进程为单位来管理资源。

线程:线程是进程中的其中一条执行路径。同一个进程中可以存在1个或多个的线程。

   例如:qq进程,它里面有多个线程

   (1)收消息线程

   (2)发消息线程

   (3)传输文件的线程

   ...


   最早的计算机是单任务单进程==>计算机同一时刻只能运行一个程序。

   后面出现了多任务多进程的系统==>计算机能同时运行多个程序。

   此时是单个CPU阶段的话,多个进程需要来回“切换”,在进程之间切换时,

   需要给进程做拍照处理,记录当前进程进行到那一步了,

   回头切换回来时,从这个位置继续执行。

   因为进程的内存等资源是不共享的,所以切换时等操作成本比较高。


   当我们多个任务具有相关性时,采用多线程,切换的成本更低一点,

   因为同一个进程的多个线程之间可以共享“堆、方法区”内存。


   如果多个CPU的话,多个进程可以同时进行,不需要切换,前提是进程数量<=CPU的数量。

   如果多个CPU的话,多个线程可以同时进行,不需要切换,前提是线程数量<=CPU的数量。


   线程是CPU调度的最小单位。

   一个进程至少有一个线程的。


并行:

   多个任务同时在多个CPU上运行。不管是微观角度还是宏观角度,都是同时运行的。

并发:

   多个任务“同时”在运行,其实是CPU在多个任务之间快速的“切换”,

   给“人”的感觉是同时的。

9.2 编写多线程程序

9.2.1 继承Thread类

2、Java程序:

(1)main线程:主线程

(2)后台还有一些其他服务线程:GC线程、类加载线程等等

(3)还可以手动再启动一些其他线程


3、如何手动启动其他的线程

(1)继承Thread

(2)实现Runnable

(3)实现Callable(高级)

(4)线程池(高级)


4、继承Thread类的方式步骤

(1)编写线程类,继承Thread

(2)必须重写public void run()方法

这个线程要干什么,就在run()里面写什么,称为线程体

(3)创建自定义线程类的对象

(4)启动线程

注意启动线程,调用start()方法,不要手动调用run()。

如果手动调用run()就不是多线程程序,成了单线程程序。


演示:

   单独启动一个线程,输出1-100之间的偶数

public class EvenThread extends Thread {
    @Override
    public void run() {
        //输出1-100之间的偶数
        for(int i=2; i<=100; i+=2){
            System.out.println("偶数i = " + i);
        }
    }
}
public class TestThread {
    /*public static void main(String[] args) {
        EvenThread evenThread = new EvenThread();
        evenThread.run();//当成普通对象调用方法,而不是多线程在工作

        //串行,先运行完上面的run()方法的所有代码,再运行下面的代码

        //在main线程中输出1-100的奇数
        for(int i=1; i<=100; i+=2){
            System.out.println("奇数i = " + i);
        }
    }*/

    public static void main(String[] args) {
        EvenThread evenThread = new EvenThread();
        evenThread.start();//从父类Thread类继承的

        //并行或并发,上面的线程的run方法和下面的代码是“同时”进行

        //在main线程中输出1-100的奇数
        for(int i=1; i<=100; i+=2){
            System.out.println("奇数i = " + i);
        }
    }
}

Java学习笔记(十五)_Runnable

Java学习笔记(十五)_Runnable_02

9.2.2 实现Runnable接口

5、实现Runnable接口的方式

(1)编写线程类,实参Runnable接口

(2)必须重写public void run()方法

这个线程要干什么,就在run()里面写什么,称为线程体

(3)创建线程类对象

(4)启动线程,需要借助Thread类的对象,才能调用start方法启动线程



6、两种方式的区别

(1)实现接口没有单继承限制

(2)其他的区别后面再说

public class EvenRunnable implements Runnable {
    @Override
    public void run() {
        //输出1-100之间的偶数
        for(int i=2; i<=100; i+=2){
            System.out.println("偶数i = " + i);
        }
    }
}
public class TestRunnable {
    public static void main(String[] args) {
        EvenRunnable evenRunnable = new EvenRunnable();
//        evenRunnable.start();//错误,因为EvenRunnable没有start方法,它的父类是Object
                            //父接口Runnable也没有start方法
        Thread thread = new Thread(evenRunnable);
        thread.start();

        //在main线程中输出1-100的奇数
        for(int i=1; i<=100; i+=2){
            System.out.println("奇数i = " + i);
        }
    }
}

9.2.3 匿名内部类

public class TestThreadRunnable {
    public static void main(String[] args) {
        //使用匿名内部类分别继承Thread和实现Runnable接口
        //一个输出1-100的偶数,一个输出1-100的奇数
        new Thread(){//继承Thread类的方式
            @Override
            public void run() {
                //输出1-100之间的偶数
                for(int i=2; i<=100; i+=2){
                    System.out.println("偶数i = " + i);
                }
            }
        }.start();

        /*Runnable runnable = new Runnable(){
            @Override
            public void run() {
                //输出1-100之间的奇数
                for(int i=1; i<=100; i+=2){
                    System.out.println("奇数i = " + i);
                }
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();*/

        new Thread(new Runnable(){
            @Override
            public void run() {
                //输出1-100之间的奇数
                for(int i=1; i<=100; i+=2){
                    System.out.println("奇数i = " + i);
                }
            }
        }).start();
    }
}

9.3 Thread类的方法

系列一、

7、Thread类的API方法

(1)String getName():获取线程的名字

(2)void setName(String name):设置线程名字

(3)public static Thread currentThread():获取当前线程对象,执行这句代码的线程对象

(4)public static void sleep(long time):线程休眠,运行这句代码的线程会休眠xx时间,单位是毫秒

(5)public void join():线程加塞,线程阻塞,

                   加塞是 a.join(),代表a线程会把当前线程(运行这句代码的线程)给阻塞了,

                   即当前线程被加塞了,直到a线程完事。

    public void join(long time)

                   加塞是 a.join(时间),代表a线程会把当前线程(运行这句代码的线程)给阻塞了,

                    即当前线程被加塞了,直到时间到了,当前线程恢复。

public class TestMethod {
    public static void main(String[] args) {
        ThreadDemo threadDemo1 = new ThreadDemo();
        ThreadDemo threadDemo2 = new ThreadDemo();
        ThreadDemo threadDemo3 = new ThreadDemo("线程3");
        ThreadDemo threadDemo4 = new ThreadDemo();

        threadDemo4.setName("线程4");

        threadDemo1.start();
        threadDemo2.start();
        threadDemo3.start();
        threadDemo4.start();

//        System.out.println(getName());//错误,(1)TestMethod没有继承Thread类(2)main方法是静态方法,不能直接调用非静态方法
        Thread currentThread = Thread.currentThread();
        System.out.println("主线程的名字: " + currentThread.getName());

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("其他代码");

    }
}
class ThreadDemo extends Thread{
    public ThreadDemo() {
    }

    public ThreadDemo(String name) {
        super(name);
    }

    @Override
    public void run() {
        System.out.println(getName());//getName()从父类Thread类继承的
    }
}
public class TestJoin {
    public static void main(String[] args) {
        PrintNum p = new PrintNum();
        p.start();

        PrintChar printChar = new PrintChar();
        printChar.start();

        try {
//            p.join();
            p.join(3000);//阻塞当前线程,main线程,因为这句代码由main线程调用
                            //和printChar线程无关。
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("main");
    }
}
class PrintNum extends Thread{
    @Override
    public void run() {
        for(int i=1; i<=10; i++){
            System.out.println("i = " + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
class PrintChar extends Thread{
    @Override
    public void run() {
        for(char c = 'a'; c<='z'; c++){
            System.out.println("c = " + c);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

系列二、

1、Thread类API
部分构造器:
- public Thread() :分配一个新的线程对象。
- public Thread(String name) :分配一个指定名字的新的线程对象。
- public Thread(Runnable target) :分配一个带有指定目标新的线程对象。
- public Thread(Runnable target,String name) :分配一个带有指定目标新的线程对象并指定名字。

方法们:
- public void run() :此线程要执行的任务在此处定义代码。
- public void start() :导致此线程开始执行; Java虚拟机调用此线程的run方法。
- public String getName() :获取当前线程名称。
- public void setName(String name):设置当前线程的名称。
如果不设置线程名称,有默认名字,Thread-编号。
- public static Thread currentThread() :返回当前正在执行的线程对象的引用。
- public final int getPriority() :返回线程优先级
- public final void setPriority(int newPriority) :改变线程的优先级
    说明:当线程的优先级比较高时,可以获得更多的CPU调度的机会。
        但是不代表线程优先级低的完全没有机会。
    优先级的值范围是:【1,10】
- public static void sleep(long millis) :使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
- public static void yield():yield只是让当前线程暂停一下,让系统的线程调度器重新调度一次,
    希望优先级与当前线程相同或更高的其他线程能够获得执行机会,但是这个不能保证,完全有可能的情况是,
    当某个线程调用了yield方法暂停之后,线程调度器又将其调度出来重新执行。
- public void join() :等待该线程终止。
  public void join(long millis) :等待该线程终止的时间最长为 millis 毫秒。如果millis时间到,将不再等待。
  public void join(long millis, int nanos) :等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。
public class TestMethod {
    public static void main(String[] args) {
        Thread currentThread = Thread.currentThread();
        //因为是main线程在执行上面这句代码,所以得到的是main线程对象
        System.out.println(currentThread);

        MyRunnable my = new MyRunnable();
        Thread t = new Thread(my);
        t.setName("t线程");
        t.start();

        Thread t2 = new Thread(my);
        t2.setName("t2线程");
        t2.start();
    }
}

class MyRunnable implements Runnable{
    @Override
    public void run() {
        Thread currentThread = Thread.currentThread();
        //因为这句代码是在MyRunnable的run方法中,那么这里获取的是t线程对象或t2线程对象
        System.out.println("当前线程名称:" + currentThread.getName());
    }
}
public class TestPriority {
    public static void main(String[] args) {
        /*
        一个线程打印1-100奇数,一个线程打印1-100的偶数,
        把打印奇数的线程的优先级设置为最高,
        把打印偶数的线程的优先级设置为最低,
         */
        Thread even = new Thread(){
            @Override
            public void run() {
                for(int i=2; i<=100; i+=2){
                    System.out.println("偶数:" + i);
                }
            }
        };
        Thread odd = new Thread(){
            @Override
            public void run() {
                for(int i=1; i<=100; i+=2){
                    System.out.println("奇数:" + i);
                }
            }
        };

       // even.setPriority(100);//java.lang.IllegalArgumentException非法参数异常
        /*
        查找某个类的源码的快捷键:
        (1)Ctrl + n:打开搜索框,输入类名
        (2)Ctrl + F12:打开某个类的成员列表
        (3)如果类名和方法名已使用,直接按Ctrl键+单击类名或方法名,也可以定位源码

        public final static int MIN_PRIORITY = 1;  最低优先级
        public final static int NORM_PRIORITY = 5;  普通优先级
        public final static int MAX_PRIORITY = 10;  最高优先级
         */
        even.setPriority(10);
        odd.setPriority(1);
        even.start();
        odd.start();
    }
}
public class TestYield {
    public static void main(String[] args) {
        /*
        在main线程中输出1-100的偶数,
        在另一个线程中输出1-100的奇数
         */

        Thread even = new Thread(){
            @Override
            public void run() {
                for(int i=2; i<=100; i+=2){
                    System.out.println("偶数:" + i);
                }
            }
        };
        even.setPriority(10);
        Thread.currentThread().setPriority(1);
        even.start();

        for(int i=1; i<=100; i+=2){
            System.out.println("奇数:" + i);
            if(i==5){
                Thread.yield();//当前线程暂停,让出CPU
            }
        }
    }
}

系列三、

  @Deprecated

 public final void stop()已过时。

 如何解决让某个线程提前停止的操作?

public class TestStop {
    static boolean flag = true;
    public static void main(String[] args) {
        /*
        两个线程:
        1、一个线程输出1-100的偶数
        2、另一个线程输出1-100的奇数
        3、偶数线程是连续打印数字
        4、奇数线程是每隔1毫秒打印一个数字
        5、当偶数线程打印完了,可以奇数线程提前停下来,就算奇数线程没有打印完所有的奇数

        思路之一:标记法
         */
        Thread even = new Thread(){
            @Override
            public void run() {
                for(int i=2; i<=100; i+=2){
                    System.out.println("偶数:" + i);
                }
                flag = false;
            }
        };
        Thread odd = new Thread(){
            @Override
            public void run() {
                for(int i=1; i<=100 && flag; i+=2){
                    System.out.println("奇数:" + i);
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };

        even.start();
        odd.start();
    }
}

系列四、

- public final void setDaemon(boolean on)将该线程标记为守护线程或用户线程。

       当正在运行的线程都是守护线程时,Java 虚拟机退出。


 守护线程是一种特殊的线程,特殊在它不能独立存在。当JVM中,所有非守护线程都结束了,那么守护线程会自动结束。

public class TestDaemon {
    public static void main(String[] args) {
        //主线程先启动PrintNumber线程,并把PrintNumber线程对象设置为守护线程
        PrintNumber p = new PrintNumber();
        p.setDaemon(true);
        p.start();


    }

/*    @Test
    public void test(){
        PrintNumber p = new PrintNumber();
        p.start();
    }*/
}

class PrintNumber extends Thread{
    @Override
    public void run() {
        int num = 1;
        while(true){
            System.out.println("num =  " + num);
            num++;
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

系列五、

-  public void interrupt() 中断线程

public class Sporter extends Thread {
    private long timePerMeter;
    private long restTime;
    private long time;
    private static volatile boolean flag = true;//这里是static,所有运动员共享
    //volatile表示每一个线程不缓存该变量的值,直接从主存里面读取该变量的值,
    //如果缓存了,也及时和主存中的值同步,保证一致性
    private int distance;//记录运动员跑了多少米

    public Sporter(String name, long timePerMeter, long restTime) {
        super(name);
        this.timePerMeter = timePerMeter;
        this.restTime = restTime;
    }

    @Override
    public void run() {
        long start = System.currentTimeMillis();

        for(int i=1; i<=30 && flag; i++){
            try {
                Thread.sleep(timePerMeter);//模拟跑1米的时间
            } catch (InterruptedException e) {
//                e.printStackTrace();
                System.err.println(getName()+"线程被中断了,异常信息:" + e);
                continue;
            }
            System.out.println(getName() + "跑了" +i + "米");
            distance++;
            if(i==10 || i==20){//i%10==0,会包含30米
                System.out.println(getName() + "开始休息....");
                try {
                    Thread.sleep(restTime);//模拟休息时间
                } catch (InterruptedException e) {
//                    e.printStackTrace();
                    System.err.println(getName()+"线程被中断了,异常信息:" + e);
                    continue;
                }
            }
        }

        if(distance == 30){
            System.out.println(getName() +"已经到达终点");
            flag = false;
        }

        long end = System.currentTimeMillis();
        time = end - start;
    }

    public long getTime() {
        return time;
    }

    public int getDistance() {
        return distance;
    }

    public static boolean isFlag() {
        return flag;
    }
}
public class Exercise5 {
    public static void main(String[] args) {
        Sporter rabbit = new Sporter("兔子",100,10000);
        Sporter tortoise = new Sporter("乌龟",1000,1000);

        rabbit.start();
        tortoise.start();

        while(true){
//            if(Sporter.isFlag()==false){
            if(!Sporter.isFlag()){
                rabbit.interrupt();
                tortoise.interrupt();
                break;
            }
        }

        System.out.println("兔子跑了:" + rabbit.getDistance()+",耗时:" + rabbit.getTime());
        System.out.println("乌龟跑了:" + tortoise.getDistance()+",耗时:" + tortoise.getTime());

        if(rabbit.getDistance() > tortoise.getDistance()){
            System.out.println("兔子赢");
        }else if(rabbit.getDistance() < tortoise.getDistance()){
            System.out.println("乌龟赢");
        }else {
            if (rabbit.getTime() < tortoise.getTime()) {
                System.out.println("兔子赢");
            } else if (rabbit.getTime() > tortoise.getTime()) {
                System.out.println("乌龟赢");
            } else {
                System.out.println("平局");
            }
        }
    }
}


标签:Java,Thread,void,笔记,线程,十五,run,main,public
From: https://blog.51cto.com/u_16213911/7161374

相关文章

  • 蓝桥杯Web笔记
    一、node.js1、概念(1)Node.js是一个免费的、开源的、跨平台的JavaScript运行时环境,不是一门语言,也不是一个框架、库,而是一个JavaScript的运行环境。让JavaScript脱离了浏览器,能够允许开发人员在浏览器之外编写命令行工具和服务器端脚本例如:我们常关注的浏览器的兼容性,PC端和手机......
  • JavaWeb程序设计基础
    基础认识1、web技术简介(1)HTML:HTML1.0->HTML5.0(H5),标签是语法(2)CSS:样式表,让页面美起来(3)JavaScript:脚本,让页面动起来(4)DOM:元素的树结2、动态网页技术(1HTML是编制静态网页的基本语言(2)动态网页:可以根据访问者的不同需要,对访问者输入的信息提供不同响应的网页不同的人、不同的时间、不同的......
  • FEMU模拟器学习笔记
     QEMU参数解析  QEMU的main函数进来后,首先要进行参数解析。一个启动模拟器的命令行如下:x86_64-softmmu/qemu-system-x86_64-name"FEMU-ZNSSD-VM"-enable-kvm-cpuhost-smp2-m4G-devicevirtio-scsi-pci,id=scsi0-devicescsi-hd,drive=hd0-drivefile=/home......
  • JavaScript​​-null 、 undefined和布尔值
    null和undefined 概述 null与undefined都可以表示“没有”,含义非常相似。将一个变量赋值为undefined或null,老实说,语法效果几乎没区别。vara=undefined;//或者vara=null;上面代码中,变量a分别被赋值为undefined和null,这两种写法的效果几乎等价。在if语句中,它们都会被自......
  • 基于Java的校园打字练习系统的设计与实现-计算机毕业设计源码+LW文档
    一、研究目的和意义研究目的:随着互联网的进一步发展,打字这项技能几乎成为所以大学生的必备技能。校园打字系统已经成为大学生练习打字的重要帮手,它可以将古代和现代诗歌、网红文章、中外著名名著全部存储在里面,便于使用者自行选择文本进行练习打字;随着电脑的普及,各行各业都会有使......
  • 【刷题笔记】26. Remove Duplicates from Sorted Array
    题目Givenasortedarraynums,removetheduplicatesin-placesuchthateachelementappearonlyonceandreturnthenewlength.Donotallocateextraspaceforanotherarray,youmustdothisbymodifyingtheinputarrayin-placewithO(1)extramemory.E......
  • 贪心,构造学习笔记
    贪心构造不会黄题绿题懵逼横批:依托答辩\(\text{CF1764C}\)题目描述有一些点,每一个点有一个点权\(a_i\)。你可以在任意点之间连边,最终的图需要满足不存在\(a,b,c\)满足\(a_a\leqslanta_b\leqslanta_c\)并且\(ab,bc\)之间有连边。思路点拨我们连出来的图一定可以......
  • 快速理解什么是 JavaScript 的继承
    其实JavaScript中的继承,就是指的是一种机制,它允许一个对象(子对象)获取另一个对象(父对象)的属性和方法。这样的话子对象可以重用父对象的代码,同时还可以添加自己的特定功能。JavaScript是使用原型继承的方式来实现对象之间的继承关系。首先你肯定得知道一些基本概念:对象:在JavaScrip......
  • Java Collection集合体系
    Collection集合体系Collection集合常见方法List集合遍历方式什么是迭代器ArrayList集合底层LinkedList集合底层Set集合HashSet集合底层LinkedHashSet底层TreeSet底层......
  • Java为什么不让用指针?
    指针是C和C++等语言中的一个强大工具,它们允许开发者直接访问内存地址,从而为高性能的系统级编程提供了可能性。然而,Java在设计时刻意避免了指针的使用。为什么?2.Java的设计哲学简单性和可读性:Java的设计目标之一是使语言更加简单和容易理解,从而降低错误的可能性。指针经常被认为是......