java多线程

java多线程

参考博客:

  1. Java多线程详解

一. 概述

  • 首先,我们需要分清楚一些概念,什么是进程(进城),什么是线程(献城).
    在任务管理器中我们可以看到进程在执行.还有一些进程的信息,例如CPU的占有率,占用内存大小.简单来说一个运行的程序就是进程.
  • 这里给出一个定义: 一个具有独立功能的程序在一个数据集合上的一次动态执行
    也就是说把一个可执行文件(程序)加载到内存中,CPU执行的这个动态过程称为进程.
  • 我们可以用多个进程来完成一件任务,但是进程间的通讯,共享数据实现比较的麻烦,同时系统维护进程的开销也比较的大(例如 分配资源,建立PCB,撤销进程,回收资源,撤销PCB,进程的切换),为了解决这些问题,提出了线程. 将进程的责任进行进一步的划分.进程来执行管理功能.将进程看做是一个资源的平台(环境),提供各种的资源(地址空间,打开的文件,数据段,代码段).而进程的执行功能,交给线程.``线程的定义:进程中的一条执行流程 线程可以访问进程的资源,那么多个线程完成一个任务,比多个进程来完成实现简单.
  • 线程=进程- 共享资源
    线程的优点 :
  1. 一个进程多以同时存在多个线程,多个执行流
  2. 线程可以并发的执行
  3. 每个线程直接共享地址空间和文件资源
    线程的缺点 :
  4. 一个线程崩溃,会导致所属的进程的所有线程崩溃.
    并发和并行:
  • 并发:以前的计算机只有一个CPU中只有一个执行单元,任何的时刻只能执行一条代码,但是速度足够的快,我们可以不断的切换进程(线程),(虽然只有一个进程(线程)在运行,但是速度足够的快,我们在使用的过程中感觉不到进程(线程)的切换,仿佛同时执行多个任务.也就是说在一段时间内,它们是同时执行的.

  • 并行 :现在的CPU中有多个执行单元,相当于有多个流水线,可以同一时刻执行多条指令.同一时刻,可以同时处理对个进程(线程).

  • 多线程(进程)究竟是并发还是并行是操作系统的调度来确定的,程序编写者无法确定.但是,并发还是并行它们的最终的结果还是不变的.

  • 虽然说Java的多线程与具体和平台无关,但是要明白Java多线程,还是需要了解一下操作系统中线程和进程相关的知识.

二.Java多线程的入门

Java实现多线程有四种机制:

  • 继承Thread
  • 实现Runable接口
  • 通过CallableFuture创建
  • 使用线程池创建

继承Thread

步骤:

  1. 定义一个继承Thread类的子类.并重写该类的run()方法
  2. 创建Thread子类的实例,即创建了线程对象
  3. 调用该线程对象的start()方法,启动线程
public class MyThread extends Thread{
	

@Override
	public void run() {
		// TODO Auto-generated method stub
		for(int i= 0;i<10;i++) {
			System.out.println(Thread.currentThread().getName()+"  "+i); //currentThread()是一个静态方法,获得当前执行的线程.
		}
	}

public static void main(String[] args) {
	Mythread myThread = new ThreadLocalDemo();
	 myThread.start(); //启动线程
	for(int i= 0;i<10;i++) {
		System.out.println(Thread.currentThread().getName()+"  "+i);
	}
}
}

main  0
main  1
main  2
main  3
Thread-0  0
main  4
Thread-0  1
Thread-0  2
main  5
Thread-0  3
Thread-0  4
Thread-0  5
Thread-0  6
Thread-0  7
main  6
Thread-0  8
Thread-0  9
main  7
main  8
main  9

实现Runable接口

步骤:

  1. 定义Runnable接口的实现类,并重写该接口的run()方法
  2. 创建Runnable实现类的实例
  3. 并以此实例作为Thread的target对象传入.
  4. 启动该线程的thread的实现类
public class MyThread implements Runnable {

	@Override
	public void run() {
		for(int i= 0;i<10;i++) {
			System.out.println(Thread.currentThread().getName()+"  "+i); 
		}
	}

public static void main(String[] args) {
	MyThread myThread= new MyThread();
	Thread thread1= new Thread(myThread);
	thread1.start();
	for(int i= 0;i<10;i++) {
		System.out.println(Thread.currentThread().getName()+"  "+i); 
	}
}
}

通过CallableFuture创建

这种方法可以使得线程执行有返回值

  • 创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
  • 创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
  • 使用FutureTask对象作为Thread对象的target创建并启动新线程。
  • 调用FutureTask对象的get()方法来获得子线程执行结束后的返回值其中.
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
	// TODO Auto-generated method stub
	for(int i =0;i<10;i++) {
		System.out.println(Thread.currentThread().getName()+"  "+i);
	}
	return "执行完成";
}
public static void main(String[] args) {
	FutureTask task = new  FutureTask<String>( new  MyCallable());
	Thread thread1 = new Thread(task,"myThread");
	thread1.start();
	for(int i =0;i<10;i++) {
		System.out.println(Thread.currentThread().getName()+"  "+i);
	}
    y {
		System.out.println(task.get()); //get方法值阻塞的.除非等待task中的线程执行完成
	} catch (InterruptedException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	} catch (ExecutionException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
}
}

使用线程池创建

日后在补

三. 线程的生命周期

