`
AisiniLe
  • 浏览: 9571 次
  • 性别: 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多线程与并发库,老师讲的非常仔细,老师很有耐心.欢迎大家下载学习.

    01_黑马程序员_张孝祥_Java基础加强_课程价值与目标介绍.zip

    【标题】"01_黑马程序员_张孝祥_Java基础加强_课程价值与目标介绍.zip" 提供的是一门由黑马程序员机构推出的Java基础强化课程,由讲师张孝祥主讲,旨在深入讲解Java编程的基础知识并进行能力提升。 【描述】中提到...

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

    ### Java多线程基础 1. **线程的概念**:在Java中,线程是程序执行流的基本单元。一个标准的Java应用程序至少有一个线程,即主(main)线程。通过创建多个线程,可以实现并发执行,提高程序的运行效率。 2. **创建...

    黑马程序员_Java基础辅导班教程课件[第01期]第13天

    在"黑马程序员_Java基础辅导班教程课件[第01期]第13天"中,我们聚焦于Java编程语言的基础知识,这是一门面向初学者的课程,旨在帮助学员快速掌握Java开发的基本技能。第13天的课程通常会涵盖上一天(第12天)所学...

    黑马程序员_毕向东_Java基础源码.rar

    这个名为“黑马程序员_毕向东_Java基础源码.rar”的压缩包文件,包含了丰富的Java基础源代码实例,对于初学者来说,是深入了解Java编程的良好资源。 一、Java基本语法 Java语言以其严格的类型检查和面向对象特性...

    黑马程序员_(适合初学者入门的Java基础视频)

    "黑马程序员_(适合初学者入门的Java基础视频)"是一套专为初学者设计的Java基础教学课程,旨在帮助零基础的学习者逐步掌握Java编程的基本概念和技能。 该视频教程涵盖了Java的基础知识,包括: 1. **环境搭建**:...

    黑马程序员_Java基础辅导班教程课件[第01期]第10天

    在"黑马程序员_Java基础辅导班教程课件[第01期]第10天"中,我们聚焦于Java编程语言的基础知识,这是一门面向初学者的课程,旨在帮助学员快速掌握Java的核心概念。通过这个阶段的学习,学员将能够理解并运用Java的...

    黑马程序员_Java基础辅导班教程课件[第01期]第4天

    在"黑马程序员_Java基础辅导班教程课件[第01期]第4天"中,我们可以推测这是针对初学者的Java编程课程,旨在帮助学员掌握Java的基础知识。 在课程的第4天,可能讲解了以下核心概念: 1. **类(Class)**:`Demo.class...

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

    本教程将聚焦于Java中的多线程技术,以“黑马程序员-java多线程技术01”为学习起点,探讨如何在Java中实现并管理线程。 首先,我们来理解什么是线程。线程是操作系统分配CPU时间的基本单元,一个进程中可以有多个...

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

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

    黑马程序员_毕向东最新经典Java基础视频

    根据提供的文件信息,我们可以推断出这是一套由知名IT教育机构“黑马程序员”出品、由讲师毕向东主讲的Java基础教学视频。由于实际视频内容无法直接获取,本篇将依据标题、描述以及部分标签内容,综合分析并展开相关...

    黑马程序员_从零开始征服Android之旅(第一季)源码和笔记

    这份资料涵盖了一系列关键知识点,包括但不限于Android基础、UI设计、数据存储、网络通信、多线程以及性能优化等方面。 首先,Android基础是入门的关键,它涉及到Android系统架构、Android Studio集成开发环境的...

    黑马程序员入学Java精华总结

    ### 黑马程序员入学Java精华总结 #### 一、Java概述与基础知识 1. **何为编程?** - 编程是指通过编写计算机能够理解的指令来解决问题或完成特定任务的过程。这些指令通常被组织成算法,并使用某种编程语言实现。...

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

    Java多线程是Java编程中不可或缺的部分,它允许程序同时执行多个任务,提高了程序的效率和响应速度。本文主要探讨了多线程的概念、应用场景以及Java中创建线程的两种方式。 首先,进程和线程是理解多线程的基础概念...

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

    Java并发库是Java编程中非常重要的一个领域,它为开发者提供了高效、安全的多线程编程工具。在《java并发库高级应用源码--张孝祥》中,我们将会深入探讨Java中的线程管理和并发控制策略,这对于我们理解和优化多线程...

    多线程高新(黑马程序员)

    本文将深入探讨“多线程高新”这一主题,结合黑马程序员的课程内容,来阐述多线程的核心概念、优势、实现方式以及在实际应用中的注意事项。 首先,多线程是指在一个进程中同时执行多个线程,这些线程可以共享同一...

    黑马程序员_2小时教你写一个安卓程序[第01天]课件源码

    在本课程中,“黑马程序员”将引导我们快速入门安卓应用程序的开发,这是一段为期两天的密集学习旅程,第01天的课程主要侧重于基础知识的建立和首个安卓程序的编写。通过这次学习,我们将了解到安卓开发环境的搭建、...

    黑马程序员java基础试题、笔记

    "黑马程序员java基础试题、笔记"这个压缩包资源为Java初学者和希望加入"黑马程序员"培训课程的学员提供了丰富的学习材料。这些资源包括面试问题合集、整理的资料、Android面试题、学员入学面试总结、面试技巧、必须...

    黑马程序员毕向东java基础课堂完整版文档

    《黑马程序员毕向东Java基础课堂完整版文档》是一份全面且深入的Java学习资源,由知名教育机构黑马程序员的讲师毕向东倾力打造。这份资料涵盖了从Java编程基础到高级特性的全过程,旨在帮助初学者系统地掌握Java编程...

Global site tag (gtag.js) - Google Analytics