多线程简介与创建方式

线程简介:

要想说线程,首先必须得聊聊进程,因为线程是依赖于进程存在的。

进程概述:

进程就是正在运行的程序,是执行程序的一次执行过程,一个动态的概念,是系统进行资源分配和调用的独立单位;每一个进程都有他独立的内存空间和系统资源;

线程概述:

在一个进程内部又可以执行多个任务,而这每一个任务我们就可以看成是一个线程。是程序使用CPU的基本单位。

多线程的意义:

  • 多线程的作用不是提高执行速度,而是为了提高应用程序的使用率。

程序在运行的时候,都是在抢CPU的时间片(执行权),如果是多线程的程序,那么在抢到 CPU的执行权的概率应该比单线程程序抢到的概率要大.那么也就是说,CPU在多线程程序中执行的时间要比单线程多,所以就提高了程序的使用率.但是即使是多线程程序,那么他们 中的哪个线程能抢占到CPU的资源呢,这个是不确定的,所以多线程具有随机性.

  • 单进程的计算机只能做一件事,而随着现在的技术发展,已经不能满足人们的需求;

例如:人们现在一边玩游戏(游戏进程)一边听歌(音乐进程),这就是两个进程,但对于单核计算机来讲,游戏进程和音乐进程不能同时运行,CPU在一个时间点上只能做一件事情,计算机在不断地频繁切换,而且速度是非常的快,所以,感觉是在同时进行,而多线程就可以提高CPU的使用率;

进程和线程的区别

进程:应用程序的执行实例,有独立的内存空间和系统资源

线程:CPU调度和分派的基本单位,进程中执行运算的最小单位,可完成一个独立的顺序控制流程

进程和线程的关系

(1)一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。线程是操作系统可识别的最小执行和调度单位。

(2)资源分配给进程,同一进程的所有线程共享该进程的所有资源。 同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)。但是每个线程拥有自己的栈段,栈段又叫运行时段,用来存放所有局部变量和临时变量。

(3)处理机分给线程,即真正在处理机上运行的是线程。

(4)线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。

多线程的并行与并发:

  1. 线程的并行是指在逻辑上的同时发生,在某一时间内同时运行多个程序;
  2. 线程的并发是指在物理上的同时发生,在某一时间点同时运行多个程序;

并发 : 指应用能够交替执行不同的任务, 其实并发有点类似于多线程的原理, 多线程并非是如果你开两个线程同时执行多个任务, 执行, 就是在你几乎不可能察觉到的速度不断去切换这两个任务, 已达到"同时执行效果", 其实并不是的, 只是计算机的速度太快, 我们无法察觉到而已. 就类似于你, 吃一口饭喝一口水, 以正常速度来看, 完全能够看的出来, 当你把这个过程以n倍速度执行时..可以想象一下.

并行 : 指应用能够同时执行不同的任务, 例:吃饭的时候可以边吃饭边打电话, 这两件事情可以同时执行;

线程的优先级

每一个 Java 线程都有一个优先级,这样有助于操作系统确定线程的调度顺序。

Java 线程的优先级是一个整数,其取值范围是 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )

默认情况下,每一个线程都会分配一个优先级 NORM_PRIORITY(5)

优先级只是意味着获得调度的概率低,而不是优先级低就不会被调用,都是有CPU的调度有关。可以使用  getPriority()   .   setPriority(int  xxx)    的方式改变或获取优先级。

具有较高优先级的线程对程序更重要,并且应该在低优先级的线程之前分配处理器资源。但是,线程优先级不能保证线程执行的顺序,而且非常依赖于平台。

线程方法

说明解释
setPriority(int  newPriority)更改线程的优先级
static   void   sleep(long  millis)在指定的毫秒数内使当前正在执行的线程休眠
void    join ()等待线程终止
static  void   yield()暂停当前正在执行的线程而去执行其他的线程
void   interrupt  ()线程中断
Boolean   isAlive()测试线程是否处于活动状态

守护(daemon)线程

  1. 线程有守护线程和用户线程;
  2. 虚拟机必须要保证用户线程执行完成,而不需要等待守护线程执行完毕;

线程的创建:

由于线程是依赖进程而存在的,所以我们应该先创建一个进程出来。
          而进程是由系统创建的,所以我们应该去调用系统功能创建一个进程。
          但是Java是不能直接调用系统功能的,所以,我们没有办法直接实现多线程程序。
          但是呢?Java可以去调用C/C++写好的程序来实现多线程程序。
          由C/C++去调用系统功能创建进程,然后由Java去调用这样的东西,
          然后提供一些类供我们使用。我们就可以实现多线程程序了。

三种创建方式:

Thread  class

继承Thread类

Runnable接口   

实现Runnable接口 

Callable接口

实现Callable接口

