线程基础

1.1 进程、线程与任务

   进程:程序的运行实例,一个应用的运行过程。是程序向操作系统申请资源的基本单位。

  线程:是进程中可独立执行的最小单位

 任务:线程所要完成的计算称为任务

一个进程可以包含多个线程。同一个进程中的所有线程共享该进程中的资源 ,如内存空间、文件句柄等

1.2多线程编程

1.2.1什么是多线程编程

多线程编程就是以线程为基本抽象单位的一种编程范式

1.2.2为什么使用多线程

使不相干的任务相互“不影响”。像饭店有多个点餐员,以减少顾客的等待时间。

某系统需要从指定的日志文件系统中统计出一些信息,读取日志所涉及的I/O操作是一个比较慢的操作,可以使用一个专门的线程负责日志文件的读取,另一个线程去负责对读取到的日志数据进行统计。

1.3java线程API简介

Thread类或其子类的一个实例就是一个线程

1.3.1线程的创建、启动与运行

创建一个Thread类(或其子类)的实例就是创建一个线程

run() 任务的处理逻辑入门方法,由Java虚拟机在运行相应线程时直接调用,而不是应用代码直接进行调用。

start() 启动相应的线程。启动一个线程的实质是请求JAVA虚拟机运行相应的线程,但具体何时运行由线程调度器决定。因此 start方法调用结束并不意味着相应线程已经开始运行,这个线程可能稍后才被运行,甚至也可能永远不会被运行。

Thread类的两个常用构造器 :Thread() 和Thread(Runnable target)。相应的创建线程有两种方式。一种定义Thread子类,并覆盖run()方法;另一种是创建一个java.lang.Runnable接口的实例,并在实例的run()方法中实现任务处理逻辑,然后以该Runnable接口实例作为构造器的参数直接创建(new)一个Thread类的实例。

每个线程均可以有自己的名字,这个名字便于我们区分不同的线程。这个名字在多线程调试中非常有必要。

package test;

public class MyThreadApp {
	public static void main(String[] args) {
		
		Thread helloThread = new HelloThread();
		helloThread.start();
		
		Thread helloRunnable = new Thread(new HelloRunnable());
		helloRunnable.start();
		
		System.out.println("myThreadApp :" + Thread.currentThread().getName());
	}
}
class HelloThread extends Thread{
	@Override
	public void run () {
		System.out.println("Hello Thread :" + Thread.currentThread().getName() );
	}
}

class HelloRunnable implements Runnable{
	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println("Hello Runnable :" + Thread.currentThread().getName());
	}
}

可以看到结果:

Hello Thread :Thread-0
myThreadApp :main
Hello Runnable :Thread-1

多次运行会发现结果的顺序会有差别。

不管哪种方式创建线程,run()方法执行结束,相应的线程运行也就结束了。也有可能是run()方法的代码异常导致中止。运行结束的线程所占用的资源(如内存空间)会被java虚拟机垃圾回收。

线程属于“一次性用品”,只能start()一次,多次调用同一个Thread实例的start()会导致抛出异常。

1.3.2 Runnable接口

Runnable接口只定义一个方法 public void run() 任务的逻辑就体现在run方法中。Thread类实际上就是Runnable接口的一个实现类,其实现

public void run(){
    if(target != null){
       target.run();
    }
   }

 Thread类中run 方法的实现是逻辑是 target != null 就调用 target.run();如果为null 则什么也做。实例变量target的类型为Runnable。这种逻辑就决定创建线程的两种方式:一种在Thread子类的run方法中直接实现任务逻辑,另一种是在一个Runnable实例中实现任务逻辑,该逻辑由Thread类的run方法负责调用。

   线程两种创建方式的区别:从面向对象编程角度来看,创建子类是一种基于继承的技术,创建实例是一种基于组合的技术,组合相对继承耦合性更低,也更加灵活;  从对象共享的角度来看,创建实例意味看多个线程实例共享同一个Runnable实例。在某些情况下可能导致程序的运行出乎我们的意料; 从对象创建成本的角度来,线程实例是一个特殊的Runnable实例,因为在创建它的时候java虚拟机为会其分配调用栈空间、内核线程资源 。因此创建一个线程实例要比创建一个普通的Runnable实例来说,其成本相对昂贵一点。

package com.example.demo;

/**
 * 从对象共享角度来看 ,组合方式(Runnable) 会存在竞态 和线程不安全问题
 */
public class ThreadComparaToRunnable {

