多线程(二)

进程        多任务操作系统

描述 : PCB                组织 : 链表

线程        轻量级进程

同一个进程的多个线程共享一份系统资源( 创建线程,就省去了申请资源开销. 销毁线程,也省去了资源释放的开销.)

每个线程,都是一个独立的执行流,都可以独自参与CPU的调度.

每个进程,就会包含多个线程了,多个线程共享同一份资源(内存 + 文件描述符表)

操作系统为我们在编程时提供了一些操作线程的api,由于操作系统都是C/C++实现的,所以提供的api也都是C/C++的

而在我们java编程时,JDK就对这些系统提供的api进行了封装,封装成了java风格的api

Thread类run方法,可以用来操作线程的

run方法,是一个线程的入口,没有run方法,线程没有入口,就无法创建线程.

一、操作系统"内核"

内核,是操作系统最核心的功能模块,( 管理硬件 , 给软件提供稳定的运行环境 )

1.1 内核态和用户态

我们平常所运行的应用程序,如:浏览器,网易云音乐等等的,都是在运行用户态了,

那么,为什么要划分出内核态和用户态呢?

这主要还是为了稳定.防止你运行的应用程序出了什么错误,把硬件设备或软件资源给弄出问题了.

系统封装了一些api,这些api都是属于一些合法的操作,应用程序,只能调用这些api,就不至于对系统/硬件设备,产生太大的危害.

操作系统=内核+配套的应用程序.

二、线程

每一个线程,都是一个独立的执行流,每个线程都能独立的去CPU上调度执行.

按照之前所学习的只是,如果代码中出现了死循环,就会被卡在死循环这里,而无法执行后面的代码.

而现在,在我们引入多线程后,如果一个线程中出现了死循环,是不会影响到另一个线程中代码的执行的.

 

class MyThread extends Thread{
    @Override
    public void run() {
        while(true){
            System.out.println("线程1");
            try {
                sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

class MyThread2 extends Thread{
    @Override
    public void run() {
        while(true){
            System.out.println("线程2");
            try {
                sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
public class MyThread1 {
    public static void main(String[] args) {
        Thread t1 = new MyThread();
        t1.start();

        Thread t2 = new MyThread2();
        t2.start();

    }
}

上面的这个代码,可以发现,他一直在死循环的打印 "线程1" 和 "线程2"

也就是说,不同的线程,他们的执行是独立的,互不干扰的.

并且,这些线程的执行顺序是不确定的.这是因为,操作系统中有一个调度器的模块,这个模块的实现方式,是一种类似随机调度的方式.

随机调度:一个线程,到CPU上执行和从CPU上下来的时机都是不确定的.因为线程在CPU上是"抢占式执行的. 

除了用打印来看线程,我们还可以使用第三方软件来查看线程.

在jdk中,有一个工具jconsole,它可以帮我们查看正在运行的线程.

在上面,我们还使用了,sleep函数.

Sleep是windows的api提供的函数,我们在进行java编程的时候,可以使用java自己封装的Thread.sleep函数.

在使用sleep函数的时候,我们还需要抛出一个异常,因为比如上面的sleep的传参是1000,而在sleep1000的过程中可能会被提前唤醒.

并且,在run方法中只能使用try,因为如果在run中用throws的话,会导致方法的方法签名被修改了,就无法构成重写了.

三、线程的几种方式

3.1 继承Thread重写run

class MyThread01 extends Thread{
    @Override
    public void run() {
        System.out.println("MyThread01");
    }
}
public class MyThreadDemo1 {

    public static void main(String[] args) {
        Thread t1 = new MyThread01();
        t1.start();

    }
}

3.2 实现Runnable接口,重写run

class MyThread02 implements Runnable{
    @Override
    public void run() {
        System.out.println("实现Runnable接口重写run");
    }
}
public class MyThreadDemo2 {
    public static void main(String[] args) {
        Runnable runnable = new MyThread02();
        Thread t1 = new Thread(runnable);
        t1.start();
    }
}

3.3 继承Thread类重写run,但是使用匿名内部类

public class ThreadDemo3 {
    public static void main(String[] args) {
        Thread t1 = new Thread(){
            @Override
            public void run() {
                System.out.println("内部类线程");
            }
        };
        t1.start();
    }
}

3.4 实现Runnable,重写run,匿名内部类

public class ThreadDemo4 {
    public static void main(String[] args) {
        Thread t1 =new Thread(new Runnable(){
            @Override
            public void run() {
                System.out.println("匿名内部类,实现Runnable重写run");
            }
        });
        t1.start();
    }
}

3.5 使用lambda表达式   [比较常用]

public class ThreadDemo5 {
    public static void main(String[] args) {
        Thread t1 = new Thread(()->{
            System.out.println("lambda表达式");
        });
        t1.start();
    }
}

四、Thread的重要属性和方法

4.1 方法

1. Thread()     创建线程对象

2. Thread(Runnable  target)     使用Runnable对象创建线程对象

3. Thread(String  name)     创建线程对象,并命名

4. Thread(Runnable target,String name)     使用Runnable对象创建线程,并命名.

5. Thread(ThreadGroup group,Runnable target)     线程可以被用来分组管理,分好的组即为线程组.

线程之间的名字是可以重复的.

4.2 属性

1.ID

获取方法 : getID()

2.名称

获取方法 : getName()

3.状态

获取方法 : getState();

4.优先级

获取方法 : getPriority()

5.是否后台进程

获取方法 : isDaemon()

6.是否存活

获取方法 : isAlive()

7.是否被中断

获取方法 : isInterrupted();

后台线程与前台线程相对,前台线程的运行,会阻止进程的结束,后台线程的运行,不会阻止进程结束.

我们创建的线程默认为前台线程,会阻止进程结束.只要前台线程没有执行完,进程就不会结束.(即使main已经执行完毕)

setDaemon(true)  ->   在start之前,设置线程为后台线程.

true   ->   后台线程

isAlive()  ->  表示了,进程中是否还有线程的存在.

Thread t1 = new Thead()   ->此时t1对象有了,但是内核PCB还没有,isAlive()就是false.

t1.start()   ->   这之后,内核中创建出PCB,此时isAlive就是true.

当run执行完了,此时内核中的线程就结束了,此时t1变量可能还存在,此时isAlive()就是false.

  • 17
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值