博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java多线程详解(一)
阅读量:4289 次
发布时间:2019-05-27

本文共 3787 字,大约阅读时间需要 12 分钟。

一、概念

1、进程:是指一个内存中运行的应用程序。

每个进程都有自己独立的一块内存空间,一个进程中可以启动多个线程。

比如,在windows系统中,一个运行的exe就是一个进程。

 

2、线程:是进程中的一个执行流程,一个进程中可以运行多个线程。

比如:java.exe进程中可以运行多个线程。

线程总是属于某一个进程的,进程中的多个线程共享进程的内存。

 

注:所谓的“同时”,实际上是多个线程之间轮流执行的结果,只不过每个线程执行的时间很短,就切换到另一个线程,人眼看起来像是同时在执行。

 

二、java中线程

1、使用Thread类或者Runnable接口 来定义、实例化和启动新线程。

2、一个Thread类实例是一个对象,具有变量和方法,存放在堆内存中。

3、每个线程都有一个调用栈,一旦创建一个新线程,就会产生一个新的调用栈。

 

线程的创建和启动

一、实例化线程

a)        扩展java.lang.Thread类,如:

package com;public class MyThread extends Thread {		public MyThread(String name) {		super(name) ;	}	@Override	public void run() {		for (int i = 0; i < 5; i++) {			for (int j = 0; j < 10; j++) {				System.out.println(this.getName()+" : "+i);			}		}	}	public static void main(String[] args) {		Thread t1 = new MyThread("张三") ;		Thread t2 = new MyThread("李四") ;				t1.start();		t2.start();	}}

a)        实现java.lang.Runnable接口

package com;public class MyRunnable implements Runnable {	private String name ;		public MyRunnable(String name) {		this.name = name ;	}	public void run() {		for (int i = 0; i < 5; i++) {			for (int j = 0; j < 10; j++) {				System.out.println(name +" : "+i);			}		}	}	public static void main(String[] args) {		Thread t1 = new Thread(new MyRunnable("张三")) ;		Thread t2 = new Thread(new MyRunnable("李四")) ;		t1.start();		t2.start(); 	}}

一、启动线程

a)        在线程的Thread方法上,调用start()方法,不是run()方法

b)        在调用start()方法前:

                        i.             线程处于新状态,新状态指有一个Thread对象,但还并不是一个真正的线程。

c)        在调用start()方法后:

                        i.             发生如下变化:

1.        启动新的执行线程(具有新的调用栈)

2.        该状态从新状态转移到可执行状态

3.        当该线程获得机会执行时,其目标run()方法将运行

注:对于java来说,run()方法就像main()方法一样,只是新线程知道调用的方法名称和签名,

因此,在Runnable上或者Thread上调用run方法是合法的,但并不启动新的线程。

 

二、线程模型和线程调度原理

a)        线程栈模型

                        i.             线程模型是理解线程调度原理以及线程执行过程的基础。

                      ii.             线程栈:是指某一个时刻内存中线程调度的栈信息,当前正在调用的方法总是位于栈顶,线程栈中的内容是随着线程的运行状态变化而变化的,我们要研究线程栈,就要选择某一个运行时间(也就是代码运行到哪一行),如下图:

  i.             上图中,

1.        第一个栈A是主线程main()方法运行带System.out.pringln(“Hello Java”);时的运行栈信息,main()方法位于栈A的栈顶;

2.        第二个栈A是主线程main()方法运行到new JavaThreadDemo().threadMethod();方法时的栈信息,threadMethod()方法位于栈A的栈顶。

3.        在threadMethod()方法中,运行到start()方法时,会新建立一个线程,新建立的线程也将拥有自己的线程栈B,run()方法位于栈B的栈顶。

4.        此时,线程栈A和线程栈B并行运行

5.        由此可以看出,方法调用和线程启动的区别:

a)        方法调用只是在原来的线程栈中调用方法即可。

b)        而线程启动会新建立一个独立的线程栈来运行自己的线程。

b)        线程的生命周期

线程的生命周期包括5中状态:新建、可运行状态、运行状态、阻塞、死亡。

i.  新建:创建了一个新的线程对象,但是还没有调用线程的start()方法,此时此案成处于新建状态,如:Thread t1 = new MyThread("张三") ;

ii. 可运行状态:调用线程的start()方法,线程进入可运行状态,此时线程等待JVM的调度程序将其变为运行状态

iii. 运行状态:线程调度程序,从众多的可运行状态线程中,选择一个线程来执行。这也是线程进入运行状态的唯一方式,必须有JVM来调度。

