01.04_学习Java的day15(详解)

一、线程相关的名称解释(了解)

1、什么是多线程

之前自己写的代码,都是单线程的,程序自上而下顺序执行,只有一条执行路径。
现在我们希望我们自己写的程序,能够同时做好几件事情,例如:一边聊天,并且支持多个好友聊天,一边传文件。
这个时候就需要开启多线程。
即多线程就是指一个应用程序有多条执行路径同时运行。

2、单线程和多线程的区别

(1)单线程:如果其中一句代码阻塞了,剩下的代码无法运行,我们CPU等资源对于这个线程来说就停止了。
相当于单行线,堵车
(2)多线程:如果其中一个线程阻塞了,只是这个线程阻塞,其他线程正常运行。

3、并行与并发

并行(parallel):指两个或多个事件在同一时刻发生(同时发生)。
要求:有多个处理器(CPU)
比喻:路线是多行线,过收费站,有多个收费口。
并发(concurrency):指两个或多个事件在同一个时间段内发生。指在同一个时刻只能有一条指令执行,但多个进程的指令被快速轮换执行,使得在宏观上具有多个进程同时执行的效果。
不要求:有多个处理器
比喻:路线是多行线,过收费站,只有一个收费口。
但是这个收费口动作非常快。
如果线程个数增多,等待的情况会越来越明显。

现在的程序,基本上是并行与并发同时存在。

4、进程与线程

程序(Programm):选择任意一种编程语言(C,C++,Java,Python等),为了完成一个任务,功能,而编写了一段代码。
这段代码最终会被编译为一组指令。那么一个程序就是一组指令的集合。
如果程序没有运行,相当于存储在硬盘上的一个普通的文件。
软件(Software):一个软件中可能有一个或多个的程序。
软件 = 一个或多个的程序 + 资源文件(图片,音频素材等)

            码农、程序员(关注是写代码),软件工程师(初级、中级、高级)。

进程(Process):是程序的一次运行。即一个程序如果运行了多次,就会有多个进程。
操作系统分配内存等资源是以进程为单位的。不同的进程之间是不能共享内存资源的。哪怕是同一个应用程序的两次运行,
也是独立的两个进程,也无法共享内存资源。
进程之间的数据共享的成本很高,需要通过:文件、网络通信才能实现数据的共享。
而且进程之间的切换,也是成本很高。因为目前的操作系统成为多任务,那么CPU同一个时刻只能处理一个任务,
但是我们电脑会同时启动很多个进程,就算现在多核处理器,仍然数量不可能一个处理器负责一个进程,
所以需要轮循或抢夺。每一次轮换,操作系统需要记录当前这个进程运行到哪个指令,目前的各个资源的状态是什么样的。
线程(Thread):线程是一个进程中的其中一条执行路径。
一个进程至少有一个线程。如果只有一个线程,称为单线程程序。
如果一个进程中有多个线程,称为多线程程序。
同一个进程中的多个线程是可以共享内存的,例如:堆内存、方法区内存。
那么,在同一个进程中的多个线程之间需要数据共享的话,就比较简单。

               另一点,同一个进程中的多个线程如果需要轮循或抢夺,那么成本相对比较低,我们只要记录当前线程的执行到哪一句指令即可,
                JVM中有一块内存区域:程序计数器,它就负责记录每一个线程目前执行到哪一个指令了,下一条指令是什么。

                并且每一个线程又有独立的内存,例如:栈内存

                有的时候会把线程称为轻量级的进程。进程是操作系统分配资源和调度的最小单位。线程是CPU调度的最小单位。

                多线程和多进程的目标:可以同时做好几件事情,然后可以充分利用CPU资源。

JVM内存:
(1)方法区:加载的类信息,静态变量,常量等
(2)堆:存储new的对象
(3)栈:局部变量
A:虚拟机栈:Java方法
B:本地方法栈 :native方法
(4)程序计数器:记录每一个线程的下一条指令

