JAVA多线程
第一节 多线程背景知识介绍
多线程作用:可以帮助简化模型,还能编写强大的代码。进程与线程的基础概念:Eclipse QQ都属于进程。进程是程序(任务)的执行过程,它持有资源(共享内存、共享文件)和线程。它包含两点:1.动态性,双击运行之后这才称之为进程,它是动态的概念;2.持有资源和线程,资源就是内存和文件。进程是资源的载体,也是线程的载体。脱离进程去谈论线程就没有什么意义了。资源就是我们所说的内存,我们知道程序是需要从内存中读取数据进行运行的,所以每个进程执行的时候都会被分配一个内存。那么线程是是什么呢?当你运行Eclipce你要进行一些任务,比如你要在文本编辑区编写源代码,我们的Eclipce会自动在后台编译我们的源代码,有的代码还有语法效验,再看QQ聊天,发文件,所有这些任务我们都可以把他们理解成线程。如果把进程比作一个班级,可以把每个学生比作一个线程,学生是班级中的最小单元。一个班级可以有多个学生,他们都使用班级中共同的板凳、桌椅、黑板、粉笔等来进行他们的学习生活。这个意义上我们说线程是系统中最小的执行单元,同一个进程中可以有多个线程,线程共享进程的资源。
线程的交互:如一个班级中的学生一样,我们说多个线程需要通信才能正确的工作,这种通信我们把它称之为线程的交互。交互的方式包括互斥和同步。将概念类比到班级中,我们的同学之间需要通过相互的协会来完成某些任务,有时这些协作是需要竞争的。比如学习,班级的公共资源是有限的,爱学习的同学需要抢占,需要竞争,一个同学用完了下一个同学才能使用,如果一个同学正在使用,那么其他新来的同学只能等待。另一方面我们又需要同步或者协作,好比班级六一需要排演一个节目,全班同学齐心协力,相互配合,才能把节目完美的呈现在学校面前,这些就是线程的交互。
第二节 JAVA线程初体验
JAVA线程中常用方法介绍:1.java对线程的支持;2.如何用java创建并启动线程;3.线程常用的方法和那些含义;4.如何停止线程。
java语言对线程的支持,主要体现在class Thread 类里以及interface Runnable这个接口上,它们都寄生于java.lang包,无论是Thread这个类还是Runnable这个interface,里面都有一个共通的方法,就是我们的,public void run(),run方法为我们提供了线程实际工作执行的代码,一般都会复写。
下面我们看看线程中常用的方法:
Thread常用方法
a.线程的创建
Thread()
Thread(String name)
Thread(Runnable target)
Thread(Runnable target,String name)
这四个是常用的线程构造函数
b.线程的方法
void start() 启动线程
static void sleep(long millis)
static void sleep(long millis,int nanos)
可以让线程休眠指定的时间,millis毫秒,nanos纳秒
void join()
void join(long millis)
void join(long millis,int nanos)
调用线程,可以让其他线程等待自己运行,直到结束。
这里第一个方法没有参数,其它的线程一定要等待我们的执行线程执行完毕之后才会获得运行的机会,带参数的则指明了一个时间阈值,最长需要等待的时间,这个时间单位是毫秒,我们可以通过第二个参数将时间精确到纳秒
static void yield()
当前正在运行的线程释放处理器资源并重新去竞争处理器资源
static Thread currentThread()
返回当前正在处理器上运行的线程的引用
第三节 JAVA线程-隋唐演义框架说明
1.设计一个隋唐演义程序来模拟线程的执行,首先是隋唐之间的农民战争,一定会有战争的军队,用对象来分别模拟我们的隋朝军队和农民起义军。因为双方军队的战争行为都是各自实行的,所以我们用Runnable对象可以非常轻而易举的隔离双方的差异,使其各自为战,以便于我们抽象实现。
//军队线程
//模拟作战双方的行为
public class ArmyRunnable implements Runnable{
ArmyRunnable armyTaskOfSuiDynasty=new ArmyRunnable();
ArmyRunnable armyTaskOfRevolt=new ArmyRunnable();
//army 陆军 军队;Task 任务 工作;Of 属于;Dynasty 王朝 Revolt 反叛
//完成上面的任务等于是建立了两个军队的对象,然后创建线程
public void run(){}
//复写run方法
}
//使用Runnable接口创建线程
Thread armyOfSuiDynasty=new Thread(armyTaskOfSuiDynasty,"隋军");
Thread armyOfRevolt=new Thread(armyTaskOfRevolt,"农民军");
2.英雄人物可以推动历史发展,关键人物的线程来模拟英雄人物
public class KeyPersonThread extends Thread{
public void run(){
//复写run方法
//输出某军名字+开始战斗 可以用Thread.currentThread().getName
}
}
3.需要有一个舞台线程,把所有元素融合进来 class Stage extends Thread
第四节 隋唐演义实战开发I
这里我们首先先实验下Thread类和Runnable类的使用,假如我们的演员是两个人,Mr.Thread 和Ms.Runnable,Thread先生和Runnable小姐,让他们交替上台表演。后面贴代码
这里先把学习的经验总结一下:
每一个java文件中只能有一个public类,直接在当前类的末尾新建其它类也可以,但是不能写public,这里所说的不是内部类是独立的外部类,当然直接新建一个java文件单独建类或者直接class 类名都可以。
两种方法实现线程:
1、继承 Thread 类
class MyThread extends Thread{};
Thread myThread = new MyThread();
myThread.start();
2、实现Runnable接口
class MyRunnable implements Runnable{}
Thread myRunnable = new Thread(new MyRunnable);
myRunnable.start();
3、Thread启动后执行run()方法
4、若实现接口通过Thread.currentThread().getName()方法获取当前线程名称,继承Thread则getName()方法获取当前线程。
Thread类的方法:
.getName() 返回线程的名字
.setName(String Name) 改变线程的名字
.start() 使该线程开始执行,Java 虚拟机调用该线程的 run 方法。
.sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。线程不丢失任何监视器的所属权。
.currentThreat() 返回对当前正在执行的线程对象的引用。(静态方法,返回Thread类型)
package actor;
/**
* Created by Administrator on 2017/4/6.
*/
//创建一个类继承自Thread类。
public class Actor extends Thread {
//在我们的类中复写我们的run()方法
public void run(){
//这里调用getName()方法来获得我们当前线程的名称
System.out.println(getName()+"是一个演员!");
//给出一个计数器来显示线程先生出场演出了多少次
int count=0;
boolean keepRunning=true;
while(keepRunning){
System.out.println(getName()+"登台演出了:"+(++count));
if(count==100){
keepRunning=false;
}
if(count%10==0){
try {
//每演出10场后,停顿休眠1S
Thread.sleep(1000);
//millis是毫秒,1S=1000millis
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//这里使用count++和++count输出的结果不同,后者是1,前者是0
System.out.println(getName()+"的演出结束了!");
}
public static void main(String[] args) {
Thread actor=new Actor();
//这里我们可以看到 父类的引用指向了子类的实例,多态
//换成Actor程序一样可以运行
actor.setName("Mr.Thread");
//调用setName()方法给线程设置名字
actor.start();
//调用.start方法开始线程
Thread actressThread=new Thread(new Actress(),"Ms.Runnable");
//我们有了第二个类,可以创建一个新的对象,调用Thread类的构造函数
// 通过接收我们实现了Runnable接口的对象来创建线程,或者也可以调用
//setName也没有问题
actressThread.start();
}
}
//在actor类后新建一个类,注意在我们的.java文件中,可以有多个类,但是只能
//有一个public类这里所说的不是内部类,而都是一个独立的外部类。
//或者单独新建一个类也可以,这都没有任何问题
class Actress implements Runnable{
@Override
public void run() {
//这里复制上面的方法调用getName(),但由于Runnable中
// 没有getName方法,所以我们要用Thread.currentThread()
// 来获取当前线程的引用,来获得我们当前线程的名称
System.out.println(Thread.currentThread().getName()+"是一个演员!");
//给出一个计数器来显示线程先生出场演出了多少次
int count=0;
boolean keepRunning=true;
while(keepRunning){
System.out.println(Thread.currentThread().getName()+"登台演出了:"
+(++count));
if(count==100){
keepRunning=false;
}
if(count%10==0){
try {
//每演出10场后,停顿休眠1S
Thread.sleep(1000);
//millis是毫秒,1S=1000millis
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//这里使用count++和++count输出的结果不同,后者是1,前者是0
System.out.println(Thread.currentThread().getName()+"的演出结束了!");
}
}
//作为计算机处理器CPU或者同一个核同时只能运行一条线程
//当我们的一条线程休眠之后,另外一条线程才获得了我们的处理器时间
//这就是我们两个演员交替演出的情况
第五节 隋唐演义实战开发II
package threadInImooc;
/**
* Created by Administrator on 2017/4/6.
* 军队任务线程,模拟作战双方的行为
*/
public class ArmyRunnable implements Runnable {
volatile boolean keepRunning=true;
//volatile 是java的一个关键字,必须将keepRunnings声明设置为volatile类型
//保证了线程可以正确读入其他线程写入的值。
//军队必须在得到命令之前持续的保持进攻的态势,那么是谁来下达关键的命令,
//英雄人物也好,舞台策划也好是有外部的一个线程来告诉我们军队的线程你们应该
//停止攻击了,我们应该转入其他环节,这个时候我们就写入keepRunning的值
//如果不声明由于可见性的问题,我们的当前线程有可能不能正确的读到这个值。
//关于可见性我们可以参考我们的, ref JMM java内存模型,里面详细的讲述了
//什么是happens-before原则,以及为什么这个关键词解决了这个可见性问题
//军队线程,模拟作战双方的行为
@Override
public void run() {
//当填入keepRunning之后,按住Alt+Enter(eclipce应该是ctrl+1)
//选择create field keepRunning,keepRunning作为一个开关,
//没有收到停止时就继续作战
while(keepRunning){
//每次攻击都会发送五连击
for(int i=0;i<5;i++){
System.out.println(Thread.currentThread().getName()+
"进攻对方"+"["+i+"]");
//提供当前进攻批次的信息
//我们通过一些技术手段可以使他们更公平的竞争使得对手还有还手的机会
Thread.yield();//释放当前线程的处理器资源,并重新去竞争处理器资源
//让出了处理器时间下次谁进攻还不一定了
}
}
System.out.println(Thread.currentThread().getName()+"结束战斗了!");
}
}
package threadInImooc;
/**
* Created by Administrator on 2017/4/6.
*隋唐演义的大戏舞台
*/
public class Stage extends Thread {
public void run(){
//利用军队线程引入我们的军队
ArmyRunnable armyTaskOfSuiDynast=new ArmyRunnable();
//隋军作战任务对象
ArmyRunnable armyTaskOfRevolt=new ArmyRunnable();
//农民起义军作战任务对象
Thread armyOfSuiDynast=new Thread(armyTaskOfSuiDynast,"隋军");
//创建军队线程隋军
Thread armyOfRevolt=new Thread(armyTaskOfRevolt,"农民起义军");
//创建军队线程起义军
//启动线程,调用两个线程的start方法,让军队开始作战。
armyOfRevolt.start();
armyOfSuiDynast.start();
//大家知道战争细节宏大我们观众不希望被其他细节所打扰,所以让舞台线程
//暂时休眠。
//舞台线程休眠50毫秒,大家专心看军队厮杀
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
armyTaskOfSuiDynast.keepRunning=false;
armyTaskOfRevolt.keepRunning=false;
try {
armyOfRevolt.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new Stage().start();
}
}
第六节 隋唐演义实战开发 III
军队线程:
package threadInImooc;
/**
* Created by Administrator on 2017/4/6.
* 军队任务线程,模拟作战双方的行为
*/
public class ArmyRunnable implements Runnable {
volatile boolean keepRunning=true;
//volatile 是java的一个关键字,必须将keepRunnings声明设置为volatile类型
//保证了线程可以正确读入其他线程写入的值。
//军队必须在得到命令之前持续的保持进攻的态势,那么是谁来下达关键的命令,
//英雄人物也好,舞台策划也好是有外部的一个线程来告诉我们军队的线程你们应该
//停止攻击了,我们应该转入其他环节,这个时候我们就写入keepRunning的值
//如果不声明由于可见性的问题,我们的当前线程有可能不能正确的读到这个值。
//关于可见性我们可以参考我们的, ref JMM java内存模型,里面详细的讲述了
//什么是happens-before原则,以及为什么这个关键词解决了这个可见性问题
//军队线程,模拟作战双方的行为
@Override
public void run() {
//当填入keepRunning之后,按住Alt+Enter(eclipce应该是ctrl+1)
//选择create field keepRunning,keepRunning作为一个开关,
//没有收到停止时就继续作战
while(keepRunning){
//每次攻击都会发送五连击
for(int i=0;i<5;i++){
System.out.println(Thread.currentThread().getName()+
"进攻对方"+"["+i+"]");
//提供当前进攻批次的信息
//我们通过一些技术手段可以使他们更公平的竞争使得对手还有还手的机会
Thread.yield();//释放当前线程的处理器资源,并重新去竞争处理器资源
//让出了处理器时间下次谁进攻还不一定了
}
}
System.out.println(Thread.currentThread().getName()+"结束战斗了!");
}
}
关键人物线程
/**
* Created by Administrator on 2017/4/6.
*/
public class KeyPersonThread extends Thread {
//定义了一个关键人物类,复写run方法。
public void run(){
System.out.println(Thread.currentThread().getName()+"开始了战斗!");
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+
"左突右杀,攻击隋军...");
}
System.out.println(Thread.currentThread().getName()+"结束了战斗!");
}
}
舞台线程
/**
* Created by Administrator on 2017/4/6.
*隋唐演义的大戏舞台
*/
public class Stage extends Thread {
public void run(){
System.out.println("欢迎观看隋唐演义");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("大幕徐徐拉开");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("话说隋朝末年,隋军与农民起义军杀得昏天黑地...");
//利用军队线程引入我们的军队
ArmyRunnable armyTaskOfSuiDynast=new ArmyRunnable();
//隋军作战任务对象
ArmyRunnable armyTaskOfRevolt=new ArmyRunnable();
//农民起义军作战任务对象
Thread armyOfSuiDynast=new Thread(armyTaskOfSuiDynast,"隋军");
//创建军队线程隋军
Thread armyOfRevolt=new Thread(armyTaskOfRevolt,"农民起义军");
//创建军队线程起义军
//启动线程,调用两个线程的start方法,让军队开始作战。
armyOfRevolt.start();
armyOfSuiDynast.start();
//大家知道战争细节宏大我们观众不希望被其他细节所打扰,所以让舞台线程
//暂时休眠。
//舞台线程休眠50毫秒,大家专心看军队厮杀
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("正当双方激战正酣,半路杀出个程咬金");
Thread mrCheng=new KeyPersonThread();
mrCheng.setName("程咬金");
System.out.println("程咬金的理想就是结束战争,使百姓安居乐业!");
//此时要开始这个线程,同时为了将关键人物的作用体现出来,先将另外两边
//的军队停止线程,也就是while循环停止,给true变false
armyTaskOfSuiDynast.keepRunning=false;
armyTaskOfRevolt.keepRunning=false;
//隋军和农民起义军线程停止,停止线程的方法
//下达命令之后我们要让我们的舞台剧休眠一会,这样可以让我们的军队
//确实执行停战命令
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
/**
* 历史大戏留给关键线程。
*/
mrCheng.start();
//在万众瞩目之下,所有的线程都应该等待我们的程先生,所以要调用
//join方法,所有线程等待程先生完成历史使命
try {
mrCheng.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
//假如把join方法注释掉,就会出现舞台线程的后两句穿插输出了,之后
//才执行我们关键先生的线程。join在程序的结束非常有用
System.out.println("战争结束,人民安居乐业,程先生实现了积极的人生理想" +
",为人民做出了贡献!");
System.out.println("谢谢观看隋唐演义,再见!");
}
public static void main(String[] args) {
new Stage().start();
}
}