方式一、Thread

  1. 自定义线程类继承Thread类
  2.  重写run()方法,编写线程执行体 
  3. 创建线程对象,调用start()方法启动线程

  run()和start()方法的区别

在 Java 当中,线程通常都有五种状态,创建、就绪、运行、阻塞和死亡。

  1.  第一是创建状态。在生成线程对象,并没有调用该对象的 start 方法,这是线程处于创建状态。
  2. 第二是就绪状态。当调用了线程对象的 start 方法之后,该线程就进入了就绪状态,但是此时线程调度程序还没有把该线程设置为当前线程,此时处于就绪状态。在线程运行之后,从等待或者睡眠中回来之后,也会处于就绪状态。
  3. 第三是运行状态。线程调度程序将处于就绪状态的线程设置为当前线程,此时线程就进入了运行状态,开始运行 run 函数当中的代码。
  4. 第四是阻塞状态。线程正在运行的时候,被暂停,通常是为了等待某个时间的发生(比如说某项资源就绪)之后再继续运行。sleep,suspend,wait 等方法都可以导致线程阻塞。
  5. 第五是死亡状态。如果一个线程的 run 方法执行结束或者调用 stop 方法后,该线程就会死亡。对于已经死亡的线程,无法再使用 start 方法令其进入就绪。

为什么要重写run方法?

  1.    我们可以在写其他的方法,那么其他方法中封装的代码不一定都会被我们线程执行的。
  2.  那么也就是run方法中封装应该是必须被线程执行的代码.

 run方法中的代码的书写原则: 一般是比较耗时的代码

实例:

package com.zhang.demo1.thread;
//继承thread类
public class thread1 extends Thread {
    //重写run方法
    @Override
    public void run() {
        try {
            //线程休眠
            thread1.sleep(6000);
              //线程执行体
            for (int i = 0; i < 15; i++) {
                System.out.println("线程执行体"+i);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    //主线程
    public static void main(String[] args) {
        //创建线程对象
        Thread thread = new thread1();
        //start() 方法用来 启动线程;
        thread.start();
        for (int i = 0; i < 10; i++) {
            System.out.println("主线程"+i);
        }
    }
}

以上代码编译运行的结果如下:
主线程0
主线程1
主线程2
主线程3
主线程4
主线程5
主线程6
主线程7
主线程8
主线程9
线程执行体0
线程执行体1
线程执行体2
线程执行体3
线程执行体4
线程执行体5
线程执行体6
线程执行体7
线程执行体8
线程执行体9
线程执行体10
线程执行体11
线程执行体12
线程执行体13
线程执行体14

方式二、实现Runnable接口 

  1. 实现接口
  2. 重写run方法
  3. 创建runbale接口实现类的对象。
  4. 启动线程。需要创建一个线程Thread对象 ,然后把runbale接口实现类的对象丢到构造参数里 ,
  5. 调用start方式启动;

实例:

package com.zhang.demo1.thread;
//实现runnable接口
//1.实现接口
//2.重写run方法
//3.创建runbale接口实现类的对象。
//启动线程。需要创建一个线程Thread对象 , 然后把runbale接口实现类的对象丢到构造参数里 , 调用start方式启动;
public class Thread2 implements Runnable {
    @Override
    public void run() {
        //线程执行体
        for (int i = 0; i < 15; i++) {
            System.out.println("线程执行体"+i);
        }
    }

    public static void main(String[] args) {
        //重点就是将runbale接口实现类的对象放入Thread构造器中
        Thread2 thread2 = new Thread2();
        new Thread(thread2).run();

        for (int i = 0; i < 10; i++) {
            System.out.println("主线程"+i);
        }
    }
}

以上代码编译运行的结果如下:

线程执行体0
线程执行体1
线程执行体2
线程执行体3
线程执行体4
线程执行体5
线程执行体6
线程执行体7
线程执行体8
线程执行体9
线程执行体10
线程执行体11
线程执行体12
线程执行体13
线程执行体14
主线程0
主线程1
主线程2
主线程3
主线程4
主线程5
主线程6
主线程7
主线程8
主线程9
 

  方式三、实现Callable接口

  1. 创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值;
  2. 重写Callable方法,需要抛出异常;
  3. 创建目标对象;
  4. 创建执行服务:ExecutorService   ser=Executors.newFixedThreadPool(1);
  5. 提交执行:Future< Boolean> result=ser.submit(t1);
  6. 获取结果:Boolean   r1=result1.get();
  7. 关闭服务:ser.shutdownNow();

创建线程的三种方法的比较:

  1. 采用实现 Runnable、Callable 接口的方式创建多线程时,线程类只是实现了 Runnable 接口或 Callable 接口,还可以继承其他类。
  2. 使用继承 Thread 类的方式创建多线程时,编写简单,如果需要访问当前线程,则无需使用 Thread.currentThread() 方法,直接使用 this 即可获得当前线程。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值