`
AisiniLe
  • 浏览: 9264 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
最近访客 更多访客>>
社区版块
存档分类
最新评论

黑马程序员_Java多线程基础

阅读更多

----------- Android培训Java培训Java学习型技术博客、期待与您交流! ------------

学习到多线程,就必须对多线程中的概念理解透彻.

程序:

程序是一段静态的代码,它是应用程序执行的蓝本.

进程:

进程是程序的一次动态执行过程,它对应了从代码加载、执行至执行完毕的一个完整过程,这个过程也是进程本身从产生、发展至消亡的过程.

线程:

线程是比进程更小的单位,一个进程执行过程中可以产生多个线程,每个线程有自身的产生、存在和消亡的过程,也是一个动态的概念.

每个进程都有一段专用的内存区域,而线程间可以共享相同的内存区域(包括代码和数据),并利用这些共享单元来实现数据交换、实时通信与必要的同步操作.

主线程:

每个Java程序都有一个默认的主线程。Java程序总是从主类的main方法开始执行。当JVM加载代码,发现main方法后就启动一个线程,这个线程就称作"主线程",该线程负责执行main方法.

在main方法中再创建的线程就是其他线程。

 

在Java中,创建线程的方式有两种:

1、继承Thread类,覆盖run()方法:使用Thread子类创建线程的优点是可以在子类中增加新的成员变量或方法,使线程具有某种属性或功能。但Java不支持多继承,Thread类的子类不能再扩展其他的类.

2、实现Runnable接口:用Thread类直接创建线程对象,使用构造函数Thread(Runnable target)(参数target是一个Runnable接口),创建线程对象时必须向构造方法参数传递一个实现Runnable接口类的实例,该实例对 象称作所创线程 的目标对象。当线程调用start()方法,一旦轮到它使用CPU资源,目标对象自动调用接口中的run()方法(接口回调).

线程间可以共享相同的内存单元(包括代码和数据),并利用这些共享单元来实现数据交换、实时通信与必要的同步操作。对于Thread(Runnable target)创建的使用同一目标对象的线程,可以共享该目标对象的成员变量和方法.

另外,创建目标对象类在必要时还可以是某个特定类的子类,因此,使用Runnable接口比使用Thread的子类更具有灵活性.


线程的常用方法:

1.start():线程调用该方法将启动线程,从新建态进入就绪队列,一旦享用CPU资源就可以脱离创建它的线程,独立开始自己的生命周期.

2.run():Thread类的run()方法与Runnable接口中的run()方法功能和作用相同,都用来定义线程对象被调度后所进行的操作,都是系统自动调用而用户不得引用的方法。run()方法执行完毕,线程就成死亡状态,即线程释放了分配给它的内存(死亡态线程不能再调用start()方法)。在线程没有结束run()方法前,不能让线程再调用start()方法,否则将发生IllegalThreadStateException异常.

sleep(int millsecond):有时,优先级高的线程需要优先级低的线程做一些工作来配合它,此时为让优先级高的线程让出CPU资源,使得优先级低的线程有机会运行,可以使用sleep(int millsecond)方法。线程在休眠时被打断,JVM就抛出InterruptedException异常。因此,必须在try-catch语句块中调用sleep方法.

3. isAlive():当线程调用start()方法并占有CPU资源后该线程的run()方法开始运行,在run()方法没有结束之前调用isAlive()返回true,当线程处于新建态或死亡态时调用isAlive()返回false.

4. currentThread():是Thread类的类方法,可以用类名调用,返回当前正在使用CPU资源的线程。

5.interrupt():当线程调用sleep()方法处于休眠状态,一个占有CPU资源的线程可以让休眠的线程调用interrupt()方法"吵醒"自己,即导致线程发生IllegalThreadStateException异常,从而结束休眠,重新排队等待CPU资源。

 

线程的几种状态:

线程在一定条件下,状态会发生变化。线程变化的状态转换图如下:

  1、新建状态(New):新创建了一个线程对象。

  2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。

  3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。

  4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:

  (一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。

  (二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。

  (三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

  5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

 

创建线程的具体方式:

1.继承Thread类,覆盖run()方法

大致结构是:

 

class 类名 extends Thread{ 

 属性; 
 … 
 public void run(){ 
.....
 } //必须覆写run()方法

方法;
 … 
   
 } 
 

 

简单的示例:

 

 
 public class ThreadDemo
 {
 	public static void main(String[] args)
 	{
 		Demo demo = new Demo();
 		
 		demo.start();
 		
 		for(int i = 1; i <= 100; i++)
 		{
 			System.out.println("main Run(主)" + i);
 		}
 	}
 }
 
 class Demo extends Thread
 {
 	public void run()
 	{
 		for(int i = 1; i <= 100; i++)
 		{
 			System.out.println("Demo Run(副)" + i);
 		}
 	}
 }
 
 

运行结果是main与Demo方法的交替出现..

由于循环次数较多,只截取其中的部分片段

 

...................
Demo Run(副)93
 Demo Run(副)94
 Demo Run(副)95
 Demo Run(副)96
 Demo Run(副)97
 Demo Run(副)98
 Demo Run(副)99
 Demo Run(副)100
 main Run(主)48
 main Run(主)49
 main Run(主)50
 main Run(主)51
 main Run(主)52
 main Run(主)53
 main Run(主)54
...................
 

每次运行结果都不相同:因为多个线程获取cpu的执行权,cpu执行到谁,谁就运行.多线程一个特性:随机性.

 

如果将上述代码中的demo.start()换成demo.run().此时就类似于单线程了,先执行完Demo Run(副),然后再执行main Run(主).

结果分析:

如果是直接调用run()方法,此时并没有使用新建的线程执行,还在主线程的内部进行执行,而调用start()表明开启新的进程,并在新的进程中执行该段方法.

直接调用run()方法相当于在主方法中对象直接调用本类方法.

2.实现Runnable接口

具体步骤:

 

--定义类实现Runnable接口

--覆盖Runnable接口中的run方法,将线程要有运行的代码放在run方法中

--通过Thread类建立线程对象

--Runnable接口的子类对象作为实际对象传给Thread类的构造函数,自定义的run方法所属的对象是Runnable接口的子类对象,所以要让线程去明确指定对象的run方法

--调用Thread类的start方法开启线程.

大致结构是:

 

class 类名 implements Runnable{ 
 属性;
 … 
 public void run(){ 
.....
 } //实现接口中run()方法
 方法; 
   ....
 } 
 

  简单的示例:

 

 class TicketRun implements Runnable
 {
 	private int ticket = 100;
 
 	public void run()
 	{
 		while (true)
 		{
 			if (ticket > 0)
 			{
 				System.out.println(Thread.currentThread().getName()
 				+ " Sale : " + ticket--);
 			}			
 		}
 	}
 }
 
 public class TicketRunDemo
 {
 	public static void main(String[] args)
 	{
 		TicketRun rd = new TicketRun();
 
 		Thread t1 = new Thread(rd);// 创建线程对象时,就要明确需要运行的run方法
 		Thread t2 = new Thread(rd);
 	
 
 		t1.start();
 		t2.start();
 	
 
 	}
 }
 

 

运行结果(部分截取)

 

.........................
Thread-3 Sale : 46
 Thread-1 Sale : 47
 Thread-3 Sale : 42
 Thread-0 Sale : 43
 Thread-2 Sale : 44
 Thread-0 Sale : 39
 Thread-3 Sale : 40
 Thread-1 Sale : 41
 Thread-3 Sale : 36
 Thread-3 Sale : 34
 Thread-0 Sale : 37
 Thread-2 Sale : 38
 Thread-0 Sale : 32
 Thread-3 Sale : 33
.............................
 

那么两者有什么区别?

两者线程代码存放的位置不同:实现接口的线程代码放在接口的子类run方法中,继承方式的线程代码存放在Thread子类的run方法中

实现接口方式避免了单继承的局限性,在定义线程时,建议实现接口方式.

如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。

 

实现Runnable接口比继承Thread类所具有的优势:

1):适合多个相同的程序代码的线程去处理同一个资源

2):可以避免java中的单继承的限制

3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。

 

 

多线程的安全问题(同步与死锁)

 

当多条语句在操作同一个线程共享数据时,一个线程多多条语句只执行了一部分,还没有执行完,另一个线程参与执行,导致共享数据的错误.

解决方式:同步代码块.synchronized.对象如同锁,持有锁的线程可以再同步代码中执行,没有锁的线程,即使获得cpu执行权,也进不去同步代码块.

将操作共享数据的语句放入同步代码块中 原理:设置标志位()

 同步的前提:

1.必须要有多个线程,单线程无需多同步.

2.多个线程必须使用同一个锁.

 必须保证同步中只能有一个线程在运行

 

优点:解决多线程的安全问题

弊端:每次必须进行锁判断,较为消耗资源.

采用同步的话,可以使用同步代码块和同步方法两种来完成。

【同步代码块】:

语法格式:

synchronized(同步对象){

 //需要同步的代码

}

while (true)
 {
 	synchronized (this)
 	{
 		if (ticket > 0)
 		{
 
 			try
 			{
 				Thread.sleep(10);
 			} catch (Exception e)
 			{
 				System.out.println("error");
 			}
 			System.out.println(Thread.currentThread().getName()
 						+ " --code-- : " + ticket--);
 		}
 
 	}
}
 

【同步方法】

 

也可以采用同步方法。

语法格式为synchronized 方法返回类型方法名(参数列表){

    // 其他代码

}

 

public synchronized void show()	
{
 
 	if (ticket > 0)
 	{
 
 		try
 		{
 			Thread.sleep(10);
 		} catch (Exception e)
 		{
 			System.out.println("error");
 		}
 			System.out.println(Thread.currentThread().getName() + " ----show---- : "
 					+ ticket--);
 	}
 
 }
 

需要注意的问题:

 

同步方法使用的锁

方法需要被对象调用,方法都有一个所属对象的引用,就是this.所以同步方法使用的锁是this.

若同步方法被static修饰.锁是Class对象,则使用的锁是 类名.class

静态进内存时,内存中没有本类对象,但有该类的字节码对象

 

提醒一下,当多个线程共享一个资源的时候需要进行同步,但是过多的同步可能导致死锁。

具体示例:

 

 

 public class ThisLockDemo
 {
 	public static void main(String[] args)
 	{
 		TicketRun rd = new TicketRun();
 
 		Thread t1 = new Thread(rd);// 创建线程对象时,就要明确需要运行的run方法
 		Thread t2 = new Thread(rd);
 
 		t1.start();
 		try
 		{
 			Thread.sleep(20);
 		} catch (Exception e)
 		{
 		}
 		rd.flag = false;
 		t2.start();
 	}
 
 }
 
 class TicketRun implements Runnable
 {
 	private int ticket = 100;
 
 	boolean flag = true;
 	//Object o = new Object();
 
 	public void run()
 	{
 		if (flag)
 		{
 			while (true)
 			{
 				synchronized (this)
 				{
 					if (ticket > 0)
 					{
 
 						try
 						{
 							Thread.sleep(10);
 						} catch (Exception e)
 						{
 							System.out.println("error");
 						}
 						System.out.println(Thread.currentThread().getName()
 								+ " --code-- : " + ticket--);
 					}
 
 				}
 			}
 		} else
 			while (true)
 				show();
 	}
 
 	public synchronized void show()	//若同步方法被static修饰.锁是Class对象,则使用的锁是	类名.class
 	{
 
 		if (ticket > 0)
 		{
 
 			try
 			{
 				Thread.sleep(10);
 			} catch (Exception e)
 			{
 				System.out.println("error");
 			}
 			System.out.println(Thread.currentThread().getName() + " ----show---- : "
 					+ ticket--);
 		}
 
 	}
 }
 

 

 

小插曲:单例设计模式

 

饿汉式与懒汉式

饿汉式:

 

class Single
 {	
 	private static final Single s = new Single();
 	
 	private Single(){}
 	
 	public static Single getInstance()
 	{
 		return s;
 	}
 }
 

懒汉式:(实例的延时加载)--->保证不了单例,存在安全问题.

 

class Single
 {
 	private static Single s = null;
 	
 	private Single(){}
 	
 	public static Single getInstance() 
 	{
 		if(s == null)
 			s = new Single();
 		return s;
 	}
 }

 

加强的懒汉式:

 

public class Single
 {
 	private static Single s = null;
 	private Single() {}
 	
 	public static Single getInstance()
 	{
 		if(s == null)
 		{
 			synchronized(Single.class)
 			{
 				if(s == null)
 					s = new Single();
 			}
 		}
 		return null;
 	}
 }

 

 

Done...

 

 

 

 

分享到:
评论

相关推荐

    黑马程序员_张孝祥_Java多线程与并发库 视频+代码+资料

    黑马程序员_张孝祥_Java多线程与并发库,视频+代码+资料

    黑马程序员_张孝祥_Java多线程与并发库

    黑马程序员_张孝祥_Java多线程与并发库,老师讲的非常仔细,老师很有耐心.欢迎大家下载学习.

    传智播客.黑马程序员《Java 基础入门》课后习题答案

    1、 面向对象、跨平台性、健壮性、安全性、可移植性、多线程性、动态性等。 2、 JRE(Java Runtime Environment,Java 运行时环境),它相当于操作系统部分,提供了 Java 程序运 行时所需要的基本条件和许多 Java ...

    黑马程序员-java多线程技术01

    NULL 博文链接:https://huangminwen.iteye.com/blog/1157983

    传智播客_Java培训_毕向东_Java基础[05-多线程]

    传智播客_Java培训_毕向东_Java基础[05-多线程]系黑马程序员_毕向东_Java基础视频教程

    黑马程序员–Java多线程讲解笔记

    当一个进程中线程有多个时,是多线程。  为什么要用多线程  1,让计算机"同时"做多件事情,节约时间。  2,后台运行程序,提高程序的运行效率.。  3,多线程可以让程序"同时"处理多个事情。  4,...

    java并发库高级应用源码--张孝祥

    java并发库thread使用,传统线程技术、定时器技术、线程互斥技术,同步通讯技术、多线程共享数据、并发库应用,线程锁技术,阻塞锁、阻塞队列,线程池等应用

    图解java多线程设计模式

    java.util.concurrent包、synchronized关键字、Swing框架、Java内存模型等内容也均有涉及,不仅能够了解Java多线程的相关知识,还可加深对Java语言的理解。 本书适合以下读者阅读 a.对多线程感兴趣的人 b.对Java...

    图解java多线程设计模式2017年8月最新版

    264张图表 + 300段Java示例程序 = 轻松学习多线程编程 日本经典多线程入门书,原版长销11年! 本书适合以下读者阅读 a.对多线程感兴趣的人 b.对Java编程感兴趣的人 c.对设计模式感兴趣的人 d.对面向对象开发感兴趣的...

    java拼图游戏源码.zip

    这是一个java写的拼图游戏,用了swing、多线程等知识,可以设置行数列、列数、还有使用多线程写的游戏动画,拼图的图块具有磁贴设计,代码封装性较强,容易移植,纯原创。

    《Java基础案例教程(第2版)》课后习题答案1

    第1章 Java开发入门一.填空题1. 面向对象,SUN 2. JavaSE,JavaEE,JavaME3.面向对象、跨平台性、支持多线程4. JDK5.bin

    黑马程序员 安卓学院 万元哥项目经理 分享220个代码实例

    |--利用FinalHttp实现多线程断点续传 |--加密之MD5 |--动画Animation详解 |--动画之view左右抖动 |--动画之移动动画 |--动画之组合动画 |--动画之缩放动画ScaleAnimation |--反序列化对象 |--发送短信 读天气 调音量...

    图解java多线程设计模式 2017年8月最新版

    这本书的pdf文件有162M,是最新版高清的pdf,网上原书售价60多元,这里提供的附件是百度云的链接地址。

    拼图游戏 (源码+所有文件)

    萌新做的一个简单的Java拼图游戏 启动类:pers.sept.jigsaw1.swing下的...多线程。 下拉列表选择数字可以切换关卡图片,最后的“+”,可以添自己的图片到关卡中。 设有背景音乐 有一键通过按钮 等等,块下载去看看吧。

    JAVA核心知识点整理.pdf

    给大家分享一篇我在学习java过程中...包含常见的面试题:JVM、Java集合、多线程并发、java基础、SSM框架原理、微服务、Netty和RPC、网络、日志、算法、数据结构、加密、分布式算法、机器学习、大数据等知识点,都是干货

    Java基础最全笔记文档

    Java基础笔记分为 Java基础篇 和 Java加强篇 Java基础篇包括: 1. Java环境搭建、Java快速入门、IDEA开发工具 ...11. 多线程 12. 网络编程 13. 单元测试、反射、注解、动态代理 14. XML、解析、工厂模式和装饰模式

    java7源码-Java-concurrent:Java并发编程

    黑马程序员——Java异步课程源码 plugin Lombok logback.xml 说明 课堂代码主要在 case_java8 子模块内,按章节划分, io.github.hank.java.concurrent.n2 - 同步与异步 io.github.hank.java.concurrent.n3 - Java...

    百度地图毕业设计源码-Java-Notes:2020Java快速成长学习路线,从0到1的过程,打破你知识的盲区,渐渐爱上Java,我想对还是小

    @学习路线根据黑马程序员学习路线改编 Part1: Java基础&Web基础 Java基础 面向对象思想 集合框架 IO流 多线程与并发 异常处理 网络编程 数据库 MySQL Oracle JDBC C3P0 Druid 前端技术 HTML CSS JavaScript jQuery ...

    黑马乐优商城项目后台管理系统.zip

    多线程支持: Java内置了对多线程的支持,允许程序同时执行多个任务。这对于开发需要高并发性能的应用程序(如服务器端应用、网络应用等)非常重要。 自动内存管理(垃圾回收): Java具有自动内存管理机制,通过...

    黑马JVM学习笔记二

    title: 黑马JVM学习笔记二 date: 2020-01-13 1:00:00 ...线程是私有的,多线程之间分别有各自的程序计数器记录对应线程的执行位置 程序计数器是Java虚拟机规范中唯一一个不会存在内存溢出的区(堆和栈等会出现内存溢

Global site tag (gtag.js) - Google Analytics