好贷网好贷款

黑马程序员_Java基础<七>_多线程

发布时间:2016-12-3 3:54:19 编辑:www.fx114.net 分享查询网我要评论
本篇文章主要介绍了"黑马程序员_Java基础<七>_多线程",主要涉及到黑马程序员_Java基础<七>_多线程方面的内容,对于黑马程序员_Java基础<七>_多线程感兴趣的同学可以参考一下。

多线程 -----------android培训、java培训、期待与您交流! ------------ 一、多线程     1.1、多线程概述     进程与线程:正在运行的程序,就叫做进程。每一个进程的执行都有特定的顺序,每一个顺序都是一个执行路径,也叫做控制单元。进程中具体的执行路线就是线程,也就是说线程在控制着进程的执行,一个进程中至少有一个线程。当一个进程中具有多条线程时,称之为多线程。     JVM启动时有一个进程:java.exe,该进程中至少有一个线程负责java程序的执行;该线程运行main方法中的代码,称为主线程。     扩展:其实JVM启动时不止一个线程,除了主线程外还有专门负责垃圾回收的垃圾回收线程。     好处:多线程可以让程序多部分代码同时执行,提高代码运行效率;多线程有很多应用,例如:迅雷的多任务下载。     线程运行状态:线程可以分为:被创建、运行、冻结、消亡四个状态。其中冻结状态就是线程放弃了执行的资格,没有执行资格;临时(阻塞)状态:具有执行资格,但没有执行权。线程状态之间的切换可以通过特定的方法完成,如下图示:      注意:sleep()方法中需要指定睡眠时间,单位毫秒。     1.2、创建线程的方式     创建线程分为两种方式:1、创建子类继承Thread类;2、创建子类实现Runnable接口。     Java已经提供了对线程的描述,即Thread类。     方式一:创建子类继承Thread类;     步骤:(1)、定义一个类继承Thread类,并复写Thread类中的run()方法;           (2)、创建继承了Thread类的子类对象,这时线程已经被建立;           (3)、调用线程的start()方法,开启线程。     Thread类用于描述线程,该类中定义了一个run()方法用于存储线程要执行的代码,所以:为了新创建的线程有意义,必须复写run()方法。     注意:start()方法可以开启线程,并调用run()方法;run()方法不具备开启线程的功能,仅仅是简单的方法调用。     下面代码演示继承Thread类的创建线程方式: package itheima.day11; //演示继承Thread类的创建线程方式 public class ThreadTest { public static void main(String[] args) { // 创建线程 Test t1 = new Test("one"); Test t2 = new Test("two"); // 开启线程 t1.start(); t2.start(); // 主线程 main for(int x=0;x<60;x++){ System.out.println("main run==="+x); } } } //创建线程类 class Test extends Thread { Test(String name){ super(name); } // 复写run()方法,写上线程的执行代码 public void run(){ for(int i =0;i<60;i++) System.out.println(Thread.currentThread().getName()+" Test run...."+i); } }     方式二:创建子类实现Runnable接口;     步骤:(1)、定义一个类实现Runnable接口,并复写runnable接口中的run()方法;           (2)、通过Thread类创建线程,并把实现了Runnable接口的子类对象作为参数                给Thread类的构造函数;           (3)、Thread类对象调用start()方法开启线程。     其实实现Runnable接口的类,只是给出了线程执行的任务(代码),把这个任务作为构造函数的参数传给一个线程时,才创建一个线程,当把这个任务同时给多个线程时,那么多个线程可以同时操作这个任务,这时就要注意线程安全(同步互斥)问题了。注意:第一种创建线程的方式不可实现多个线程操作同一个任务!     实现方式创建线程可以避免单继承的局限性,所以建议使用实现方式。     1.3、线程安全问题     原因:多线程出现安全问题,是因为多条线程操作同一个共享数据时,一条线程还没执行完,另一条线程却参与进来,最终导致共享数据的操作错误。     解决方法:当操作共享数据时,让一个线程执行完了才让另外的线程参与运行。     如何找问题:1、明确哪些代码是多线程运行代码;2、明确共享数据;3、明确多线程运行代码中哪些语句是操作共享数据的。     Java对于多线程的安全问题提供了专业的解决方式:同步代码块和同步函数。     同步的前提:1、同步需要两个或者以上的线程;2、多个线程使用同一个锁。     同步代码块:格式为:synchronized(对象){需要被同步的代码;}。对象如同锁,只有持有锁的线程才可以在同步中执行。     同步函数:直接在函数上加上synchronized修饰符即可,在同步函数中使用的锁是当前对象this;当同步函数是静态时,静态函数进入内存时,内存中还没有本类对象,所以静态函数的锁不是当前对象,而是本类的字节码文件,类名.class。 死锁:同步的嵌套,可能会发生死锁现象,需注意避免。 下面代码演示: 多线程下的卖票程序: package itheima.day11; //需求:简单的卖票程序,多个窗口同时卖票 public class SoldTicket { public static void main(String[] args) { Ticket t = new Ticket(); Thread t1 = new Thread(t); Thread t2 = new Thread(t); Thread t3 = new Thread(t); Thread t4 = new Thread(t); t1.start(); t2.start(); t3.start(); t4.start(); } } class Ticket implements Runnable { private int ticket = 1000; public void run(){ while(true){ show(); } } public synchronized void show(){ if (ticket > 0) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "...sale..." + ticket); ticket--; } } }  多线程下的单例模式: package itheima.day11; //单例设计模式 public class SingleDemo { public static void main(String[] args) { Single s1 = Single.getInstance(); Single s2 = Single.getInstance(); Single s3 = Single.getInstance(); } } //懒汉式 class Single{ private static Single s = null; private Single(){ System.out.println("Single demo..."); } public static Single getInstance(){ if(null == s){ synchronized(Single.class){ if(null ==s) s = new Single(); } } return s; } } //饿汉式 /* class Single{ private static final Single s = new Single(); private Single(){} public static Single getInstance(){ return s; } }*/     1.4、线程间通信     线程间通信:其实就是多个线程在操作同一个资源,但操作动作不同。通常会把这个资源封装成为对象!     等待唤醒机制:等待的线程都存在于线程池中,唤醒的都是线程池中拥有同一把锁的冻结状态线程,notify()方法唤醒的是同一把锁中的任意一个线程,notifyAll()唤醒的是线程池中拥有同一把锁的所有冻结状态线程。     wait()、notify()、notifyAll()等方法都用在同步中,因为要对持有监视器(锁)的线程操作,只有同步才有锁。     为什么操作线程同步的方法定义在Object类中?     因为这些方法在操作同步中的代码时,都必须要标识它们所在线程持有的锁;只有同一个锁上的等待线程可以被同一个锁上的notify唤醒,不可以被不同锁中的notify唤醒;也就是说:等待和唤醒必须是同一个锁,而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object类中。 下面代码演示: 演示1: package itheima.day12; //演示线程的通信 public class InputOutputDemo { public static void main(String[] args) { Resource r = new Resource(); new Thread(new Input(r)).start(); new Thread(new Output(r)).start(); } } class Resource{ private String name; private String sex; boolean flag = false; public synchronized void set(String name,String sex){ if(flag) try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } this.name = name; this.sex = sex; flag = true; this.notify(); } public synchronized void out(){ if(!flag) try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(name+"..."+sex); flag = false; this.notify(); } } class Input implements Runnable { private Resource r ; Input(Resource r){ this.r = r; } public void run(){ int x = 0; while(true){ if(x==0) r.set("mike","man"); else r.set("丽丽","女女女女女"); x = (x+1)%2; } } } class Output implements Runnable { private Resource r ; Output(Resource r){ this.r = r; } public void run(){ while(true){ r.out(); } } } 生产者与消费者: package itheima.day12; //线程通信;生产者与消费者 public class ProducerConsumerDemo { public static void main(String[] args) { // 资源 Res r = new Res(); Producer pro = new Producer(r);//生产者 Consumer con = new Consumer(r);//消费者 Thread t1 = new Thread(pro);//生产者1 Thread t2 = new Thread(pro);//生产者2 Thread t3 = new Thread(con);//消费者1 Thread t4 = new Thread(con);//消费者2 t1.start();//1生产 t2.start();//2生产 t3.start();//1消费 t4.start();//2消费 } } class Res{ private String name; private int count; private boolean flag = false; public synchronized void set(String name){ // if(flag)//醒来不会再次判断标记 while(flag) try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } this.name = name+"---"+count++; System.out.println(Thread.currentThread().getName()+"...生产者...-------------"+this.name); flag = true; // this.notify(); this.notifyAll(); } public synchronized void out(){ // if(flag)//醒来不会再次判断标记 while(!flag) try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"...消费者..."+this.name); flag = false; // this.notify(); this.notifyAll(); } } //生产者 class Producer implements Runnable { private Res res; Producer(Res res){ this.res = res; } public void run(){ while(true){ res.set("+商品+");//生产 } } } class Consumer implements Runnable { // 要消费的资源 private Res res; Consumer(Res res){ this.res = res; } public void run(){ while(true){ res.out();//消费 } } }     1.5、线程的停止     分析:stop方法已经过时,那么只有一种方法能停止线程,run方法结束。其实开启多线程运行,运行代码通常是循环结构;只要控制住循环,就可以让run方法结束,也就是停止线程。     特殊情况:当线程处于冻结状态,就不会读取到标记,那么线程就不会结束。 当没有指定的方式让冻结的线程恢复到运行状态,这时需要对冻结状态进行清除。强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。 Thread类提供该方法:interrupt() 下面代码演示: package itheima.day03; //演示主线程运行60次之后让t1、t2线程停止 public class StopThreadDemo { public static void main(String[] args) { StopThread st = new StopThread(); Thread t1 =new Thread(st); Thread t2 = new Thread(st); t1.start(); t2.start(); int num =0; while(true){ if(num++==60){ // 对冻结状态的线程进行清除 t1.interrupt(); t2.interrupt(); break; } System.out.println(Thread.currentThread().getName()+"..run..."+num); } System.out.println("main over"); } } class StopThread implements Runnable{ private boolean flag = true; @Override public synchronized void run() { while(flag){ try { this.wait(); } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName() +".....Exception"); } // System.out.println(Thread.currentThread().getName()+"....run"); } } /* public void changeFlag(){ flag = false; }*/ }     1.6、线程的其他方法     1、setPriority(int Priority):设置线程的优先级。     2、setDaemon(boolean b):守护线程,后台线程。     3、toString():返回该线程的字符串表示形式,包括线程名称、优先级和线程组。     4、join():加入,当A线程执行到了B线程的join方法时,A会等待,等B线程都执行完,A才会执行。Join可以用来临时加入线程执行。 有关更多方法、更详细解释,可以查询API。 -----------android培训、java培训、期待与您交流! ------------

上一篇:Hibernate使用sql语句查询
下一篇:Marks Distribution

相关文章

相关评论