实现java.lang.Runnable接口

步骤:
(1)自定义一个线程类,实现Runnable接口
(2)必须重写父接口的一个方法:run方法
public void run(){
线程体:这个线程要完成的任务代码
}
(3)创建自定义线程类的对象
(4)启动线程:
A:创建Thread类的对象,并把自定义线程类的对象传给Thread类的对象
B:调用Thread类的对象的start()
当线程启动之后,相当于将线程交给了线程调度器来管理。

main是一个线程
my是另一个线程对象
public class Runnable {
    public static void main(String[] args) {
        MyRunnable my = new MyRunnable();
//        my.start();//无法直接调用start(),因为父类Object中没有,父接口Runnable没有。

        //刚才我们说java.lang.Thread类有,我们借用Thread类的start方法帮我们启动线程
        //所以我们需要创建Thread类的对象,并且在创建Thread类的对象的时候,告诉它执行谁的run()
        Thread t = new Thread(my);//使用了Thread(Runnable target) 构造器
        t.start();


    }
}
class MyRunnable implements Runnable{

    @Override
    public void run() {
        //例如:打印1-10之间的偶数
        for (int i=2; i<=100; i+=2){
            System.out.println("i = " + i);
        }
    }
}

多线程不是Java的概念。
属于计算机操作系统原理中的概念。
Java支持多线程开发。

多线程的课程分两个阶段:JavaSE(一天)、Java高级(juc)

二、Java中如何支持多线程开发

1、JavaSE中有两种方式:

(1)继承Thread类
(2)实现Runnable接口

2、继承java.lang.Thread类

步骤:
(1)自定义一个线程类,继承Thread类
(2)必须重写父类的一个方法:run方法
public void run(){
线程体:这个线程要完成的任务代码
}
(3)创建自定义线程类的对象
(4)启动线程:调用线程对象的start()
当线程启动之后,相当于将线程交给了线程调度器来管理。

