logo资料库

李兴华java8编程讲义.docx

第1页 / 共112页
第2页 / 共112页
第3页 / 共112页
第4页 / 共112页
第5页 / 共112页
第6页 / 共112页
第7页 / 共112页
第8页 / 共112页
资料共112页,剩余部分请下载后查看
Java多线程实现
线程与进程
Thread类实现
Runnable接口实现
两种实现方式的区别(面试题)
Callable接口(理解)
线程常用操作方法
命名和取得
线程的休眠
线程优先级
线程的同步与死锁(了解)
同步问题引出
同步操作
死锁
“生产者-消费者”模型-多线程操作经典案例
问题引出
解决数据错乱问题-同步处理
解决重复问题-Object类支持
Java基础类库
StringBuffer类
Runtime类(了解)
System类(了解)
对象克隆(了解)
数字操作类
Math类
Random类
BigInteger大整数操作类
BigDecimal大浮点数操作类(重要)
日期操作类
Date类
SimpleDateFormat(核心)
Calendar类
比较器
Arrays类(了解)
Comparable接口(核心)
二叉树实现(了解)
Comparator接口
正则表达式
正则引出
正则标记(背)
String类对正则的支持(重点)
java.util.regex包支持(理解)
反射机制
认识反射
实例化Class类对象
反射实例化对象
调用构造方法
调用普通方法
调用成员
国际化
国际化程序实现
文件操作
基本操作
File类操作方法
操作目录
方法的定义与使用
基本概念
方法重载(重要)
递归调用(了解)
字节流与字符流
简介
输出流:OutputStream
打印流
问题引出
类集框架
类集简介(了解)
Collection接口(重点)
List子接口
Set子接口
集合输出
Map接口
Stack子类
Properties子类
Collections工具类
Stream(JDK1.8)
Java数据库编程(JDBC)
JDBC简介
连接Oracle数据库
Statement接口
PreparedStatement接口
批处理与事物处理
DAO设计模式—设计分层初步
分层设计思想
业务设计实例
DAO设计模式—开发准备
数据库连接类
开发ValueObject类
DAO设计模式—数据层开发
数据层接口
数据层类实现
数据层工厂类
DAO设计模式--业务层开发
业务层接口
业务层实现类
业务层工厂类
Java 多线程实现 线程与进程 Java 是一门为数不多的支持多线程编程的语言。 进程?在操作系统的定义中,进程指的是一次程序的完整运行,这个运行的过程之中内存、处理 器、IO 等资源操作都要为这个进程服务。 在最早的 dos 系统的时代,有一个特点:如果你电脑病毒发作了,那么你电脑几乎就不能动 了。因为所有的资源都被病毒软件所占用,其他的程序无法抢占资源。但是到了后来 Windows 时 代,这一情况发生了改变,电脑即使有病毒,电脑也可以运行(就是慢点)。 Windows 属于多进程的操作系统。但是有一个问题出现了:每一个进程都需要有资源的支持, 那么多个进程怎么去分配资源呢? 在同一个时间段上,会有多个进程轮流去抢占资源。但是在某一个时间点上只能有一个进程。 线程?线程是在进程的基础上进一步划分的结果,即:一个进程上可以同时创建多个线程。 线程是比进程更快的处理单元,而且所占的资源也小。那么多线程的应用也是性能最高的应 用。 总结:线程的存在离不开进程。进程如果消失后,线程一定会消失。反之线程消失了进程未 必会消失。 Thread 类实现 掌握 java 中 3 种多线程的实现方式(JDK1.5 之后增加了第 3 种)。 如果想在 java 中实现多线程,有两种途径: 1. 继承 Thread 类; 2. 实现 Runable 接口(Callable 接口)。 Thread 类是一个支持多线程的功能类,只要有一个类继承了 Thread 那么它就是一个多线程的类。 Class MyThread extends Thread{} 所有程序的起点是 main()方法。但是所有线程也有自己的起点,即:run()方法。在多 线程的每个主体类之中都必须覆写 Thread 类中所提供的 run()方法。 public void run(){} class MyThread extends Thread{ 这个方法没有返回值,那么也就表示线程一旦开始那么就要一直执行,不能够返回内容。 this.name=name; private String name;//定义属性 public MyThread(String name){//定义构造方法 } @Override public void run() {//覆写run方法,作为线程的主体方法 System.out.println(this.name+"-------"+x); for(int x=0;x<200;x++){ } } } 本线程类的功能是进行循环的输出操作。所有的线程跟进程一样的,都必须轮流抢占资源。 1
所以多线程的执行应该是多个线程彼此交替执行,也就是说如果直接调用了 run()方法,那么 就不能够启动多线程,多线程启动的唯一方法就是 Thread 类中的 start()方法:public void start(), 调用此方法执行的方法体是 run()方法定义的。 package cn.mldn.util; class MyThread extends Thread{ this.name=name; private String name;//定义属性 public MyThread(String name){//定义构造方法 } @Override public void run() {//覆写run方法,作为线程的主体方法 System.out.println(this.name+"-------"+x); for(int x=0;x<100;x++){ } } } public class TestDemo{ public static void main(String args[]){ MyThread mt1=new MyThread("线程A"); MyThread mt2=new MyThread("线程B"); MyThread mt3=new MyThread("线程C"); mt1.start(); mt2.start(); mt3.start(); } } 此时每一个线程对象交替执行。 疑问?为什么多线程启动不是调用 run(),而必须调用 start()? public synchronized void start() { if (threadStatus != 0) throw new IllegalThreadStateException(); group.add(this); boolean started = false; try { start0(); started = true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { } } } 2
首先在 Thread 类型的 start()方法里面存在有一个“IllegalThreadStateException”异常抛出。本 方法里面使用 throw 抛出异常,按照道理来讲,应该使用 try-catch 处理,或者在 start()方法声明 上使用 throws 声明,但是此处并没有这样的代码,因为此异常属于 RuntimeException 的子类,属 于选择性处理。如果某一个线程对象重复进行了启动,就会抛出此异常。 发现在 start()方法里面要调用一个 start0()方法即 private native void start0();,而且此方法的结 构与抽象方法类似,唯一不同的是使用了 native 声明。在 java 的开发里面有一门技术称为 JNI(Java Native Interface)技术,是使用 java 调用本机操作系统提供的函数,但是这样的技术有一个缺点, 不能离开特定的操作系统。 如果要想线程能够执行,需要操作系统来进行资源分配,所以此操作严格来讲主要是由 JVM 负责根据不同的操作系统而实现的。 Thread 的 start()方法不仅仅要启动多线程的执行代码,还要去根据不同的操作系统进行资源的 分配。 Runnable 接口实现 虽然 Thread 类可以实现多线程的主体类定义,但是它有一个问题,java 具有单继承局限,正因为 如此,在任何情况下针对于类的继承都应该是回避的问题,那么多线程也一样,为了解决单继承 的限制,在 java 里面专门提供了 Runable 接口: @FunctionalInterface public interface Runnable{//函数式接口,特征是一个接口只能有一个方法 public void run(); } 在接口里面任何的方法都是 public 定义的权限,不存在默认的权限。 那么只需要让一个类实现 Runnable 接口,并且也需要覆写 run()方法。 与继承 Thread 类相比,此时的 MyThread 类在结构上是没有区别的,但是有一点是有严重区 别的:如果此时继承了 Thread 类,那么可以直接继承 start()方法;但如果实现的是 Runnable 接口,并没有 start()方法可以被继承。 class MyThread implements Runnable{ this.name=name; private String name;//定义属性 public MyThread(String name){//定义构造方法 } @Override public void run() {//覆写run方法,作为线程的主体方法 System.out.println(this.name+"-------"+x); for(int x=0;x<100;x++){ } } } 不管何种情况下,如果要想启动多线程,一定依靠 Thread 类完成,在 Thread 类里定义有如 下的构造方法: public Thread(Runnable target),接收的是 Runnable 接口对象; 范例: package cn.mldn.util; class MyThread implements Runnable{ private String name;//定义属性 3
this.name=name; public MyThread(String name){//定义构造方法 } @Override public void run() {//覆写run方法,作为线程的主体方法 System.out.println(this.name+"-------"+x); for(int x=0;x<200;x++){ } } } public class TestDemo{ public static void main(String args[]){ MyThread mt1=new MyThread("线程A"); MyThread mt2=new MyThread("线程B"); MyThread mt3=new MyThread("线程C"); Thread m1=new Thread(mt1); Thread m2=new Thread(mt2); Thread m3=new Thread(mt3); m1.start(); m2.start(); m3.start(); } } 此时就避免了单继承局限,那么也就是说在实际工作中使用接口是最合适的。 两种实现方式的区别(面试题) 通过讲解已经清楚多线程两种实现方式,这两种方式有哪些区别? 首先我们要明确的是使用 Runnable 接口与 Thread 相比,解决了单继承局限,所以不管后面 的区别与联系,至少这一点就已经下了死定义。如果要使用,一定使用 Runnable 接口。 观察 Thread 类定义: public class Thread extends Object implements Runnable 发现 Thread 类实现了 Runnable 接口。 4
此时整个的定义结构非常像代理设计模式,如果是代理设计模式,客户端调用的代理类的方 法应该是接口里提供的方法,那么也应该是 run()才对。 除了以上的联系之外,还有一点:使用 Runnable 接口可以比 Thread 类更好的描述出数据共 享这一概念。此时的数据共享指的是多个线程访问同一资源的操作。 范例:观察代码 Thread 实现(每一个线程对象都必须通过 start()启动) package cn.mldn.util; class MyThread extends Thread{ private int ticket=10;//定义属性 @Override public void run() {//覆写run方法,作为线程的主体方法 for(int x=0;x<100;x++){ if(this.ticket>0){ System.out.println("卖票,ticket="+this.ticket--); } } } } public class TestDemo{ 可以直接启动 public static void main(String args[]){ //由于Mythread类有start()方法,所以每一个MyThread类就是一个线程对象, MyThread mt1=new MyThread(); MyThread mt2=new MyThread(); MyThread mt3=new MyThread(); mt1.start(); mt2.start(); mt3.start(); } 本程序声明了 3 个 MyThread 类对象,并且分别调用了 3 次 start()方法启动线程对象。结 } 5
果是每一个线程对象都在卖各自的票。此时并不存在数据共享。 范例:利用 Runnable 实现 package cn.mldn.util; class MyThread implements Runnable{ private int ticket=10;//定义属性 @Override public void run() {//覆写run方法,作为线程的主体方法 for(int x=0;x<100;x++){ if(this.ticket>0){ System.out.println("卖票,ticket="+this.ticket--); } } } } public class TestDemo{ public static void main(String args[]){ MyThread mt1=new MyThread(); new Thread(mt1).start(); new Thread(mt1).start(); new Thread(mt1).start(); } } 此时也属于 3 个线程对象,唯一的区别是这 3 个线程对象都占用了同一个 MyThread 类的对 象引用,也就是这 3 个线程对象都直接访问同一个数据资源。 6
面试题:请解释 Thread 类与 Runnable 接口实现多线程的区别?(请解释多线程两种实现方式的 区别?) Thread 类是 Runnable 接口的子类,使用 Runnable 接口实现可以避免单继承局限; Runnable 接口实现的多线程可以比 Thread 类实现的多线程更加清楚的描述数据共享的概念。 请写出多线程两种实现操作? 把 Thread 类继承的方式和 Runnable 接口实现的方式都写出来。 Callable 接口(理解) 使用 Runnable 接口实现的多线程可以避免单继承局限,但是有一个问题,Runnable 接口里面 的 run() 方 法 不 能 返 回 操 作 结 果 。 为 了 解 决 这 个 矛 盾 , 提 供 了 一 个 新 的 接 口 : java.util.concurrent.Callable 接口。 public Interface Callable{ public V call()throws Exception; } Call()方法完成线程的主体功能后可以返回一个结果,而这个结果的类型由 Callable 接口的泛型 决定。 范例:定义一个线程主体类 import java.util.concurrent.Callable; class MyThread implements Callable{ private int ticket=10;//定义属性 @Override public String call() throws Exception{ for(int x=0;x<100;x++){ if(this.ticket>0){ System.out.println("卖票,ticket="+this.ticket--); } } return "票已卖光!!!"; 7
} } 此时观察 Thread 类里面并没有直接支持 Callable 接口的多线程应用。 从 JDK1.5 开始提供有 java.util.concurrent.FutureTask类。这个类主要是负责 Callable 接口对 象的操作的,这个接口的定义结构: public class FutureTask extends Object implements RunnableFuture public interface RunnableFuture extends Runnable, Future FutureTask 有如下的构造方法:FutureTask(Callable callable)。 接收的目的只有一个,那么就是去的 call()方法的返回结果。 package cn.mldn.util; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; class MyThread implements Callable{ private int ticket=10;//定义属性 @Override public String call() throws Exception{ for(int x=0;x<100;x++){ if(this.ticket>0){ System.out.println("卖票,ticket="+this.ticket--); } } return "票已卖光!!!"; } } public class TestDemo{ public static void main(String args[]) throws Exception{ MyThread mt1=new MyThread(); MyThread mt2=new MyThread(); FutureTask fu1=new FutureTask(mt1);//目的是取得 FutureTask fu2=new FutureTask(mt2);//目的是取得 new Thread(fu1).start();//启动多线程 new Thread(fu2).start();//启动多线程 //多线程执行完毕后可以取得内容,依靠FutureTask的父接口Future中的get() call()的返回结果 call()的返回结果 方法完成 System.out.println("A线程的返回结果"+fu1.get()); System.out.println("B线程的返回结果"+fu2.get()); } 第三种方法最麻烦的在于需要接收返回值信息,并且又要与原始的多线程的实现靠拢(向 } Thread 类靠拢)。 总结: 1. 对于多线程的实现重点在于 Runnable 接口与 Thread 类启动的配合上。 2. 对于 JDK1.5 的新特性了解就行,知道区别在于返回结果上。 8
分享到:
收藏