Java----多线程详解

多线程

并发与并行

1.并行:指两个或多个时间在同一时刻发生(同时发生),多个cpu(核)的基础上,每个核执行一个任务,多个核同时执行。

2.并行:指两个或多个事件在同一时间段内发生

线程与进程

1.进程:
是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序既是一个进程从创建,运行到消亡的过程。

2.线程:
进程内部的一个独立执行单元;一个进程可以同时并发的运行多个线程,可以理解为一个进程便相当于一个单cpu操作系统,而线程便是这个系统中运行的多个任务。

进程与线程的区别

进程:有独立的内存空间,进程中的数据存放空间(堆空间和栈空间)是独立的,至少有一个线程。

线程:堆空间是共享的,栈空间是独立的,线程消耗的资源比进程小的多。

注:
1.因为一个进程中的多个线程是并发运行的,那么从微观角度看也是有先后顺序的,哪个线程执行完全取决于cpu的调度,程序员是干涉不了的,这就造成了多线程的随机性。

2.java程序的进程里面至少包含两个线程,主线程就是main方法线程,另外一个是垃圾回收机制线程,每当使用Java命令执行一个类的时候,实际上都会启用一个JVM,每一个JVM实际上就是在操作系统中启动了一个线程,Java本身启用垃圾回收机制的线程,所以至少会启动两个线程。

3.因为创建一个线程的开销比创建一个进程的开销小的多,那么我们在开发多任务运行的时候,通常考虑创建多线程,而不是创建多进程。

线程调度

计算机通常只有一个cpu时,在任意时刻只能执行一条计算机指令,每一个进程只有获得cpu的使用权才能执行指令。所谓多线程并发运行,其实是各个进程在很微小的时间片内轮流获得cpu的使用权,分别执行各自的任务;在可运行池中,会有多个线程处于就绪状态等待cpu,JVM就负责线程调度,JVM采用的是抢占式调度,没有采用分时调度,这样就会造成多线程执行的随机性

多线程实现的两种方式

java语言内置多线程功能支持

一、继承Thread类

构造方法
1.public Thread():分配一个新的线程对象

2.public Thread(String name):分配一个指定名字的新的线程对象

常用方法
1.public String getName():获取当前线程名称

2.public void start():导致此线程开始执行; Java虚拟机调用此线程的run方法

3.public void run():此线程要执行的任务在此处定义代码

4.public static void sleep(long millis):使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)

5.public static Thread currentThread():返回对当前正在执行的线程对象的引用

