2019独角兽企业重金招聘Python工程师标准>>>

一.线程的创建和启动

java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例。每条线程的作用是完成一定的任务,实际上就是执行一段程序流(一段顺序流的代码)。Java使用run方法来封装这样一段程序。

1.继承Thread类创建线程类

/**继承Thread来创建线程类*/
public class FirstThread extends Thread {
private int i;
//重写run方法,run方法的方法体就是线程执行体
public void run() {
for(;i<10;i++){
System.out.println(this.getName()+":"+i);
}
}
public static void main(String []args){
for(int i=0;i<20;i++){
System.out.println(Thread.currentThread().getName()+"             .."+i);
if(i==10){
System.out.println("--------------------------------------------");
new FirstThread().start();
new FirstThread().start();
System.out.println("---------------------------------------------");
}
}
}
}
结果:红色部分每次运行都不一致,因为多线程也是并发的
main             ..0
main             ..1
main             ..2
main             ..3
main             ..4
main             ..5
main             ..6
main             ..7
main             ..8
main             ..9
main             ..10
--------------------------------------------
Thread-0:0
---------------------------------------------
Thread-1:0
Thread-1:1
Thread-1:2
Thread-1:3
Thread-0:1
Thread-1:4
Thread-1:5
main             ..11
Thread-1:6
Thread-1:7
Thread-1:8
Thread-1:9
Thread-0:2
Thread-0:3
main             ..12
main             ..13
......

总结 :从上面结果可以看出Thread-0和Thread-1两条线程输出的i变量都不连续(注意:i变量是FirestThread的实例属性,而不是局部变量,但因为程序每次创建线程都会创建一个FirstThread对象,所以Thread-0和Thread-1不能共享该实例属性)。

使用继承Thread类的方法来创建线程类,多条线程之间无法共享线程类的实例变量。

2.实现Runnable接口创建线程类

