《day14---多线程入门_进阶》
2016-04-17 15:42
218 查看
/* 多线程: 进程:正在执行中的程序,一个应用程序启动后在内存中运行的那片空间。进程具有动态性和并发性。 线程:进程中的一个执行单元。负责进程中的程序的运行的。一个进程中至少要有一个线程。 一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。 程序启动了多线程,有什么应用呢? 可以实现多部分程序同时执行。专业术语称之为 并发。 多线程的使用可以合理使用cpu的资源,如果线程过多会导致降低性能。 cpu在处理程序时是通过快速切换完成的。在我们看来好像随机一样。 */
//03-多线程-主线程的运行方式&创建线程的第一种方式。 /* 通过代码来演示之前和之后的区别。 在之前的代码中,jvm启动后,必然有一个执行路径(线程)从main方法开始的, 一直执行到main方法结束。这个线程在java中称之为主线程。 当主线程在这个程序中执行时,如果遇到了循环而导致停留时间过长, 就无法执行下面的程序。 可不可以实现一个主线程负责执行其中一个循环,由另一个线程负责其他代码的执行。 实现多部分的代码同时执行。 这就是多线程技术可以解决的问题。 该如何创建线程呢? 通过API中的英文Thread搜索。查到了Thread类。 通过阅读Thread类中的描述。 创建线程有两种方式。 1,继承Thread类。 1.1 定义一个类去继承Thread. 1.2 重写run方法。 1.3 创建子类对象。就是创建线程对象。 1.4 调用strat方法。开启线程并让线程执行,同时还会告诉jvm调用run方法。 为什么要这么做? 继承Thread类:因为Thread类描述线程事物,具备线程应该有的功能。 那为什么不直接创建Thread类的对象呢? Thread t1 = new Thread(); t.strat();//这么做没有错,但是该strat调用的是Thread类中的run方法, 而这个run方法没有做什么事情,更重要的是这个run方法中并没有定义我们需要让 线程执行的代码。 创建线程的目的是什么?是为了建立单独的执行路径,让多部分代码实现同时执行。 也就是线程创建并执行需要给定代码(线程的任务)。 对于之前所讲的主线程,它的任务定义在了主函数中。 自定义的线程需要执行的任务都定义在run方法中。 Thread类中run方法内部的任务并不是我们所需要的。只要重写这个run方法就行了。 既然Thread类已经定义了线程任务的位置,只要在位置中定义任务代码即可。 所以进行了重写run方法动作。 多线程执行时,在栈内存中,其实每一个执行线程都有一片自己所属的栈内存空间。 进行方法的压栈和弹栈。 当执行线程的任务结束了,线程自动在栈内存中释放了。 当所有的执行线程都结束了,进程就结束了。 //获取线程名称。可以通过Thread类中的一个currentThreadd()方法。怎么获取名称呢? getName(); Thread.currentThread().getName(); 主线程的名称是:main 自定义的线程名称是:Thread-1 线程多个时,数字依次递增。 */ class Demo extends Thread { private String name; Demo(String name) { this.name = name; } public void run() { //int[] arr = new int[3]; //System.out.println(arr[4]); for(int x=1;x<=20;x++) { System.out.println("name="+name+"....."+Thread.currentThread().getName()+".."+x); } } } class ThreadDemo1 { public static void main(String[] args) { //创建了两个线程对象。 Demo d1 = new Demo("小强"); Demo d2 = new Demo("旺财"); d2.start();//将d2这个线程开启。 d1.run();//由主线程负责。 /* 线程对象调用run方法和调用start方法的区别? 调用run方法不开启线程,仅是对象调用方法。 调用start开启线程,并让jvm调用run方法在开启的线程中执行。 */ } }
//创建线程的第二种方式。 /* 创建线程的第二种方式:使用Runnable接口。 1,定义类实现Runnable接口。//避免了单继承的局限性。 2,覆盖接口中的run方法。将线程任务代码定义到run方法中。 3,创建Thread类的对象。 //只有创建Thread类的对象才可以创建线程。 4,将Runnable接口的子类对象作为参数传递给Thread类的构造函数。 因为线程任务已被封装到Runnable接口的run方法中,而这个run方法所属于Runnable接口的子类对象。 所以将这个子类对象作为参数传递给Thread的构造函数,这样,线程对象创建时就可以明确要运行的任务。 5,调用Thread类的strat方法开启线程。 第二种方式实现Runnable接口避免了单继承的局限性。所以,较为常用。 实现Runnable接口的方式,更加的符合面向对象,线程分为两部分,一部分线程对象,一部分线程任务。 继承Thread类,线程对象和线程任务耦合在一起,一旦创建Thread类的子类对象,既是线程对象,又有线程任务。 实现Runnable接口,将线程任务单独分离出来封装成对象。类型就是Runnable接口类型。 Runnable接口对线程对象和线程任务解耦。 //通过源代码的形式讲解了一些Runnable接口的子类对象作为参数传递给Thread构造函数的原因。 class Thread{ private Runnable target; Thread(Runnable traget) { this.target = target; } public void run() { if (target != null) { target.run(); } } public void strat() { run(); } } Demo d = new Demo();//Runnable d = new Demo(); Thread t = new Thread(d); t.strat(); */ class Demo implements Runnable { private String name; Demo(String name) { this.name = name; } //覆盖了接口Runnable的run方法。 public void run() { for(int x=1;x<20;x++) { System.out.println("name="+name+"....."+Thread.currentThread().getName()+".."+x); } } } class ThreadDemo2 { public static void main(String[] args) { //创建Runnable子类的对象。注意它并不是线程对象。 Demo d = new Demo("Demo"); //创建Thread类的对象,将Runnable接口的子类对象作为参数传递给Thread类的构造函数。 Thread t1 = new Thread(d); Thread t2 = new Thread(d); t1.start(); t2.start(); System.out.println(Thread.currentThread().getName()+"------>"); } }
//多线程的安全问题产生及原因以及解决思路 /* 案例:售票的例子。 售票的动作需要同时执行。所以要使用多线程技术。 发生了线程安全问题,出现了错误的数据;0 -1 -2 问题产生的原因: 1,线程任务在操作共享的数据。 2,线程任务操作共享数据的代码有多条(运算有多个)。 解决思路: 只要让一个线程在执行线程任务时,将多条操作共享数据的代码执行完, 在执行过程中,不要让其他线程参与运算就行了。 代码体现: Java中解决此问题通过代码块来完成的, 这个代码代码块称之为同步代码块 synchronized 格式: synchronized(对象) { //需要被同步的代码。 } 同步好处:同步代码块解决了多线程安全问题。 同步的弊端: 降低了程序的性能。 同步前提: 必须保证多个线程在同步中使用的是同一个锁 解决了什么问题?当多线程安全问题发生时,加入了同步后,问题依旧发生时, 就要通过这个同步的前提来判断同步是否正确。 */ class Ticket implements Runnable { //1,描述票的数量 private int tickets = 100; //2,售票的动作。这个动作需要被多线程执行,那就是线程任务代码, //需要定义在run方法中。 //记住,线程任务中通常都有循环结构。 private Object obj = new Object(); public void run() { while(true) { synchronized(obj)//如果写 new Object() 则用的就不是同一个锁。 { if(tickets > 0) { //要让线程在这里稍停,模拟问题的发生。sleep 看到了 0 -1 -2 这样的错误的数据,这就是传说中的安全问题。 try{Thread.sleep(1);}catch(InterruptedException e){/*未写处理方式,后面讲*/} System.out.println(Thread.currentThread().getName()+"...."+tickets--);//打印线程名称 } } } } } class ThreadDemo3 { public static void main(String[] args) { //1,创建Runnable接口的子类对象。 Ticket t = new Ticket(); //创建四个线程对象。并将Runnable接口的子类对象作为参数传递给Thread的构造函数。 Thread t1 = new Thread(t); Thread t2 = new Thread(t); Thread t3 = new Thread(t); Thread t4 = new Thread(t); //3,开启四个线程 t1.start(); t2.start(); t3.start(); t4.start(); } }
相关文章推荐
- 【leetcode】121. Best Time to Buy and Sell Stock
- 揭秘在ListView等AdapterView上动态添加删除项的陷阱
- NJUST 1925 sequence
- PHP常见注入方式
- DNS原理及其解析过程【精彩剖析】
- Hadoop学习笔记(5)-搭建Hadoop分布式
- 第12章--Java集合之Map接口
- 延时NSTimer
- 初试Visual Studio Code配置Python开发环境
- 详解struct、class中成员变量的字节对齐
- 什么是软件需求?什么是功能需求?
- Android 在其他线程中更新UI线程的解决方法
- A 偷吃糖果 南京理工大学第八届程序设计大赛(校外镜像)
- 第七周学习进度
- 查看进程数
- swift2.0 异步post请求
- 堆优化 Dijstra单源最短路径算法 2(邻接表)
- ffmpeg应用基础-2
- 第八周项目一(3) 数组作数据成员(多文件)
- 如何去调用res中的资源