iv. 阻塞状态:线程的等待、睡眠和阻塞统称为阻塞状态,此时线程依然是活着的,处于待命状态,知道某个条件出现时,变为可运行状态。

v. 死亡状态:当线程的run()方法执行完毕后,线程结束。此时线程已经不存在,它所占用的所有资源都会被回收。

二、线程阻塞:

a)        线程阻塞有多种,常见的有三种(IO阻塞不讨论):

     i.   睡眠:调用线程的sleep()方法进入睡眠状态。

当线程睡眠时,它入睡在某个地方,在苏醒之前不会返回到可运行状态。当睡眠时间到期,则返回到可运行状态。

1.        线程处于睡眠状态时,JVM调度程序会暂停此线程的执行,从而去执行其他的处于可运行状态的线程。

2.        sleep()方法是Thread的静态方法,只能控制当前线程的睡眠。

3.   线程睡眠时间到了之后,返回到可运行状态,等待JVM调度。

         ii.             等待:

        iii.             获取线程锁而阻塞

b)  让线程暂时离开运行状态的三中方法:

        i.    调用线程的sleep()方法,使线程睡眠一段时间

        ii.   调用线程的yield()方法,使线程暂时回到可运行状态,来使其他线程有机会执行。

       iii.  调用线程的join()方法,使当前线程停止执行,知道当前线程中加入的线程执行完毕后,当前线程才可以执行

 

三、线程的优先级和让步

a)        线程让步:通过Thread.yield()来实现的,yield()方法的作用是:暂停当前正在执行的线程对象,并执行其他线程。

b)        JVM线程调度程序是基于优先级的抢先调度机制。

c)        当线程池中线程都具有相同的优先级,调度程序的JVM自由选择他喜欢的线程;如果存在不同级别的优先级,JVM会有高概率选择级别高的线程,但并不一定选择高的,只是概率大一点而已。

d)        如果是线程池中具有相同的优先级,这个时候调度程序的操作有两种可能:

                        i.             选择一个线程运行,知道他阻塞或者运行完成为止;

                      ii.             时间分开,为池内的每个线程提供均匀的运行机会

e)        可以通过setPriority(int newPriority),更改线程的优先级。如:

Thread t = new MyThread();

       t.setPriority(8);
       t.start();

四、小结:

a)        到目前为止,我们知道了,线程离开运行状态共有3中方法:

                        i.             调用Thread.sleep():是当前线程睡眠n毫秒以后,进入可执行状态,等待调度;

                      ii.             调用Thread.yield(): 让当前运行线程回到可运行状态,是的具有相同优先级的线程有机会执行;

                     iii.             调用join()方法:保证当前线程停止运行,直到该线程所加入的线程完成为止,然而,如果它加入的线程没有存活,则当前线程不需要停止。

b)        除了以上三种方式外,还有下面几种特殊情况可能是线程离开运行状态

                        i.             线程的run()方法完成;

                      ii.             在对象上调用wait()方法(不是在线程上);

                     iii.             线程不能再对象上获得锁定,他正试图运行该对象的方法代码;

                     iv.             线程调度程序可以决定将当前运行状态移动到可运行状态,以便让另一个线程获得运行机会,而不需要任何理由。

参考文章:http://www.cnblogs.com/riskyer/p/3263032.html

http://www.2cto.com/kf/201409/335651.html

http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html

你可能感兴趣的文章
常用排序方法介绍
查看>>
Java异常分类和统一处理
查看>>
原 荐 cache线程池对数据库操作的饥饿问题
查看>>
使用Eclipse把java文件打包成jar 含有第三方jar库的jar包
查看>>
3种web会话管理的方式
查看>>
SSM(框架)-异常1:面向接口式编程异常
查看>>
Android蓝牙4.0之玩爆智能穿戴、家具(二)
查看>>
使用Condition实现多线程之间调用
查看>>
javaAPI之String
查看>>
JQ 新窗口打开链接并设置参数
查看>>
JQuery实现列表中复选框全选反选功能封装
查看>>
JAVA GC 简单总结
查看>>
JS中常遇到的浏览器兼容问题和解决方法
查看>>
JAVA学习笔记之-servlet知识点
查看>>
apache 配置不同的端口访问不同的站点
查看>>
2017年3月Java9带来的革新!
查看>>
Log4j容器深入探究
查看>>
记glide框架使用中所遇到的问题
查看>>
学习AOP之透过Spring的Ioc理解Advisor
查看>>
Jquery一个简单的注册验证
查看>>