在这里插入图片描述

  1. 新建状态
    用new创建一个Tread实例后,在线程便处于新生状态,此时线程已经拥有自己的栈,应该还拥有类似一份TCB的数据结构,维护自己线程信息的结构.
  2. 就绪态
    此时线程已经具备可执行的条件,但是没有获得CPU,出入线程就绪队列中,等待系统为其分配CPU。等待状态并不是执行状态,当系统选定一个等待执行的Thread对象后,它就会从等待执行状态进入执行状态,系统挑选的动作称之为“cpu调度”。一旦获得CPU,线程就进入运行状态,继续执行代码
  3. 运行状态
    程序获得CPU的执行权,执行指令. 此时线程可能会进入阻塞状态或者线程死亡
    进入阻塞状态的分类:
  • 主动的放弃CPU(调用Sleep())
  • 线程调用一个阻塞式I/O,在I/O方法返回前,线程都是出于阻塞态
  • 线程试图获得一个互斥资源,而该资源被其他线程占用
  • 线程在等待某个通知
  • 线程调用suspend()方法.当时该方法容易导致死锁.
    当线程的run()方法执行完,或者被强制性地终止,例如出现异常,或者调用了stop()、destory()方法等等,就会从运行状态转变为死亡状态。

四 线程的管理

  1. sleep() 线程睡眠
    如果我们需要让当前正在执行的线程暂停一段时间,并进入阻塞状态,则可以通过调用Thread的sleep方法。sleep是一个静态方法,调用该方法会使当前的线程阻塞.java线程是有JVM的调度算法决定的,我们不可能精确的控制,sleep()阻塞1s后,进入就绪态,当时不保证立马被调用.
  2. yield()线程让步
    也是Thread中的一个静态方法,作用是让当前的线程进入到就绪状态.yield()方法只是让当前线程暂停一下,重新进入就绪的线程池中,让系统的线程调度器重新调度器重新调度一次,完全可能出现这样的情况:当某个线程调用yield()方法之后,线程调度器又将其调度出来重新进入到运行状态执行。
    sleep和yield的区别:
  • sleep方法会使暂停当前线程后,会进入阻塞状态,只有当睡眠时间到了,才会转入就绪状态。而yield方法调用后 ,是直接进入就绪状态,所以有可能刚进入就绪状态,又被调度到运行状态。
  • sleep方法声明抛出了InterruptedException,所以调用sleep方法的时候要捕获该异常,或者显示声明抛出该异常。而yield方法则没有声明抛出任务异常。
  • sleep方法比yield方法有更好的可移植性,通常不要依靠yield方法来控制并发线程的执行。
  1. join() 线程合并
    线程的合并就将几个并发的线程合并成一个"单线程执行".应用场景是当一个线程必须等待另一个线程执行完毕才能执行,join()方法不是静态的
    join的三个重载方法 :
void join()
//当前线程等待该线程终止的时间最长为 millis 毫秒。 如果在millis时间内,该线程没有执行完,那么当前线程进入就绪状态,重新等待cpu调度  
void join(long mills)
  // 等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。如果在millis时间内,该线程没有执行完,那么当前线程进入就绪状态,重新等待cpu调度  
void join(long mills, int nanos) 
  1. 设置线程的优先级
    在算法调度中,并不是简单的先进先出队列来实现调度机制.通常每个线程都有一个优先级属性,优先级属性越高,获得执行的机会就越多,而优先级低的线程则获得较少的执行机会。与线程休眠类似,线程的优先级仍然无法保障线程的执行次序。只不过,优先级高的线程获取CPU资源的概率较大,优先级低的也并非没机会执行。
    Thread类提供了setPriority(int newPriority)和getPriority()来 设置优先级,虽然Java设置是1-10,当时对应操作系统划分的等级却不一定,所以JAVA用三个静态常量来实现.
MAX_PRIORITY   =10;
MIN_PRIORITY   =1;
NORM_PRIORITY   =5;
  1. 后台守护线程
    守护线程使用的情况比较的少. 例如JVM的垃圾,内存管理等线程都是守护线程,守护线程的用途通常用来执行一些后台作业,例如运行程序的时候播放音乐,文字编辑器里的自动语法检查.守护线程的好处,不需要关心它的结束问题.只有JVM虚拟机退出的时候才退出.
public static void setDeamom(boolean on) //将该线程标记为守护线程或者用户线程,当正在运行的线程都是守护线程时,JVM退出
// on -true,则将该线程标记为守护线程  
// 抛出: 
// IlleagalThreadStateException  如果该线程处于活动态 
// SecurityException 如果当前线程无法修改

注:JRE判断程序是否执行结束的标准是所有的前台执线程行完毕了,而不管后台线程的状态,因此,在使用后台线程时候一定要注意这个问题。
6. 正确的执行线程
Thread.stop()、Thread.suspend、Thread.resume、Runtime.runFinalizersOnExit这些终止线程运行的方法已经被废弃了,使用它们是极端不安全的!想要安全有效的结束一个线程,可以使用下面的方法:

• 正常执行完run方法,然后结束掉;

• 控制循环条件和判断条件的标识符来结束掉线程。
class MyThread extends Thread {  
    int i=0;  
    boolean next=true;  
    @Override  
    public void run() {  
        while (next) {  
            if(i==10)  
                next=false;  
            i++;  
            System.out.println(i);  
        }  
    }  
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章