public class test1 extends Thread {
    @Override
    public void run() {
        //run是编写线程执行的任务的地方
        for (int i=0;i<40;i++){
            System.out.println(Thread.currentThread().getName()+"正在执行。。。。"+i);
            try {
                sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class test2 extends Thread {
    @Override
    public void run() {
        //run是编写线程执行的任务的地方
        for (int i=0;i<40;i++){
            System.out.println(Thread.currentThread().getName()+"正在执行。。。。"+i);
            try {
                sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class Test_Thread{
    public static void main(String[] args) {
        test1 t1 = new test1();
        test2 t2 = new test2();
        t1.setName("线程1");
        t2.setName("线程2");
        //启动线程
        t1.start();
        t2.start();
        //主线程,和其他线程一起执行
        for(int i=0;i<50;i++){
            System.out.println("线程CCCC正在执行。。。。"+i);
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
二、实现Runnable接口

1.实现Runnable接口,重写接口中的run方法,该run()方法的方法体也是该线程的线程执行体。

2.创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,因为Thread类提供了构造方法,把Runnable实现类作为参数传递,这样这个实现类就可以通过Thread对象使用Thread类中的start()方法启动线程。而只是继承Runnable的类没有启动线程的start()方法

构造方法
1.public Thread(Runnable target):分配一个带有指定目标新的线程对象

2.public Thread(Runnable target,String name):分配一个带有指定目标新的线程对象并指定名字

public class test3 implements Runnable {
    @Override
    public void run() {
        for(int i=0;i<50;i++){
            System.out.println(Thread.currentThread().getName()+"正在执行-----"+i);
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class test4 implements Runnable{
    @Override
    public void run() {
        for(int i=0;i<50;i++){
            System.out.println(Thread.currentThread().getName()+"正在执行------"+i);
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }


        }
    }
}

public class Test_Runnable {
    public static void main(String[] args) {
        test3 t3 = new test3();
        test4 t4 = new test4();
        Thread td1 = new Thread(t3);
        Thread td2 = new Thread(t4,"RRR");
        td1.start();
        td2.start();
        //主线程,和其他线程一起执行
        for(int i=0;i<50;i++){
            System.out.println(Thread.currentThread().getName()+"正在执行----"+i);
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

注:
1.实现Runnable接口,使得该类有了多线程类的特征。run()方法是多线程程序的一个执行目标。所有的多线程代码都在run方法里面。Thread类实际上也实现了Runnable接口

2.实际上所有的多线程代码都是通过运行Thread的start()方法来运行的,因此不管是继承Thread类还是实现Runnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的。

Thread和Runnable的区别

一个类如果继承Thread,则不适合资源共享;但是如果实现了Runnable接口的话,很容易实现资源共享。

1.如果使用继承Thread类的方法实现多线程,如果要共享一个线程的资源

//如果要共享t1线程,好像不太容易实现
test1 t1 = new test1();
//如果要再执行一个t1线程,就要再new一个test1,这样操作的就不是同一个内存空间,不能实现资源共享
test t2 = new test1();

2.如果使用实现Runnable接口的方式来实现多线程,如果要共享一个资源

//如果要共享t1线程
test1 t1 = new test1();
Thread td=new Thread(t1);
//再创建一个t1线程,操作的是同一块内存空间,共享了同一个资源
Thread td1=new Thread(t1);
总结

实现Runnable接口比继承Thread类所具有的优势:
1.适合多个相同的程序代码的线程去共享同一个资源

2.可以避免java中的单继承的局限性

3.线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类

使用匿名内部类的方式创建线程

使用匿名内部类方式,可以方便的实现每个线程执行不同的线程任务,不用再创建类。

public class Inner_class {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i=0;i<50;i++){
                    System.out.println("匿名线程正在执行------"+i);
                    try {
                        Thread.sleep(400);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();

        for(int j=0;j<50;j++){
            System.out.println(Thread.currentThread().getName()+"正在执行---"+j);
            try {
                Thread.sleep(400);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
线程生命周期

线程是一个动态的概念,有创建的时候,也有运行和变化的时候,还有消亡的时候,从生到死就是一个生命周期。在生命周期中有各种各样的状态,这些状态可以相互转换。

1.新建态:刚创建好对象的时候,刚new出来的时候

2.就绪态:线程准备好了所有运行的资源,只差cpu来临

3.运行态:cpu正在执行的线程的状态

4.阻塞态:线程主动休息、或者缺少一些运行的资源,即使cpu来临,也无法运行

5.死亡态:线程运行完成、出现异常、调用方法结束

生命周期图解

在这里插入图片描述

线程状态描述

1.getState():返回当前线程对象的状态对象

2.state的六种状态
①NEW:新建态,没有开启线程
②RUNNABLE:就绪态和运行态
③BLOCKED:阻塞态(等待锁、I\O)
④WAITING:阻塞态(调用了wait方法,等待其他线程唤醒的状态)
⑤TIMED_WAITING:阻塞态(调用了有时间限制的wait方法、sleep方法)
⑥TERMINATED:死亡态

主线程

在任何java程序启动时,一个线程立刻运行(即main方法对应的线程),该线程通常称为程序的主线程。

1.特点:他是产生其他子线程的线程,他不一定是最后完成执行的线程,子线程可能在主线程结束之后还在运行

线程的优先级

每个线程都有优先级,优先级高的获得较多的执行机会;但并不是有高优先级的线程时,低优先级的线程就不能执行。

1.线程的优先级分为10级
①Thread.MAX_PRIORITY = 10
②Thread.NORM_PRIORITY = 5
③Thread.MIN_PRIORITY = 1

2.getPriority():返回当前线程对象的优先级 默认线程的优先级是5

3.setPriority(int newPriority):设置线程的优先级 虽然设置了线程的优先级,但是具体的实现取决于底层的操作系统的实现(最大的优先级是10 ,最小的1 , 默认是5)。

线程的join方法

非静态方法,当前线程挂起(阻塞),等待加入(join)的线程执行完成。某个线程调用join方法意味着,当前的线程(join方法存在的线程)挂起,直到调用join的线程执行完毕。

public static void main(String[] args) throws InterruptedException {
        test1 t1 = new test1();
        t1.setName("线程1");
        //启动线程
        t1.start();
        
        //执行这句代码后,主线程main挂起,直到t1线程执行结束,主线程再继续执行
        t1.join();
        for(int i=0;i<50;i++){
            System.out.println("线程CCCC正在执行。。。。"+i);
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
线程的yield方法

和sleep方法很像,但是和sleep不同,他只是短暂的挂起当前线程,让别的线程先运行,进入到就绪状态,而sleep进入到阻塞态。

守护线程(后台线程)

当所有线程完成,java程序退出,这句话并不准确,因为gc这种垃圾回收线程,我们无法停止他们,为何java程序还是会退出?因为gc是系统线程并且被称为“守护线程”,当所有非守护线程完成后,无论守护线程是否运行完成都要结束(是在一段时间内结束,并不会立即结束)。守护线程只有在别的线程运行的时候才有意义

1.把线程设置成守护线程:setDaemon(boolean on)
必须在启动线程之前调用用setDaemon(true)方法,才可以把该线程设置为守护线程。

test1 t1 = new test1();
        test2 t2 = new test2();
        t1.setName("线程1");
        t2.setName("线程2");

		//设置为守护线程
		t1.setDaemon(true);
		
        //启动线程
        t1.start();
        t2.start();
  • 20
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值