奋斗黑马程序员----Java之多线程讲解(一)

来源:互联网 发布:php html 表单 编辑:程序博客网 时间:2024/05/16 14:31
----------android培训java培训、期待与您交流! ----------
 
/**java----多线程小结 *  *//**1,相关概念 *  *   进程:运行中的应用程序称为进程,拥有系统资源(cpu、内存)。每一个进程 * 执行都有一个执行路径,该顺序是一个执行路径,或者叫一个控制单元 *  *   线程:进程中的一段代码,一个进程中可以哦有多段代码。本身不拥有资源 *(共享所在进程的资源),线程就是进程中的一个独立的控制单元。线程在控制着 *进程的执行。一个进程中至少有一个线程。 * *   如迅雷的多线程下载,一个资源分成几个部分,向服务端请求同时下载,可加快速度。 *   线程是进程中的内容,每一个应用程序里面至少都有一个线程,因为线程是程序中的 * 控制单元,或者叫执行路径 *  *   java VM 启动的时候会有一个进程java.exe,该进程中至少有一个线程负责java程序 * 的执行,而且这个线程运行的代码存在于main方法中,该线程(控制java运行的线程) * 称之为:主线程。 *  * 在java中,程序入口被自动创建为主线程,在主线程中可以创建多个子线程。 * 区别: *  1、是否占有资源问题 *  2、创建或撤销一个进程所需要的开销比创建或撤销一个线程所需要的开销大。 *  3、进程为重量级组件,线程为轻量级组件 *   *  多进程: 在操作系统中能同时运行多个任务(程序) *  多线程: 在同一应用程序中有多个功能流同时执行 *   *  多线程的优点: *   1,可以减轻系统性能方面的瓶颈,因为可以并行操作; *   2,提高程序运行的效率,内存利用率 *   3,在多CPU系统中,可以把不同的线程在不同的CPU中执行,真正做到同时处理多任务。 *  *//**2,怎么创建线程?多线程? *  * 如何在自定义的代码中,自定义一个线程呢? *  1,继承Thread类 *  2,实现Runnable接口 *   *//**2.1,创建线程的第一种方法:继承Thread 类, * 通过对api的查找,java已经对提供了对线程这类事物的描述,就是Thread类 *  * 步骤: *  1,定义类,继承Thread *   2,复写父类中的run方法 ,目的:将自定义的代码存储在run方法中,让线程运行 *   3,调用线程的start方法,该方法有两个作用:a,启动线程;b,调用run方法 *    * 为什么要覆盖run方法呢? *    Thread类用于描述线程,该类就定义了一个功能,用于存储线程要运行的代码,该存储 *  功能就是run方法也就是说,Thread类中的run方法,适用于存储线程要运行的代码(主线 *  程运行代码存储在main方法中,jvm定义的) *  */class HelloJava extends Thread{public void run(){//将线程要运行的代码放到run方法中for(int i=0;i<5;i++){System.out.println(Thread.currentThread().getName()+"--hello,Thread,I am coming!");}}}public class Thread1 extends Thread {public static void main(String[] args) {HelloJava hj = new HelloJava();hj.start();//启动多线程//hj.run();//这不是启动线程,仅仅是调用了run方法HelloJava hh = new HelloJava();hh.start();for(int i=0;i<5;i++){System.out.println(Thread.currentThread().getName()+",这是main线程");}}}/* 输出:Thread-0--hello,Thread,I am coming!Thread-1--hello,Thread,I am coming!Thread-1--hello,Thread,I am coming!Thread-1--hello,Thread,I am coming!Thread-1--hello,Thread,I am coming!Thread-1--hello,Thread,I am coming!main,这是main线程Thread-0--hello,Thread,I am coming!main,这是main线程Thread-0--hello,Thread,I am coming!main,这是main线程Thread-0--hello,Thread,I am coming!main,这是main线程Thread-0--hello,Thread,I am coming!main,这是main线程 *//* 发现每次的运行结果都不同: 因为多个线程都获取cpu的执行权,cpu执行到谁,谁就运行明确一点:在某一个时刻,只能有一个程序在运行(多核除外)cpu在做着快速的切换,以达到看上去是同时运行的效果我们可以形象得把多线程的运行形容为在互相抢夺cpu的执行权,这就是多线程的一个特性:随机性,谁抢到谁执行,至于执行多长,cpu说的算(后期可以控制,但是不容易) *//**2.2 * 创建线程的第二种方式:实现Runnable接口 * 步骤: * 1,定义类实现Runnable接口 * 2,覆盖Runnable接口中的run放方法 * 将线程要运行的代码存放到run方法中 * 3,通过Thread类建立线程对象 * 4,将Runnable接口的子类对象作为"实际参数"传递给Thread类的构造函数 * 为什么要将Runnable接口的子类对象传递给Thread的构造函数?? *    因为:自定义的run方法所属的对象是Runnable接口的子类对象,所以如果要让线 * 程去指定对象的run方法,就必须明确该run方法所属的对象。。 * 5,调用Thread类的start方法开启线程并调用Runnable接口子类的run方法 *  */class MyThread implements Runnable{private String name;MyThread(String name){this.name = name;}public void run(){for(int i=0;i<10;i++){System.out.println(name+"--"+i+ ",Runnable接口的线程");}}}public class RunThread {public static void main(String[] args) {MyThread mt = new MyThread("Run");Thread t1 = new Thread(mt);Thread t2 = new Thread(mt);Thread t3 = new Thread(mt);Thread t4 = new Thread(mt);t1.start();  //不能使用run()方法开启线程t2.start();t3.start();t4.start();}}/**2.3,继承Thread类和实现Runnable接口有什么区别? *  * 一定要记住,面试要考) * (自己理解:如果一个类想要实现多线程,运用第一种方式,那么该类一定要继承Thread类, * 如果该类已经有了自己的父类的话,此时再继承Thread类就会出错,而如果采用接口实现的话, *则不会出现这种错误,因为接口可以多实现) *  * 实现方式:避免了单继承的局限性,在定义线程时,建议使用实现方式(第二种方式), *如果一个类没有父类的话,也可以使用第一种方式 *  * 两种方式的区别: * 1,继承Thread类:线程代码存放在Thread子类run方法中 * 2,实现Runnable:线程代码存放在接口的子类的run方法中 *  3,一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很 * 容易的实现资源共享。 *  */class MyThread1 extends Thread{private int count = 5;public void run(){for(int i=0;i<7;i++){if(count>0){try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("count="+count--);}}}}class MyThread2 implements Runnable{private int ticket = 5;public void run(){while(true){if(ticket>0){try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("ticket="+ticket--);}}}}/*输出:ticket=5   ;ticket=2   ;ticket=3   ;ticket=4   ;ticket=1   ;ticket=0   ;ticket=-1   ;ticket=-2   ;ticket=-3   ;*//**3,多线程的安全问题: * 通过分析发现:打印出0,-1,-2等错误的ticket,此时就要考虑现成的安全问题。 * 多线程出现了安全问题,写多线程时一定要小心安全问题 *  * 问题的原因:(重点,要记住) * 当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分, * 还没有执行完,另一个线程就参与进来执行,导致共享数据的冲突错误。 *  *  为了避免这样的事情发生,我们要保证线程同步互斥,所谓同步互斥就是:并发 * 执行的多个线程在某一时间内只允许一个线程在执行以访问共享数据。 *  *   解决办法:对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中, * 其他线程不可以参与执行. *  * 解决办法: * a.同步代码块  synchronized(同步对象){  //但是一般都把当前对象this作为同步对象。//允许访问控制的代码   } * b.同步方法  public synchronized void method(){     //允许访问控制的代码  } * c.锁定整个类   public synchronized class SyncObject{ } *  * 同步的前提: * 1,必须要有两个或者两个以上的线程。 * 2,必须是多个线程使用同一个锁 * 必须保证同步中只能有一个线程在运行** * (有些代码是需要同步的,有些代码是不需要同步的) *  * 好处:解决了多线程的同步问题; * 弊端:多个线程需要判断锁,所以要消耗更多资源、 *  *  **同步内部不能有循环(for,while),否则,就不是多线程了,要将 *同步放到循环内。 *  *//**3.1,同步代码块  * java对于多线程的安全问题,提供了专业的解决问题:****就是同步代码块儿**** * synchronized(对象) * { * 需要同步的代码;(那么那些是同步的代码呢?就看那些是 共享数据 * } *  * 同步代码块:对象如同锁,持有锁的线程可以在同步中执行, *  没有持有锁的的线程即使获得cpu的执行权,也进不去,因为没有获取锁。。 * 经典实例:火车上的卫生间。 *//** * 采用同步方法解决安全问题 */class MyThread1 implements Runnable{private int count = 5;public void run(){saleTicket();}//同步方法private synchronized void saleTicket(){for(int i=0;i<7;i++){if(count>0){try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("count="+count--);}}}}/** * 采用同步代码块解决安全问题 */class MyThread2 implements Runnable{private int ticket = 5;public void run(){while(true){synchronized(this){if(ticket>0){try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("ticket="+ticket--+"   ;");}}}}}public class ShareThread {public static void main(String[] args) {//这是同步方法MyThread1 mt = new MyThread1();Thread mt4 = new Thread(mt);Thread mt1 = new Thread(mt);Thread mt2 = new Thread(mt);Thread mt3 = new Thread(mt);mt1.start();mt2.start();mt3.start();mt4.start();/*//实现方式,可以实现资源共享MyThread2 mtt1 = new MyThread2();Thread t1 = new Thread(mtt1);Thread t2 = new Thread(mtt1);Thread t3 = new Thread(mtt1);Thread t4 = new Thread(mtt1);Thread t5 = new Thread(mtt1);t1.start();t2.start();t3.start();t4.start();t5.start();*/}}/**4,同步函数用的锁是哪一个? *同步函数(show())到底用的是哪个锁?? *函数需要被对象调用,那么函数都有一个所属对象的引用,就是this *所以同步函数使用的锁是**this**(要记得,程序不要求会写,主要是理解同步函数的锁) * *需求:通过该程序进行验证 *使用两个线程来卖票,一个线程在同步代码块中,一个线程在同步函数中,都在执行卖票操作。 * 代码如下: *  * 结论:如果同步代码块用的所也是this的话, 就不会出现0号票。 */class Ticket3 implements Runnable{private int tick = 100;boolean flag = true;Object obj = new Object();public  void run(){if(flag){while(true){synchronized(this){if(tick > 0){try{Thread.sleep(30);} catch(Exception e){System.out.println(e.toString());}System.out.println(Thread.currentThread().getName()+"预购从速"+tick--);}}}}else{while(true){show();}}}private synchronized  void show(){if(tick>0){try{Thread.sleep(30);}catch (Exception e){System.out.println("什么情况?");}System.out.println(Thread.currentThread().getName()+"车票买出一张,预购从速"+tick--);}}}public class ThisLockDemo {public static void main(String[] args) {Ticket3 tt = new Ticket3();Thread t1 = new Thread(tt);Thread t2 = new Thread(tt);t1.start();try{Thread.sleep(20);}catch(Exception e){}tt.flag = false;t2.start();}}/** * 5,静态的同步方法(**考点**) * * 同步函数被静态stait修饰后,通过验证发现,使用的锁不再是this了,此时二者不同步了! * 因为静态方法中不可以定义this,静态进内存时,内存中没有本类对象,但是一定有该类对 * 应的字节码文件对象:类名.class,该对象的类型是class *  * 结论: * 静态的同步方法,使用的锁是该方法所在的类的字节码文件对象:类名.class * synchronized(Ticket3.class)//同步代码块 *  * 字节码文件在内存中是唯一的?? */class Tickett3 implements Runnable{private static int tick = 100;boolean flag = true;public void run(){if(flag){while(true){synchronized(Tickett3.class){/* * 注意此处的锁是那个? */if(tick > 0){try{Thread.sleep(30);}catch(Exception e){System.out.println(e.toString());}System.out.println(Thread.currentThread().getName()+"春运啦,买票要快点啊。"+tick--);}}}}else{while(true){show();}}}private static synchronized void show(){if(tick > 0){try{Thread.sleep(30);} catch(Exception e){System.out.println(e.toString());}System.out.println(Thread.currentThread().getName()+"夏云啊,买票要速度啊啊"+tick--);}}}public class StaticMethodThread {public static void main(String[] args) {Tickett3 tt = new Tickett3();Thread t1 = new Thread(tt);Thread t2 = new Thread(tt);t1.start();try{Thread.sleep(30);}catch(Exception e){System.out.println(e.toString());}tt.flag = false;t2.start();}}/**6,获取线程对象以及线程名称 *  * 原来线程都有自己**默认**的名称:Thread-编号,该编号从0开始,能用用getName()获取 *   也可以用setName()来设置线程的名字 *  * static Thread currentThread();获取当前线程对象,通用写法 * getName();获取线程的名称 *  * 设置线程的名称:setName或者构造函数 *  *//**7,死锁: *    由多线程带来的性能改善是以可靠性为代价的,主要是因为有可能产生线程死锁。死锁是这 *  样一种情形:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线 *  程被无限期地阻塞,因此程序不能正常运行。简单的说就是:线程死锁时,第一个线程等待第 *  二个线程释放资源,而同时第二个线程又在等待第一个线程释放资源。 *    这里举一个通俗的例子:如在人行道上两个人迎面相遇,为了给对方让道,两人同时向一侧 *  迈出一步,双方无法通过,又同时向另一侧迈出一步,这样还是无法通过。假设这种情况一 *  直持续下去,这样就会发生死锁现象。  *   *    导致死锁的根源在于不适当地运用“synchronized”关键词来管理线程对特定对象的访问。 *  “synchronized”关键词的作用是,确保在某个时刻只有一个线程被允许执行特定的代码块, *  因此,被允许执行的线程首先必须拥有对变量或对象的排他性访问权。当线程访问对象时,线 *  程会给对象加锁,而这个锁导致其它也想访问同一对象的线程被阻塞,直至第一个线程释放它 *  加在对象上的锁。 *  */class Tickett2 implements Runnable{private static int tick = 100;Object obj = new Object();boolean flag = true;public void run(){if(flag){while(true){synchronized(obj){/* * 多个锁(3个) */show();}}}else{while(true){show();}}}public synchronized void show(){/* * 锁上套锁 */synchronized(obj){if(tick>0){try{Thread.sleep(30);} catch(Exception e){System.out.println(e.toString());}System.out.println(Thread.currentThread().getName()+"买票啊"+tick--);}}}}public class LockDemo {public static void main(String[] args) {Tickett2 tt = new Tickett2();Thread t1 = new Thread(tt);Thread t2 = new Thread(tt);t1.start();try{Thread.sleep(30);}catch(Exception e){System.out.println(e.toString());}tt.flag = false;t2.start();}}/**8,自己写一个死锁: *  *//** * 有时面试官会要求写一个死锁程序,以测试是否理解死锁 * 下面就是一个 */class LockObject{/* * 生成锁的对象 */static Object lockA = new Object();static Object lockB = new Object();}class Testt implements Runnable{boolean flag = true;Testt(boolean flag){this.flag = flag;}public void run(){if(flag){synchronized(LockObject.lockA){System.out.println(Thread.currentThread().getName()+"--if lockA--");synchronized(LockObject.lockB){System.out.println(Thread.currentThread().getName()+"**if lockB**");}}}else{synchronized(LockObject.lockB){System.out.println(Thread.currentThread().getName()+"--else lockB");synchronized(LockObject.lockA){System.out.println(Thread.currentThread().getName()+"**else lockA");}}}}}public class MyLock {public static void main(String[] args) {Thread t1 = new Thread(new Testt(true));Thread t2 = new Thread(new Testt(false));t1.start();t2.start();}}
 
 /**小结: * 1,现成的相关概念。 * 2,怎么创建线程?有几种方法,分别是什么? * 3,线程的安全问题,该怎么解决?有几种解决办法?分别是什么? * 4,同步函数的锁是哪一个?static的同步函数的锁又是哪一个? * 5,设置并获取线程名字的途径有哪些?(查文档) * 6,什么是死锁?写一个死锁的小例子。 */
 
----------android培训java培训、期待与您交流!----------

  详细请查看:http://edu.csdn.net/heima/

原创粉丝点击