public class SecondThread implements Runnable {
private int i;
public void run() {
for(;i<20;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
public static void main(String [] args){
for(int i=0;i<20;i++){
System.out.println(Thread.currentThread().getName()+"             .."+i);
if(i==10){
SecondThread st=new SecondThread();
//通过new Thread( Runable target,String name)来创建新线程
new Thread(st,"线程1").start();
new Thread(st,"线程2").start();
}
}
}
结果:红色部分每次运行都不一致,因为多线程也是并发的
main             ..0
main             ..1
main             ..2
main             ..3
main             ..4
main             ..5
main             ..6
main             ..7
main             ..8
main             ..9
main             ..10
--------------------------------------------
线程1:0
--------------------------------------------
线程1:1
线程2:1
线程2:3
main             ..11
线程2:4
线程2:5
线程2:6
线程1:2
线程2:7
线程2:9
线程2:10
线程2:11
线程2:12
线程2:13
main             ..12
线程2:14
线程2:15
线程2:16
线程2:17
线程1:8
线程2:18
main             ..13
main             ..14
线程1:19
main             ..15
main             ..16
main             ..17
。。。。

总结:根据源代码中Thread类构造方法  Ruanalbe接口对象target只能作为参数传递到Thread构造方法中,所以多个线程可以共用一个Runnable对象,因为都用同一个Runnable对象所以在Runnable实现类的实例变量也可以共享了。

所以Runable非常适合多个相同线程来处理同一份资源的情况。

二.线程的生命周期

1.New新建 :当线程被创建时,该线程处于新建状态,此时它和其他java对象一样,仅仅由Java虚拟机为其分配了内存,并初始化了其成员变量的值。(此时的线程没有表现出任何表现出任何线程的动态特征,程序也不会执行线程的线程执行体)new Thread()||new Thread(Runnable target,String name)。

2.Runnable就绪:就绪也就是说启动线程,但是启动线程使用start方法,而不是run方法!永远不要调用线程对象的run()方法!调用start方法来启动线程,系统会将该run方法当成线程执行体来处理。如果直接调用线程对象的run方法。则run方法会立即执行,且在这个run方法的执行体未执行结束前其他线程无法并发执行(即系统会将run方法当做一个普通对象的普通方法,而不是线程执行体对待)

附1:如果有一个主线程,一个子线程。当根据逻辑代码该调用子线程时不一定会立即调用,为了想在子线程start()后立即调用子线程,可以考虑使用Thread.sleep(1),这样会让当前线程(主线程)睡眠1毫秒,因为cpu在这1毫秒中是不会休息的,这样就会去执行一条处于就绪状态的线程。

附2:不能对已经处于就绪状态的线程,再次使用start()

3.Running 运行:当处于就绪状态时,该线程获得cpu,执行体开始运行,就处于运行状态了。

4.Blocked 阻塞:线程不可能一直处于运行状态(线程执行体足够短,瞬间就可以完成的线程排除),线程会在运行过程中需要被中断,因为是并发,目的是会让其他线程获得执行的机会,线程的调度细节取决于OS采用的策略。(抢占式调度xp win7 linux unix..)。如果是一些特殊的小型设备可能采用 协作式调度(只有线程自己调用它的sleep()或yield()才会放弃所占用的资源)。

5.Dead死亡:根据上图所示。测试测试某条线程是否已经死亡,可以调用线程对象的isAlive()方法,当线程处于就绪,运行,阻塞时,返回true。线程处于新建,死亡时返回false。

不能对已经死亡的线程调用start()方法使它重新启动,死亡就是死亡,是不能再次作为线程执行的。

当主线程结束时候,其他线程不受任何影响,并不会随之结束。一旦子线程启动起来后,它就拥有和主线程相同的地位,它不会受到主线程的影响。

三.控制线程

1.join线程:

让一个线程等待另一个线程完成的方法:join()。当在某个程序执行流中调用其他线程的join()方法,那该执行流对应的线程就会阻塞,知道被join()加入的join线程完成为止。join方法通常有使用线程的程序调用,将大问题划分成许多小问题,每个小问题分配一个线程。当所有的小问题都得到处理后,再调用 主线程来进一步操作(Thread t=new Thread();t.start();t.join简单来说就是加入到t线程。等t线程执行完成后才会返回出来执行线程。)

Join方法有三种重用形式:

Join():等待被join的线程执行完成

Join(long millis):等待join线程的时间最长为millis毫秒,如果在这个时间内,被join的线程还没有执行结束则不再等待)

Join(long millis,int nanos)千分之一毫秒(不用)

Code:

public class JoinThread implements Runnable{
@Override
public void run() {
for(int i=0;i<5;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
public static void main(String [] args) throws InterruptedException{
//实例化一个Runnable
JoinThread jt=new JoinThread();
//创建一个线程
new Thread(jt).start();
for(int i=0;i<10;i++){
if(i==3){
Thread th=new Thread(jt);
//启动第二个线程
th.start();
//main的线程中调用了th线程的join方法
//让第二个线程执行完成后再执行main
th.join();
}
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
结果:
Thread-0:0
Thread-0:1
Thread-0:2
main:0
main:1
Thread-0:3
main:2
Thread-0:4
Thread-1:0
Thread-1:1
Thread-1:2
Thread-1:3
Thread-1:4
main:3
main:4
main:5
main:6
main:7
main:8
main:9

2.后台线程:

Code:

public class DaemonThread implements Runnable{
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
public static void main(String [] args){
//要将前台线程转换成后台线程,需要在该线程刚新建还未start()之前转换。main线程也是前台线程
//所有前台线程死亡时,后台线程也就随之死亡。
DaemonThread dt=new DaemonThread();
Thread td=new Thread(dt,"线程1");
System.out.println("main方法是否是后台线程"+Thread.currentThread().isDaemon());
System.out.println("td线程最初是否是后台线程"+td.isDaemon());
//指定td为后台线程
td.setDaemon(true);
System.out.println("td线程执行setDaemon方法后是否是后台线程"+td.isDaemon());
//就绪启动后台线程
td.start();
for(int i=0;i<5;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}
结果:只要前台线程结束,后台线程也会随之结束,并不是马上结束
main方法是否是后台线程false
td线程最初是否是后台线程false
td线程执行setDaemon方法后是否是后台线程true
main 0
main 1
线程1:0
线程1:1
main 2
线程1:2
线程1:3
main 3
线程1:4
线程1:5
main 4
线程1:6
线程1:7
线程1:8
线程1:9
线程1:10
线程1:11
线程1:12
线程1:13

3.线程睡眠:sleep

/** * 线程睡眠:sleep有两种重载形式: * static void sleep(long millis) * static void sleep(long millis,int nanos) * */
public class SleepThread {
public static void main(String [] args) throws InterruptedException{
for(int i=0;i<5;i++){
System.out.println("线程:"+Thread.currentThread().getName()+"当前时间:"+new Date());
//让当前线程暂停2秒
Thread.sleep(2000);
}
}
}
结果:
线程:main当前时间:Fri Nov 04 18:51:33 CST 2011
线程:main当前时间:Fri Nov 04 18:51:35 CST 2011
线程:main当前时间:Fri Nov 04 18:51:37 CST 2011
线程:main当前时间:Fri Nov 04 18:51:39 CST 2011
线程:main当前时间:Fri Nov 04 18:51:41 CST 2011

4.线程让步(yield)

/** * yield()方法是一个和sleep方法有点类似的静态方法。yield也可以让当前正在执行的线程暂停 * 但它不会阻塞该线程,它只是将该线程转入就绪状态。yield只是让当前线程暂停一会儿,让系统的 * 调度器重新调度一次(完全可能的情况是:当一个线程调用了yield方法暂停之后,线程调度器又马上 * 将其调度出来重新执行。) * 实际上,当前线程调用了yield方法后,只有优先级和当前线程相同,甚至优先级高于当前线程的处于 * 就绪状态的线程才会获得执行机会。 * */
public class YieldThread implements Runnable{
@Override
public void run() {
for(int i=0;i<50;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
if(i==20){
Thread.yield();
}
}
}
public static void main(String [] args){
//启动第一条子线程
Thread td1=new Thread(new YieldThread(),"线程1");
//最高级
//td1.setPriority(Thread.MAX_PRIORITY);
//启动第二条子线程
Thread td2=new Thread(new YieldThread(),"线程2");
//最低级
td2.setPriority(Thread.MIN_PRIORITY);
td1.start();
td2.start();
System.out.println(Thread.currentThread().getName());
}
}

总结:sleep和yield区别

A.sleep方法暂停当前线程后,会给其他线程执行机会,不会理会其他线程的优先级。而yield只会给优先级>=当前优先级的线程执行机会

B.Sleep方法会将线程转入阻塞状态,知道经过阻塞时间才会转入就绪状态。而yield是不会将线程转入阻塞状态的,它只是强制当前线程进入就绪状态。

C.Sleep会抛出InterruptedException异常。而yield没有声明任何异常

D.Sleep方法比yield方法有更好的移植性。

E.通常不依靠yield来控制并发线程控制

转载于:https://my.oschina.net/91jason/blog/295333

Java多线程(全)学习笔记(上)相关推荐

  1. java多线程JUC学习笔记

    JUC(java.util.concurrent) 1.1 进程/线程 1.2并发/并行 并发编程:并发.并行 并发(多线程操作同一个资源) CPI一核,模拟出来多条线程,天下武功,唯快不破,快速交替 ...

  2. java多线程基础学习[狂神说java-多线程笔记]

    java多线程基础学习 一.线程简介 1.类比 2.程序进程线程 3.线程的核心概念 二.线程的实现(重点) 调用方法与调用多线程的区别 Thread 类 1.thread使用方法 2. 代码实现 3 ...

  3. Java基础篇 学习笔记

    List item Java基础篇 学习笔记 java基础篇 第1章 计算机.程序和java概述 学习笔记 1.1什么是计算机 简单来说:计算机就是 ' 存储 ' 和 ' 处理 ' 数据的电子设备. ...

  4. java/android 设计模式学习笔记(1)--- 单例模式

    前段时间公司一些同事在讨论单例模式(我是最渣的一个,都插不上嘴 T__T ),这个模式使用的频率很高,也可能是很多人最熟悉的设计模式,当然单例模式也算是最简单的设计模式之一吧,简单归简单,但是在实际使 ...

  5. Linux与C++11多线程编程(学习笔记)

    多线程编程与资源同步 在Windows下,主线程退出后,子线程也会被关闭; 在Linux下,主线程退出后,系统不会关闭子线程,这样就产生了僵尸进程 3.2.1创建线程 Linux 线程的创建 #inc ...

  6. java/android 设计模式学习笔记(1)---单例模式

    前段时间公司一些同事在讨论单例模式(我是最渣的一个,都插不上嘴 T__T ),这个模式使用的频率很高,也可能是很多人最熟悉的设计模式,当然单例模式也算是最简单的设计模式之一吧,简单归简单,但是在实际使 ...

  7. java最全学习路线

    java最全学习路线 java核心基础 JDK安装配置 数据类型和运算符 流程控制 数组 类和对象 封装继承多态 抽象类和接口 集合框架 泛型 实用类 IO流 多线程 反射 网络编程 XML解析 Ja ...

  8. [转]Java中文处理学习笔记——Hello Unicode

    Java中文处理学习笔记--Hello Unicode 作者: 车东 Email: chedongATbigfoot.com/chedongATchedong.com 写于:2002/07 最后更新: ...

  9. Java中文处理学习笔记——Hello Unicode (转)

    Java中文处理学习笔记--Hello Unicode (转)[@more@] Java中文处理学习笔记--Hello Unicode 作者: 车东 chedong@bigfoot.com 最后更新: ...

  10. Java中文处理学习笔记

    Java中文处理学习笔记--Hello Unicode 作者: 车东 Email: chedongATbigfoot.com/chedongATchedong.com 写于:2002/07 最后更新: ...

最新文章

  1. eclipse配置struts.xml自动提示
  2. Python—程序设计:抽象工厂模式
  3. NUXT 入门第一课: 关于 Nuxt.js
  4. 从popup window出发做navigation
  5. 你所阅读的,决定你是什么样的人
  6. [C++11]常量表达式函数
  7. jaxb-xjc.jar_使用xjc在一秒钟内生成您的JAXB类
  8. perl语言之列表与数组
  9. raspberry 防火墙_用于数据科学,Linux防火墙,Raspberry Pi NAS,openmediavault,Syncthing,微服务等的Python库
  10. 百面机器学习—3.逻辑回归与决策树要点总结
  11. Mac盖上屏幕后外接屏幕持续黑画面的解决方法
  12. python数学建模|综合评价方法
  13. 机票预订系统活动图_机票预订系统UML讲解
  14. win10 软路由_千元完美的家用低功耗软路由:J4125 迷你电脑GK41开箱体验!又是播放器,也是服务型AIO!...
  15. 深度学习—利用TensorFlow2实现狗狗品种品种(mobilenet实现)
  16. linux usr/bin/和 usr/local/bin之间的关系,什么是软链接?
  17. coc跑团san数值规则_【规则】克苏鲁coc跑团游戏术语/黑话,第三篇教学。
  18. java中字段可以取名is开头吗
  19. 许三多:浮躁社会的反义词
  20. JZOJsenior3488.【NOIP2013模拟联考11】矩形(rect)

热门文章

  1. delhpi7 tcombobox清楚重复项_数据分析必须想清楚的两个概念:指标和维度
  2. java word转图片tiff_Word 2010中将文档保存为TIFF图片的方法
  3. java读取http文件内容_使用HTTP读取文件的第一部分
  4. activemq mysql 配置详解_activeMQ数据库配置
  5. 装了python3但在cmd里不识别,Pip无法识别安装命令(Windows 7,Python 3.3)
  6. 计算机应用技术重点学科,福州大学省级重点学科介绍:计算机应用技术(081203)...
  7. 论高校计算机信息管理能力的提升,论高校计算机信息管理能力的提升
  8. action链接html,如何使用@ html.actionlink删除链接文本
  9. Flask Oauth
  10. shell read