`
jiao13953900900
  • 浏览: 32289 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

黑马程序员-多线程1

    博客分类:
  • java
阅读更多

 

---------------------- android培训java培训、期待与您交流! ----------------------

 

 

 

多线程1

 

 

 

理解多线程

    多线程是这样一种机制,它允许在程序中并发执行多个指令流,每个指令流都称为一个线程,彼此间互相独立。线程又称为轻量级进程,它和进程一样拥有独立的执行控制,由操作系统负责调度,区别在于线程没有独立的存储空间,而是和所属进程中的其它线程共享一个存储空间,这使得线程间的通信远较进程简单。

 

    具体到java内存模型,由于Java被设计为跨平台的语言,在内存管理上,显然也要有一个统一的模型。系统存在一个主内存(Main Memory), Java中所有变量都储存在主存中,对于所有线程都是共享的。每条线程都有自己的工作内存(Working Memory),工作内存中保存的是主存中某些变量的拷贝,线程对所有变量的操作都是在工作内存中进行,线程之间无法相互直接访问,变量传递均需要通过主存完成。

 

    多个线程的执行是并发的,也就是在逻辑上“同时”,而不管是否是物理上的“同时”。如果系统只有一个CPU,那么真正的“同时”是不可能的。多线程和传统的单线程在程序设计上最大的区别在于,由于各个线程的控制流彼此独立,使得各个线程之间的代码是乱序执行的,将会带来线程调度,同步等问题。

 

 

多线程:

 

进程:是一个正在执行中的程序。

每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元。

 

线程:就是进程中的一个独立的控制单元。

线程在控制着进程的执行。

 

一个进程中至少有一个线程。

 

 

 

Java VM  启动的时候会有一个进程java.exe.

 

该进程中至少一个线程负责java程序的执行。

而且这个线程运行的代码存在于main方法中。

该线程称之为主线程。

 

扩展:其实更细节说明jvm,jvm启动不止一个线程,还有负责垃圾回收机制的线程。

 

 

 

1,如何在自定义的代码中,自定义一个线程呢?

 

通过对api的查找,java已经提供了对线程这类事物的描述。就Thread类。

 

创建线程的第一种方式:继承Thread类。

步骤:

1,定义类继承Thread。

2,复写Thread类中的run方法。

目的:将自定义代码存储在run方法。让线程运行。

 

3,调用线程的start方法,

该方法两个作用:启动线程,调用run方法。

 

 

 

发现运行结果每一次都不同。

因为多个线程都获取cpu的执行权。cpu执行到谁,谁就运行。

明确一点,在某一个时刻,只能有一个程序在运行。(多核除外)

cpu在做着快速的切换,以达到看上去是同时运行的效果。

我们可以形象把多线程的运行行为在互相抢夺cpu的执行权。

 

这就是多线程的一个特性:随机性。谁抢到谁执行,至于执行多长,cpu说的算。

 

为什么要覆盖run方法呢?

 

Thread类用于描述线程。

该类就定义了一个功能,用于存储线程要运行的代码。该存储功能就是run方法。

 

也就是说Thread类中的run方法,用于存储线程要运行的代码。

 

创建线程方式一

 

继承Thread类

子类覆盖父类中的run方法,将线程运行的代码存放在run中。

建立子类对象的同时线程也被创建。

通过调用start方法开启线程。

例程

 

创建线程方式二

 

实现Runnable接口

子类覆盖接口中的run方法

通过Thread类创建线程,并将实现了Runnable接口的子类对象作为参数传递给Thread类的构造函数。

Thread类对象调用start方法开启线程。

思考:为什么要给Thread类的构造函数传递Runnable的子类对象?

因为,自定义的run方法所属的对象是Runnable接口的子类对象。

所以要让线程去指定指定对象的run方法。就必须明确该run方法所属对象。

 

线程的状态:

运行:通过start运行

冻结:sleep(00)或者wait();

消亡:stop()或者结束

临时:有执行权,在等待执行

线程的四种状态

 

    1、新状态:线程已被创建但尚未执行(start() 尚未被调用)。

 

    2、可执行状态:线程可以执行,虽然不一定正在执行。CPU 时间随时可能被分配给该线程,从而使得它执行。

 

    3、阻塞状态:线程不会被分配 CPU 时间,无法执行;可能阻塞于I/O,或者阻塞于同步锁。

 

    4、死亡状态:正常情况下run() 返回使得线程死亡。调用 stop()或 destroy() 亦有同样效果,但是不被推荐,前者会产生异常,后者是强制终止,不会释放锁。

 

原来线程都有自己默认的名称。

Thread-编号 该编号从0开始。

static Thread currentThread():获取当前线程对象。

getName(): 获取线程名称。

 

设置线程名称:setName或者构造函数。

class Test extends Thread
{
	//private String name;
	Test(String name)
	{
		//this.name = name;
		super(name);//在创建线程对象时候,使用父类的构造方法接收线程名称。
	}
	public void run()
	{
		for(int x=0; x<60; x++)
		{
			System.out.println((Thread.currentThread()==this)+"..."+this.getName()+" run..."+x);
		}
	}

}

 

 

多线程售票;

 

 

 

需求:简单的卖票程序。

多个窗口同时买票。

如果使用继承Thread类的方式创建多个线程,那么如果要共享一个数据,那个数据必须是静态的,因为多个对象共同操作同一个数据。

因为静态的变量声明周期过长,所以一般都使用实现Runnable的方式来进行共享数据。

 

创建线程的第二种方式:实现Runable接口

 

步骤:

1,定义类实现Runnable接口

2,覆盖Runnable接口中的run方法。

将线程要运行的代码存放在该run方法中。

 

3,通过Thread类建立线程对象。

4,将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。

 

为什么要将Runnable接口的子类对象传递给Thread的构造函数。

因为,自定义的run方法所属的对象是Runnable接口的子类对象。

所以要让线程去指定指定对象的run方法。就必须明确该run方法所属对象。

 

 

5,调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。

 

 

 

实现方式和继承方式有什么区别呢?

 

实现方式好处:避免了单继承的局限性。

在定义线程时,建立使用实现方式。

 

两种方式区别:

继承Thread:线程代码存放Thread子类run方法中。

实现Runnable,线程代码存在接口的子类的run方法。

 

class Ticket implements Runnable//extends Thread
{
	private  int tick = 100;
	public void run()
	{
		while(true)
		{
			if(tick>0)
			{
				System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);
			}
		}
	}
}


class  TicketDemo
{
	public static void main(String[] args) 
	{

		Ticket t = new Ticket();

		Thread t1 = new Thread(t);//创建了一个线程;
		Thread t2 = new Thread(t);//创建了一个线程;
		Thread t3 = new Thread(t);//创建了一个线程;
		Thread t4 = new Thread(t);//创建了一个线程;
		t1.start();
		t2.start();
		t3.start();
		t4.start();

	}
}

 通过分析,发现,打印出0,-1,-2等错票。

 

多线程的运行出现了安全问题。

 

问题的原因:

当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,

另一个线程参与进来执行。导致共享数据的错误。

 

解决办法:

对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。

 

 

 

Java对于多线程的安全问题提供了专业的解决方式。

 

就是同步代码块。

synchronized(对象)
{
	需要被同步的代码

}

 对象如同锁。持有锁的线程可以在同步中执行。

没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。

 

火车上的卫生间---经典。

 

同步的前提:

1,必须要有两个或者两个以上的线程。

2,必须是多个线程使用同一个锁。

 

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

 

 

好处:解决了多线程的安全问题。

 

弊端:多个线程需要判断锁,较为消耗资源,

class Ticket implements Runnable
{
	private  int tick = 1000;
	Object obj = new Object();
	public void run()
	{
		while(true)
		{
			synchronized(obj)
			{
				if(tick>0)
				{
					//try{Thread.sleep(10);}catch(Exception e){}
					System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);
				}
			}
		}
	}
}


class  TicketDemo2
{
	public static void main(String[] args) 
	{

		Ticket t = new Ticket();

		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);
		Thread t3 = new Thread(t);
		Thread t4 = new Thread(t);
		t1.start();
		t2.start();
		t3.start();
		t4.start();


	}
}

 线程安全问题实例:

 

需求:

银行有一个金库。

有两个储户分别存300员,每次存100,存3次。

 

目的:该程序是否有安全问题,如果有,如何解决?

 

 

如何找问题:

1,明确哪些代码是多线程运行代码。

2,明确共享数据。

3,明确多线程运行代码中哪些语句是操作共享数据的。

 

 

class Bank
{
	private int sum;
	//Object obj = new Object();
	public synchronized void add(int n)
	{
		//synchronized(obj)
		//{
			sum = sum + n;
			try{Thread.sleep(10);}catch(Exception e){}
			System.out.println("sum="+sum);
		//}
	}
}

class Cus implements Runnable
{
	private Bank b = new Bank();
	public void run()
	{		
		for(int x=0; x<3; x++)
		{
			b.add(100);
		}
	}
}


class  BankDemo
{
	public static void main(String[] args) 
	{
		Cus c = new Cus();
		Thread t1 = new Thread(c);
		Thread t2 = new Thread(c);
		t1.start();
		t2.start();
	}
}

 

 

同步函数用的是哪一个锁呢?

函数需要被对象调用。那么函数都有一个所属对象引用。就是this。

所以同步函数使用的锁是this。

如果同步函数被静态修饰后,使用的锁是什么呢?

 

通过验证,发现不在是this。因为静态方法中也不可以定义this。

 

静态进内存是,内存中没有本类对象,但是一定有该类对应的字节码文件对象。

类名.class  该对象的类型是Class

 

 

静态的同步方法,使用的锁是该方法所在类的字节码文件对象。 类名.class

//懒汉式

class Single
{
	private static Single s = null;
	private Single(){}


	public static  Single getInstance()
	{
		if(s==null)
		{
			synchronized(Single.class)
			{
				if(s==null)
					//--->A;
					s = new Single();
			}
		}
		return s;
	}
}

 在懒汉式的单利设计模式中,会出现安全问题,为了解决这个问题,可以有两种方式:

1.使用同步函数:

特点:在函数上面添加同步,那么每一次获取该单利对象都会进行一次锁的判断,效率较低。

2.在同步函数内使用同步代码块:

特点:可以解决安全问题,在同步代码块外面还要加一次判断,如果不加判断效果同同步函数一样。

注意:因为单利模式中getInstance()方法是静态的,所以该同步代码块的锁是Single.class,即本类的字节码文件对象。

 

多线程死锁:

死锁实现方式:同步中嵌套同步,但是锁不是同一个。

 

遇到死锁程序就停了,所以在实际开发中应该尽量避免死锁;

死锁在只有面试中经常会遇到。

死锁的一个细节问题:在执行过程中需要改变标记,由一个嵌套锁转换为另一个嵌套锁。

以下是代码示例:

class Test implements Runnable
{
	private boolean flag;
	Test(boolean flag)
	{
		this.flag = flag;
	}

	public void run()
	{
		if(flag)
		{
			while(true)
			{
				synchronized(MyLock.locka)
				{
					System.out.println(Thread.currentThread().getName()+"...if locka ");
					synchronized(MyLock.lockb)
					{
						System.out.println(Thread.currentThread().getName()+"..if lockb");					
					}
				}
			}
		}
		else
		{
			while(true)
			{
				synchronized(MyLock.lockb)
				{
					System.out.println(Thread.currentThread().getName()+"..else lockb");
					synchronized(MyLock.locka)
					{
						System.out.println(Thread.currentThread().getName()+".....else locka");
					}
				}
			}
		}
	}
}


class MyLock
{
	static Object locka = new Object();
	static Object lockb = new Object();
}

class  DeadLockTest
{
	public static void main(String[] args) 
	{
		Thread t1 = new Thread(new Test(true));
		Thread t2 = new Thread(new Test(false));
		t1.start();
		t2.start();
	}
}

 

 

 

 

---------------------- android培训java培训、期待与您交流! ----------------------

分享到:
评论

相关推荐

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

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

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

    NULL 博文链接:https://lvwenwen.iteye.com/blog/1470696

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

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

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

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

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

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

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

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

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

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

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

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

    图解java多线程设计模式

    日本资-深技术作家、《程序员的数学》作者结城浩执笔,264张图表(UML类图、时序图、Timethreads图等)穿插文中,通过类比手法和浅显的语言,逐一讲解与多线程和并发处理相关的12种设计模式。每章附带练习题和答案,...

    java拼图游戏源码.zip

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

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

    精选12种与多线程和并发处理相关的设计模式 264张图表 + 300段Java示例程序 = 轻松学习多线程编程 日本经典多线程入门书,原版长销11年! 本书适合以下读者阅读 a.对多线程感兴趣的人 b.对Java编程感兴趣的人 c.对...

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

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

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

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

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

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

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

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

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

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

    学成在线-分布式任务视频处理

    2.适用于黑马程序员Java企业级实战开发《学成在线》微服务项目,基于SpringCloud、SpringCloudAlibaba技术栈开发。 3.采用xxl-job技术。 4.步骤概要: 4.1 作业分片方案 4.2 保证任务不重复执行 4.3 业务流程 5....

    黑马JVM学习笔记二

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

    JAVA核心知识点整理.pdf

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

    mfc教程(word版)

    第1章 MFC概述 1 1.1 MFC是一个编程框架 1 1.1.1 封装 1 1.1.2 继承 2 1.1.3 虚拟函数和动态约束 2 1.1.4 MFC的宏观框架体系 2 1.2 MDI应用程序的构成 3 1.2.1 构成应用程序的对象 3 1.2.2 构成应用程序的对象之间的...

Global site tag (gtag.js) - Google Analytics