Java基础<七>_多线程
2013-09-11 20:14
459 查看
多线程
一、多线程
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类的创建线程方式:
方式二:创建子类实现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。
死锁:同步的嵌套,可能会发生死锁现象,需注意避免。
下面代码演示:
多线程下的卖票程序:
多线程下的单例模式:
1.4、线程间通信
线程间通信:其实就是多个线程在操作同一个资源,但操作动作不同。通常会把这个资源封装成为对象!
等待唤醒机制:等待的线程都存在于线程池中,唤醒的都是线程池中拥有同一把锁的冻结状态线程,notify()方法唤醒的是同一把锁中的任意一个线程,notifyAll()唤醒的是线程池中拥有同一把锁的所有冻结状态线程。
wait()、notify()、notifyAll()等方法都用在同步中,因为要对持有监视器(锁)的线程操作,只有同步才有锁。
为什么操作线程同步的方法定义在Object类中?
因为这些方法在操作同步中的代码时,都必须要标识它们所在线程持有的锁;只有同一个锁上的等待线程可以被同一个锁上的notify唤醒,不可以被不同锁中的notify唤醒;也就是说:等待和唤醒必须是同一个锁,而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object类中。
下面代码演示:
演示1:
生产者与消费者:
1.5、线程的停止
分析:stop方法已经过时,那么只有一种方法能停止线程,run方法结束。其实开启多线程运行,运行代码通常是循环结构;只要控制住循环,就可以让run方法结束,也就是停止线程。
特殊情况:当线程处于冻结状态,就不会读取到标记,那么线程就不会结束。
当没有指定的方式让冻结的线程恢复到运行状态,这时需要对冻结状态进行清除。强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。
Thread类提供该方法:interrupt()
下面代码演示:
1.6、线程的其他方法
1、setPriority(int Priority):设置线程的优先级。
2、setDaemon(boolean b):守护线程,后台线程。
3、toString():返回该线程的字符串表示形式,包括线程名称、优先级和线程组。
4、join():加入,当A线程执行到了B线程的join方法时,A会等待,等B线程都执行完,A才会执行。Join可以用来临时加入线程执行。
有关更多方法、更详细解释,可以查询API。
一、多线程
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。
相关文章推荐
- Java基础<九>---> 多线程
- 黑马程序员 Java基础<五>---> 多线程
- 黑马程序员 java基础<七>--网络编程(1)
- Java基础<七>--->抽象类、接口、内部类、异常等
- <黑马程序员>---java基础---多线程知识
- 黑马程序员自学笔记 Java基础<五>---> 多线程
- 程序员_Java基础之<七>-集合框架
- 黑马程序员 JAVA基础<三> 多线程
- 黑马程序员 Java基础<七>---> 集合框架
- [原]java专业程序代写(qq:928900200),学习笔记之基础入门<SQL_Server_视图_函数_存储过程_触发器等>(二十三)
- 程序员_Java基础之<九>-泛型、其他对象
- 黑马程序员 java概述与基础知识<一>
- 程序员_Java基础之<十一>-IO流<2>File流
- Java基础知识<2>
- 程序员_Java基础之<十二>-IO流<3>其他流对象、编码
- 软件工程基础<七>
- [原]java专业程序代写(qq:928900200),学习笔记之基础入门<javascript>(三十)
- 程序员_Java基础之<五>-线程
- [原]java专业程序代写(qq:928900200),学习笔记之基础入门<SQL_Server_常用查询>(二十二)
- _Java基础<二>_Java语言基础组成(上)