    public static void main(String[] args) {
        Thread t;
        CountingTash ct = new CountingTash();
        final int numberOfProceesors = Runtime.getRuntime().availableProcessors();
        for (int i = 0; i < 2 * numberOfProceesors; i++) {
            t = new Thread(ct);
            t.start();
        }
        for (int i = 0; i < 2 * numberOfProceesors; i++) {
            t = new CountingThread();
            t.start();
        }
    }

    static class Counter {
        private int count = 0;

        public void increment() {
            count++;
        }

        public int value() {
            return count;
        }
    }

    static class CountingTash implements Runnable {
        private Counter counter = new Counter();

        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                doSomething();
                counter.increment();
            }
            System.out.println("CountingTash :" + counter.value());
        }

        private void doSomething() {

            try {
                Thread.sleep(80);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
//        Tools.randomPause(80);
        }


    }

    static class CountingThread extends Thread {
        private Counter counter = new Counter();

        public void run() {
            for (int i = 0; i < 100; i++) {
                doSomething();
                counter.increment();
            }
            System.out.println("CountingThread : " + counter.value());
        }

        private void doSomething() {

            try {
                Thread.sleep(80);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }
}


CountingTash :723
CountingTash :723
CountingTash :724
CountingThread : 100
CountingThread : 100
CountingThread : 100
CountingTash :725
CountingThread : 100
CountingTash :726
CountingTash :727
CountingTash :729
CountingThread : 100
CountingTash :728
CountingThread : 100
CountingThread : 100
CountingThread : 100

 

1.3.3 线程属性

线程的属性包括线程的编号、名称、线程类型和优先级。

名称:可以使用getName()  setName(),名称的设置有助于调试和问题定位。

线程类型:按照线程是否会阻止java虚拟机正常停止,分为守护线程和用户线程。由daemon属性表示相应线程是否为守护线程。

      如果java虚拟机是被强制停止的,如Kill命令,那么用户线程也无法阻止java虚拟机的停止。

优先级:本质只一个线程调度器的提示信息,以便于线程调试器决定优先调度哪些线程运行。但并不能保证线程按优先级高低的顺序运行。

1.3.4Thread的常用方法

Thread.currentThread() 返回当前线程,即当前代码执行线程;当前是相对而言的。

void run  用于实现线程的任务逻辑。一般情况下邮虚拟机调用,应用程序不应该调用该方法

void start 启动相应线程。多次调用会导致异常。

void join 等待相应线程运行线束。若A调B,A被暂停,等B运行线束。

static void yeild 使当前线程放弃对处理器的占用。此方法不可靠,被调用时当前线程可能仍然继续运行。

       可以理解为“我当前不急用,别要需要就拿去吧。如果没有人用,我也不介意继续占用”

static void sleep(long millis) 使当前线程休眠(暂停)指定的时间

1.3.5Threa类的一些废弃方法

stop() 停止线程

suspend 暂停线程的运行

resume 使被暂停的线程继续运行。

1.4 &1.5 线程的层次关系

A所执行的代码创建了B,习惯上我们称线程B为A的子线程,或A为B的父线程。父子只是一相对称呼。

一个线程是否为守护线程取决于父线程;

一个线程的优先级默认值 为父线程的优先级。

查是没有API可以用来查询一个线程的父线程和所有子线程。父子线程的生命周期也没有必然的联系。

1.6线程的生命周期状态

一个线程从其创建到结束可能经历若干状态。状态可以使用监控工具查看,也可以通过Thread.getState()调用获得。返回值类型Thread.State是一个枚举类型。Thread.State定义了线程的状态:

NEW  一个已经创建未启动。一个线程实例只能被启动一次,所以只存在一次该状态

RUNNABLE 该状态为一个复合状态。包括两个子状态 READYRUNNING。前都表示 处于可以被线程调度器进行高度而使之处于RUNNING状态,后者表示正在运行,却run()方法所对应的逻辑任务指令。Thread.yield,可将RUNNING状态转换为READY。处于READY 子状态的线程也被称活跃线程。

BLOCKED 发起阻塞式IO 或者申请有一个被持有的资源 时,会处于该状态。该状态不会占用处理器资源 。当得到资源 或者阻塞IO完成时,会转换为RUNNABLE状态。

WAITING 当线程调用wait() 、join()、 LockSupport.park(Object)会处于此状态。notify() notifyAll() LockSupport.unpark(Object)会将状态转换至RUNNABLE.

TIMED_WAITING 带时间的等待状态。

TERMINATED 执行线束的线程处于该状态。同NEW一样,只存在一次该状态。Thread.run()正常返回或抛出异常都会导致该状态。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值