Java基础复习笔记系列 八 多线程编程
Java基础复习笔记系列之 多线程编程
参考地址:
http://blog.csdn.net/xuweilinjijis/article/details/8878649
今天的故事,让我们从上面这个图开始讲起。线程状态转换图。图很简单不要想得太复杂。了解了线程的基本的生命周期,那么我们要使用好它,就离不开了经常使用的几个方法:先来一段代码:
public class TestSleep {
public static void main(String[] args){
MyThread t = new MyThread();
t.start();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t.interrupt();
}
} class MyThread extends Thread{
public void run(){
while(true){
System.out.println(Math.random());
try {
sleep(1000);
} catch (InterruptedException e) {
return;
}
}
}
}
我们发现调用从线程开始执行之后,主线程开始了睡觉。睡了十秒钟之后醒了,接着执行t.interrupt()方法,然后将从线程的while(true)条件破坏掉,然后我们看到的结果,就是再输出了十个随机double数之后,退出了(run方法一结束,线程就结束)。这其中我们用到了Thread.sleep()方法,显然,从上面的调用来看,它是一个静态的方法。它会抛出一个InterruptedException异常,而且我们必须且只能用try...catch...来处理它。且它的原则是谁调用它,谁睡眠。还有温故一下时间的概念,1秒=1000毫秒=1000,000纳秒。接着,再来一段程序:
public class TestJoin {
public static void main(String[] args){
Thread t = new MyThread("subThread");
t.start();
try{
t.join();
}catch(InterruptedException e){
e.printStackTrace();
}
for(int i = 0; i < 20; i++){
System.out.println("i am mainThread!");
}
}
} class MyThread extends Thread{
public MyThread(String s){
super(s);
}
public void run(){
for(int i = 0; i < 20; i++){
System.out.println("i am "+ getName());
try {
sleep(1000);
} catch (InterruptedException e) {
return;
}
}
}
}
在这段程序中,我们使用了join()方法,用于合并线程。之所以用20作为测试数据,是防止时间片切换时的干扰。注意其中,用到了super(s)用于继承父类的构造方法,是为了给线程命名字。
知识点范围:
1、进程和线程区别?线程的创建和启动?(启动之后)线程的交互?
2、java中fork()函数。fork的含义是:分叉分歧。
3、java.lang.Object类下的三个方法:void notify()、void notifyAll()、void wait();以及void wait()的两个重载方法void wait(long timeout)、void wait(long timeout, int nanos).利用这三个方法,我们可以模拟出:生产者--仓库--消费者的模型。
4.线程的死锁、
5、线程的合并join()方法(是非静态方法)
6、线程的让步Thread.yield()方法(是静态方法) yield是屈从,放弃的含义。当前运行的线程让出CPU资源,该方法暂停当前正在执行的线程对象,转而去执行其他的线程。
7、守护线程,public final void setDaemon(boolean on),调用线程对象的setDaemon方法可以将其设置为守护线程。
守护线程的作用:守护线程的唯一用途就是告诉JVM不需要等待它退出,当JVM中所有的线程都是守护线程的时候就可以正常的退出了。普通线程不一样,JVM必须等待它的退出才可以正常的退出。
守护线程的理解,“比如你正在用Java写成的编辑器编写Word文档,你一边敲键盘,这是个非守护线程,后台还有一个拼写检查线程,它是个守护线程,他尽量不打扰你写稿子,你们可以同时进行,他发现有拼写错误时在状态条显示错误,但是你可以忽略!!就像城堡门前有个卫兵(守护线程),里面有诸侯(非守护线程),他们是可以同时干着各自的活儿,但是城堡里面的人都搬走了,那么卫兵也就没有存在的意了。
守护线程与普通线程的唯一区别是:当JVM中所有的线程都是守护线程的时候,JVM就可以退出了;如果还有一个或以上的非守护线程则不会退出。(以上是针对正常退出,调用System.exit则必定会退出)所以setDeamon(true)的唯一意义就是告诉JVM不需要等待它退出,让JVM喜欢什么退出就退出吧,不用管它。
8、线程的休眠,Thread.sleep(long millis)和Thread.sleep(long millis, int nanos)谁调用,谁休眠。无法精确地保证线程的执行顺序。
9、线程的同步,(先搞清楚什么是竞争资源?什么时候需要考虑同步?怎么同步?)
synchronized只能标记非抽象的方法,不能标识成员变量。
同步的示例,如:构建了一个信用卡账户,起初信用额为100w,然后模拟透支、存款等多个操作。显然银行账户User对象是个竞争资源,而多个并发操作的是账户方法operation(int x),当然应该在此方法上加上同步,并将账户的余额设为私有变量,禁止直接访问。如果不加synchronized,那么多个线程并发访问了竞争资源u,并对u的属性分别做了改动,造成错误。它的实现是分别对对象加锁,然后释放锁。
最佳实践:将竞争资源设置为private,使用synchronized关键字同步方法或代码。当然这不是唯一控制并发安全的途径。
10、java线程同步块,同步的根本的目的,是控制竞争资源的正确的访问,因此只要在访问竞争资源的时候保证同一时刻只能一个线程访问即可,因此Java引入了同步代码快的策略,以提高性能。
在使用synchronized关键字时候,应该尽可能避免在synchronized方法或synchronized块中使用sleep或者yield方法,因为synchronized程序块占有着对象锁,你休息那么其他的线程只能一边等着你醒来执行完了才能执行。不但严重影响效率,也不合逻辑。同样,在同步程序块内调用yeild方法让出CPU资源也没有意义,因为你占用着锁,其他互斥线程还是无法访问同步程序块。当然与同步程序块无关的线程可以获得更多的执行时间。
11、线程的同步与锁
同步的提出:线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏。
锁的原理:Java中每个对象都有一个内置锁。当程序运行到synchronized同步方法上时,自动获得与正在执行代码类的当前实例(this实例)有关的锁。获得一个对象的锁也称为获取锁、锁定对象、在对象上锁定或在对象上同步。当程序运行到synchronized同步方法或代码块时,该对象锁才起作用。一个对象只有一个锁。所以,如果一个线程获得该锁,就没有其他线程可以获得锁,直到第一个线程释放(或返回)锁。这也意味着任何其他线程都不能进入该对象上的synchronized方法或代码块,直到该锁被释放。
释放锁是指持锁线程退出了synchronized同步方法或代码块。
关于锁和同步,有一下几个要点:1)、只能同步方法,而不能同步变量和类;2)、每个对象只有一个锁;当提到同步时,应该清楚在什么上同步?也就是说,在哪个对象上同步?3)、不必同步类中所有的方法,类可以同时拥有同步和非同步方法。4)、如果两个线程要执行一个类中的synchronized方法,并且两个线程使用相同的实例来调用方法,那么一次只能有一个线程能够执行方法,另一个需要等待,直到锁被释放。也就是说:如果一个线程在对象上获得一个锁,就没有任何其他线程可以进入(该对象的)类中的任何一个同步方法。5)、如果线程拥有同步和非同步方法,则非同步方法可以被多个线程自由访问而不受锁的限制。6)、线程睡眠时,它所持的任何锁都不会释放。7)、线程可以获得多个锁。比如,在一个对象的同步方法里面调用另外一个对象的同步方法,则获取了两个对象的同步锁。8)、同步损害并发性,应该尽可能缩小同步范围。同步不但可以同步整个方法,还可以同步方法中一部分代码块。9)、在使用同步代码块时候,应该指定在哪个对象上同步,也就是说要获取哪个对象的锁。可以这样使用:synchronized (this){函数代码部分}。
静态方法同步,要同步静态方法,需要一个用于整个类对象的锁,这个对象是就是这个类(XXX.class)。其实这个原则,就是同步时候对synchronized (this){函数代码部分}在静态方法下的特殊的使用为:synchronized (XXX.class){函数代码部分}。
如果线程不能获得锁会怎么样?如果线程试图进入同步方法,而其锁已经被占用,则线程在该对象上被阻塞。实质上,线程进入该对象的一种池中,必须在哪里等待,直到其锁被释放,该线程再次变为可运行或运行为止。当考虑阻塞时,一定要注意哪个对象正被用于锁定:1、调用同一个对象中非静态同步方法的线程将彼此阻塞。如果是不同对象,则每个线程有自己的对象的锁,线程间彼此互不干预。2、调用同一个类中的静态同步方法的线程将彼此阻塞,它们都是锁定在相同的Class对象上。3、静态同步方法和非静态同步方法将永远不会彼此阻塞,因为静态方法锁定在Class对象上,非静态方法锁定在该类的对象上。4、对于同步代码块,要看清楚什么对象已经用于锁定(synchronized后面括号的内容)。在同一个对象上进行同步的线程将彼此阻塞,在不同对象上锁定的线程将永远不会彼此阻塞。
什么时候需要同步?在多个线程同时访问互斥(可交换)数据时,应该同步以保护数据,确保两个线程不会同时修改更改它。对于非静态字段中可更改的数据,通常使用非静态方法访问。对于静态字段中可更改的数据,通常使用静态方法访问。如果需要在非静态方法中使用静态字段,或者在静态字段中调用非静态方法,问题将变得非常复杂。
线程安全类,当一个类已经很好的同步以保护它的数据时,这个类就称为“线程安全的”。即使是线程安全类,也应该特别小心,因为操作的线程间仍然不一定安全。举个形象的例子,比如一个集合是线程安全的,有两个线程在操作同一个集合对象,当第一个线程查询集合非空后,删除集合中所有元素的时候。第二个线程也来执行与第一个线程相同的操作,也许在第一个线程查询后,第二个线程也查询出集合非空,但是当第一个执行清除后,第二个再执行删除显然是不对的,因为此时集合已经为空了。出现这种事件的原因是,上例中一个线程操作列表过程中无法阻止另外一个线程对列表的其他操作。解决上面问题的办法是,在操作集合对象上面做一个同步。这样,当一个线程访问其中一个同步方法时,其他线程只有等待。
同步线程小结:1、线程同步的目的是为了保护多个线程反问一个资源时对资源的破坏。2、线程同步方法是通过锁来实现,每个对象都有切仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无法再访问该对象的其他同步方法。3、对于静态同步方法,锁是针对这个类的,锁对象是该类的Class对象。静态和非静态方法的锁互不干预。一个线程获得锁,当在一个同步方法中访问另外对象上的同步方法时,会获取这两个对象锁。4、对于同步,要时刻清醒在哪个对象上同步,这是关键。5、编写线程安全的类,需要时刻注意对多个线程竞争访问资源的逻辑和安全做出正确的判断,对“原子”操作做出分析,并保证原子操作期间别的线程无法访问竞争资源。6、当多个线程等待一个对象锁时,没有获取到锁的线程将发生阻塞。7、死锁是线程间相互等待锁锁造成的,在实际中发生的概率非常的小。真让你写个死锁程序,不一定好使,呵呵。但是,一旦程序发生死锁,程序将死掉。
12、
Java基础复习笔记系列 八 多线程编程的更多相关文章
- Java基础复习笔记系列 九 网络编程
Java基础复习笔记系列之 网络编程 学习资料参考: 1.http://www.icoolxue.com/ 2. 1.网络编程的基础概念. TCP/IP协议:Socket编程:IP地址. 中国和美国之 ...
- Java基础复习笔记系列 四 数组
Java基础复习笔记系列之 数组 1.数组初步介绍? Java中的数组是引用类型,不可以直接分配在栈上.不同于C(在Java中,除了基础数据类型外,所有的类型都是引用类型.) Java中的数组在申明时 ...
- Java基础复习笔记系列 七 IO操作
Java基础复习笔记系列之 IO操作 我们说的出入,都是站在程序的角度来说的.FileInputStream是读入数据.?????? 1.流是什么东西? 这章的理解的关键是:形象思维.一个管道插入了一 ...
- Java基础复习笔记系列 五 常用类
Java基础复习笔记系列之 常用类 1.String类介绍. 首先看类所属的包:java.lang.String类. 再看它的构造方法: 2. String s1 = “hello”: String ...
- Java基础复习笔记系列 三
前几节都是基础中的基础,从第三讲的笔记开始,每次笔记针对Java的一个知识块儿. Java异常处理 1.什么是异常? 异常是指运行期出的错误.比如说:除以一个0:数组越界:读取的文件不存在. 异常处 ...
- Java基础复习笔记系列 二
1.Java中Static的相关用法总结?(静态方法:静态变量:静态代码块) public static void main(String args[])执行的关键,在于有static.有了stati ...
- Java基础复习笔记系列 十三 反射机制
主题:Java反射机制 学习资料参考网址: 1.http://www.icoolxue.com 1.Java反射机制. 各种框架中都使用到了Java的反射机制. 两个类:java.lang.Class ...
- Java基础复习笔记基本排序算法
Java基础复习笔记基本排序算法 1. 排序 排序是一个历来都是很多算法家热衷的领域,到现在还有很多数学家兼计算机专家还在研究.而排序是计算机程序开发中常用的一种操作.为何需要排序呢.我们在所有的系统 ...
- Java基础学习笔记二十一 多线程
多线程介绍 学习多线程之前,我们先要了解几个关于多线程有关的概念.进程:进程指正在运行的程序.确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能. 线 ...
随机推荐
- PHP实现RESTful风格的API实例(二)
接前一篇PHP实现RESTful风格的API实例(一) Response.php :包含一个Request类,即输出类.根据接收到的Content-Type,将Request类返回的数组拼接成对应的格 ...
- mysql-5.7.17-winx64免安装版,win10环境下安装配置
下载地址:http://dev.mysql.com/downloads/file/?id=467269 1.解压到自定义目录:我解压到了D盘的根目录 2.复制my-default.ini 重命名 my ...
- SQLServer数据库备份
使用sql语句备份数据: BACKUP DATABASE 数据库名称 TO DISK = '存储备份文件的路径\备份名称.bak' WITH INIT 使用例子: BACKUP DATABASE Sh ...
- javascript_core_04之数组API
1.数组API——splice: ①删除:var deletes=arr.splice(starti,n):删除starti位置开始的n个,返回删除元素组成的临时数组: ②插入:arr.splice( ...
- JVM快速学习
最近开始了全面的JAVA生态环境学习,因此,JVM的学习是必不可少的一个环节.和.NET的CLR一样,一起的JAVA应用均跑在JVM虚拟机上,不过相对我们只能干看看的CLR,JVM有很大的灵活性,可以 ...
- Index Seek和Index Scan的区别
Index Seek是Sql Server执行查询语句时利用建立的索引进行查找,索引是B树结构,Sql Server先查找索引树的根节点,一级一级向下查找,在查找到相应叶子节点后,取出叶子节点的数据. ...
- 【SQL】姗姗来迟的SQL Server 安装图解
逆天今天被人拉过去装了个数据库...顺便就记录一下安装过程吧,一直想发一直没时间弄,今天就发下安装过程吧,2012和2008安装过程一样的 [注意安装细节] 安装过程中如果出现问题参 ...
- 《BI那点儿事》Microsoft 神经网络算法
Microsoft神经网络是迄今为止最强大.最复杂的算法.要想知道它有多复杂,请看SQL Server联机丛书对该算法的说明:“这个算法通过建立多层感知神经元网络,建立分类和回归挖掘模型.与Micro ...
- FlashFXP(强大的FXP/ftp上传工具)V5.0.0.3722简体中文特别版
flashfxp是功能强大的fxp/ftp软件,融合了一些其他优秀ftp软件的优点,如像cuteftp一样可以比较文件夹, FlashFXP是一款功能强大的FXP/ftp上传工具, FlashFXP集 ...
- 云计算之路-阿里云上:消灭“黑色n秒”第三招——禁用网卡的TCP/IP Offload
程咬金有三板斧,我们有三招.在这篇博文中我们要出第三招,同时也意味着昨天在“希望的田野”上的第二招失败了. 前两招打头(CPU)不凑效,这一招要换一个部位,但依然要坚持攻击敌人最弱(最忙最累)部位的原 ...