main是一个线程,
my是一个线程
public class Thread {
    public static void main(String[] args) {
        MyThread my = new MyThread();
//        my.run();//相当于调用普通的方法,不是多线程运行机制,只能顺序执行,
                // 即下面的代码必须等run()结束之后才能运行,不是同时运行

        my.start();//启动my线程之后,my线程的run()方法中的代码将与接下来main方法中代码,“同时”运行
        //start()是从Thread类继承的

        //打印1-10的奇数
        for (int i=1; i<=100; i+=2){
            System.out.println("main: i = " + i);
            try {
                if(i==5){
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class MyThread extends Thread{
    @Override
    public void run() {
        //例如:打印1-10之间的偶数
        for (int i=2; i<=100; i+=2){
            System.out.println("i = " + i);
        }
    }
}

三、Thread类的API方法

1、构造器

Thread()
Thread(Runnable target)
Thread(String name)
Thread(Runnable target, String name)

2、Thread常用方法

(1)getName():获取线程名称
默认情况下,线程的名称是:Thread-编号
例如:Thread-0
例如:Thread-1
(2)获取当前线程对象的静态方法
public static Thread currentThread()
(3)getPriority():获取线程的优先级
setPriority():设置线程的优先级
优先级高低会影响线程抢夺CPU的概率,如果优先级高的,抢到CPU的概率会提高。
优先级一共有10个等级:为1-10,不能超过这个范围。
如果优先级不在 MIN_PRIORITY 到 MAX_PRIORITY 范围内,就会报IllegalArgumentException。
MIN_PRIORITY:1
MAX_PRIORITY:10
NORM_PRIORITY:5

public class TestThreadMethod1 {

    public void test01(){
        Thread t = new Thread();
        System.out.println(t.getName());//Thread-0
    }

//    @Test
//    public void test02(){
    //如果使用JUnit测试线程,要注意:JUnit的@Test的方法也是一个线程,如果它结束了,其他线程也会结束,因为JVM退出了

    public static void main(String[] args) {
        System.out.println("hello main");//这句代码一定先执行,剩下的线程还未start

        //如果使用main方法测试线程,当其他线程没有结束,此时就算main方法代码已经完成了,也不会退出JVM
        new Thread(){
            public void run(){
                for (int i=1; i<=5; i++){
                    //getName()从Thread父类继承的
                    System.out.println(getName()+":i="+i);
                }
            }
        }.start();

        //new 父类(实参列表){...}
        //说明匿名内部类中调用了父类的有参构造,super(name)
        new Thread("自定义线程2"){
            public void run(){
                for (int i=1; i<=5; i++){
                    //getName()从Thread父类继承的
                    System.out.println(getName()+":i="+i);
                }
            }
        }.start();
        System.out.println("-----------------------------");//main线程负责打印---

        new Thread(new Runnable(){
            public void run(){
                for (int i=1; i<=5; i++){
                    //Thread.currentThread(),获取当前线程对象,即当前哪个线程再执行这句代码,得到的就是哪个线程对象,它也是Thread类型的
                    System.out.println(Thread.currentThread().getName()+":i="+i);
                }
            }
        }).start();

        new Thread(new Runnable(){
            public void run(){
                for (int i=1; i<=5; i++){
                    System.out.println(Thread.currentThread().getName()+":i="+i);
                }
            }
        },"自定义线程4").start();
    }
}

public class ThreadMethod2 {
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        t1.start();

        MyThread t2 = new MyThread();
        t2.setPriority(10);
        t2.start();

        MyThread t3 = new MyThread();
       // t3.setPriority(100);//IllegalArgumentException
        t3.start();

        //如何获取main线程的名称和优先级呢?
        System.out.println("主线程的名称:" + Thread.currentThread().getName());
        System.out.println("主线程的优先级:" + Thread.currentThread().getPriority());

    }
}
class MyThread extends Thread{
    public void run(){
        System.out.println(getName() + "的优先级:" + getPriority());
    }
}

3、Thread类的API方法

(4)线程的休眠:
public static void sleep(long millis) throws InterruptedException
public static void sleep(long millis, int nanos)throws InterruptedException
InterruptedException:编译时异常

(5)线程的暂停:
public static void yield():让出当前线程的CPU资源,当前线程会与其他等待CPU资源的线程再次竞争
如果其他线程的优先级比较高,那么当前线程yield暂停之后,CPU资源被其他线程抢走的概率很高。

(6)线程的加塞:
public final void join()throws InterruptedException
无限制加塞,直到加塞的线程结束,被加塞的线程才有机会。
public final void join(long millis)throws InterruptedException
public final void join(long millis, int nanos)throws InterruptedException
限时加塞,在指定时间范围内,被加塞线程没有机会抢夺CPU资源。

public class ThreadMethod3 {
    public static void main(String[] args) {
        new Thread(){
            public void run(){
                for(int j=1; j<=100; j++){
                    System.out.println("j = " + j);
                }
            }
        }.start();

        //倒计时的效果
        //下面的代码由main线程负责
        for (int i=10; i>=1; i--){
            System.out.println(i);

            /*try {
                //try...catch围绕:选中代码,按Ctrl +Alt + T,选中try..catch
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }*/
            if(i==10) {
                Thread.yield();
            }
        }
    }
}

public class ThreadMethod4 {
    public static void main(String[] args) {
        TimeCount t = new TimeCount();
        t.start();

        for (int i=1; i<=10; i++){
            System.out.println("main,i=" + i);
            try {
                if(i==2){
                    //main线程被t线程加塞
                    //无限制加塞,main线程要等t线程结束,才能继续
//                    t.join();
                    //限时加塞,t线程会加塞 500毫秒,0.5秒
                    t.join(500);

                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
class TimeCount extends  Thread{
    public void run(){
        for (int i=2; i>=1; i--){
            System.out.println("i = " + i);
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值