[关闭]
@liayun 2016-05-28T11:15:37.000000Z 字数 17940 阅读 1507

多线程

java基础


进程

进程:是一个正在执行中的程序。
每一个进程执行都有一个执行顺序。该顺序就是一个执行路径或者叫一个控制单元。

线程

Java VM启动时会有一个进程java.exe,该进程中至少有一个线程负责java程序的执行。而且这个线程运行的代码存在main方法中,该线程称之为主线程
扩展知识:其实更细节说明JVM,JVM不止启动一个线程,还有负责垃圾回收机制的线程。

创建线程方式一

如何在自定义的代码中,自定义定义一个线程?
答:通过对API的查找,java已经提供了对线程这类事物的描述,即Thread类。
创建线程的第一种方式:继承Thread类。
步骤:

  1. 继承Thread
  2. 重写Thread类中的run()。目的:将自定义的代码存储在run(),让线程运行
  3. 调用线程的start()。该方法有2个作用:启动线程,调用run()

例,

  1. class Demo extends Thread {
  2. public void run() {
  3. for (int x = 0; x < 60; x++) {
  4. System.out.println("demo run---"+x);
  5. }
  6. }
  7. }
  8. class ThreadDemo {
  9. public static void main(String[] args) {
  10. Demo d = new Demo(); // 创建好一个线程
  11. // d.start(); // 开启线程,并执行该线程的run()
  12. d.run(); // 仅仅是对象的调用方法,而线程创建了,并没有被运行
  13. for (int x = 0; x < 60; x++) {
  14. System.out.println("Hello World!---"+x);
  15. }
  16. }
  17. }

发现运行结果每一次都不同。
因为多个线程都在获取CPU的执行权,CPU执行到谁,谁就运行。明确一点,在某一个时刻,只能有一个程序在运行(多核除外)。CPU在做着快速的切换,以达到看上去是同时运行的效果。我们可以形象地把多线程的运行形容为互相抢夺CPU的执行权,这就是多线程的一个特点:随机性。谁抢到谁执行,至于执行多长,CPU说了算。
为什么要覆盖run()呢?
答:Thread类用于描述线程,该类就定义了一个功能,用于存储线程要运行的代码,该存储功能就是run()。也就是说Thread类中的run()用于存储线程要运行的代码。
练习:创建两个线程,和主线程交替执行。
解:

  1. class Test extends Thread {
  2. Test(String name) {
  3. super(name);
  4. }
  5. public void run() {
  6. for (int x = 0; x < 60; x++) {
  7. System.out.println((Thread.currentThread()==this)+"..."+this.getName()+" run..."+x);
  8. }
  9. }
  10. }
  11. class ThreadTest {
  12. public static void main(String[] args) {
  13. Test t1 = new Test("one---");
  14. Test t2 = new Test("two+++");
  15. t1.start();
  16. t2.start();
  17. for (int x = 0; x < 60; x++) {
  18. System.out.println("main..."+x);
  19. }
  20. }
  21. }

通过上例,可发现原来线程都有自己默认的名称:Thread-编号,该编号从0开始。

线程的状态

线程的状态转换图

创建线程方式二

以此例引申出创建线程的第二种方式:
例,需求:简单的卖票程序。多个窗口同时买票。

  1. class Ticket implements Runnable {
  2. private int tick = 100;
  3. public void run() {
  4. while(true) {
  5. if(tick > 0) {
  6. try { Thread.sleep(10); } catch(Exception e) {}
  7. System.out.println(Thread.currentThread().getName()+"...sale:"+tick--);
  8. }
  9. }
  10. }
  11. }
  12. class TicketDemo {
  13. public static void main(String[] args) {
  14. Ticket t = new Ticket();
  15. Thread t1 = new Thread(t); // 创建一个线程
  16. Thread t2 = new Thread(t); // 创建一个线程
  17. Thread t3 = new Thread(t); // 创建一个线程
  18. Thread t4 = new Thread(t); // 创建一个线程
  19. t1.start();
  20. t2.start();
  21. t3.start();
  22. t4.start();
  23. }
  24. }

创建线程的第二种方式:实现Runnable接口。
步骤:

  1. 定义类实现Runnable接口
  2. 覆盖Runnable接口中的run()。目的:将线程要运行的代码存放在该run()
  3. 通过Thread类建立线程对象
  4. Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
    为什么要将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数?
    答:因为自定义的run()所属的对象是Runnable接口的子类对象,所以要让线程去运行指定对象的run(),就必须明确该run()所属的对象
  5. 调用Thread类的start()开启线程并调用Runnable接口子类的run方法。

实现方式和继承方式有什么区别呢?
1. 实现方式好处:避免了单继承的局限性。在定义线程时,建议使用实现方式
2. 继承Thread:线程代码存放Thread子类的run()
3. 实现Runnable:线程代码存放在接口的子类的run()

线程安全问题

还是以简单的卖票程序为例,通过分析发现打印出0、-1、-2等错票,多线程的运行出现了安全问题。
问题的原因:当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致了共享数据的错误。
解决办法:对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。
java对于多线程的安全问题提供了专业的解决方式——就是同步代码块。
注意:线程安全问题在理想状态下,不容易出现,但一旦出现对软件的影响是非常大的。

同步(synchronized)

格式:

synchronized(对象) {
    需要被同步的代码
}

对象如同锁,持有锁的线程可以在同步中执行。没有持有锁的线程即使获取CPU的执行权,也进不去,因为没有锁。
火车上的卫生间---经典同步例子。
同步的前提:

  1. 必须要有两个或者两个以上的线程
  2. 必须是多个线程使用同一个锁

必须保证同步中只有一个线程在运行。
同步的好处:解决了多线程的安全问题。
同步的弊端:多个线程需要判断锁,较为消耗资源。
所以同步之后的代码为:

  1. class Ticket implements Runnable {
  2. private int tick = 100;
  3. Object obj = new Object();
  4. public void run() {
  5. while(true) {
  6. synchronized(obj) {
  7. if(tick > 0) {
  8. try { Thread.sleep(10); } catch(Exception e) {}
  9. System.out.println(Thread.currentThread().getName()+"...sale:"+tick--);
  10. }
  11. }
  12. }
  13. }
  14. }
  15. class TicketDemo2 {
  16. public static void main(String[] args) {
  17. Ticket t = new Ticket();
  18. Thread t1 = new Thread(t); // 创建一个线程
  19. Thread t2 = new Thread(t); // 创建一个线程
  20. Thread t3 = new Thread(t); // 创建一个线程
  21. Thread t4 = new Thread(t); // 创建一个线程
  22. t1.start();
  23. t2.start();
  24. t3.start();
  25. t4.start();
  26. }
  27. }

同步函数

以此例引申出同步函数,需求:银行有一个金库,有两个储户,分别存300元,每次存100元,存3次。目的:该程序是否有安全问题,如果有,如何解决?

  1. class Bank {
  2. private int sum;
  3. public void add(int n) {
  4. sum = sum + n;
  5. try {
  6. Thread.sleep(10);
  7. } catch (InterruptedException e) {
  8. }
  9. System.out.println("sum="+sum);
  10. }
  11. }
  12. class Cus implements Runnable {
  13. private Bank b = new Bank();
  14. public void run() {
  15. for(int x = 0; x < 3; x++) {
  16. b.add(100);
  17. }
  18. }
  19. }
  20. class BankDemo {
  21. public static void main(String[] args) {
  22. Cus c = new Cus();
  23. Thread t1 = new Thread(c);
  24. Thread t2 = new Thread(c);
  25. t1.start();
  26. t2.start();
  27. }
  28. }

解:
如何找到问题:

  1. 明确哪些代码是多线程运行代码
  2. 明确共享数据
  3. 明确多线程运行代码中哪些语句是操作共享数据的

所以,修改之后的代码为:

  1. class Bank {
  2. private int sum;
  3. // Object obj = new Object();
  4. // 同步函数
  5. public synchronized void add(int n) {
  6. // synchronized(obj) {
  7. sum = sum + n;
  8. try {
  9. Thread.sleep(10);
  10. } catch (InterruptedException e) {
  11. }
  12. System.out.println("sum="+sum);
  13. // }
  14. }
  15. }
  16. class Cus implements Runnable {
  17. private Bank b = new Bank();
  18. public void run() {
  19. for(int x = 0; x < 3; x++) {
  20. b.add(100);
  21. }
  22. }
  23. }
  24. class BankDemo {
  25. public static void main(String[] args) {
  26. Cus c = new Cus();
  27. Thread t1 = new Thread(c);
  28. Thread t2 = new Thread(c);
  29. t1.start();
  30. t2.start();
  31. }
  32. }

思考:同步函数用的是哪一个锁呢?
结论:函数需要被对象调用,那么函数都有一个所属对象引用,就是this。所以同步函数使用的锁是this
程序验证:通过该程序进行验证同步函数使用的锁是this。使用两个线程来卖票,一个线程在同步代码块中,一个线程在同步函数中,都在执行买票动作。

  1. class Ticket implements Runnable {
  2. private int tick = 100;
  3. Object obj = new Object();
  4. boolean flag = true;
  5. public void run() {
  6. if(flag) {
  7. while(true) {
  8. synchronized(this) {
  9. if(tick > 0) {
  10. try { Thread.sleep(10); } catch(Exception e) {}
  11. System.out.println(Thread.currentThread().getName()+"...code:"+tick--);
  12. }
  13. }
  14. }
  15. } else {
  16. while(true)
  17. show();
  18. }
  19. }
  20. public synchronized void show() { // this
  21. if(tick > 0) {
  22. try { Thread.sleep(10); } catch(Exception e) {}
  23. System.out.println(Thread.currentThread().getName()+"...show...:"+tick--);
  24. }
  25. }
  26. }
  27. class ThisLockDemo {
  28. public static void main(String[] args) {
  29. Ticket t = new Ticket();
  30. Thread t1 = new Thread(t); // 创建一个线程
  31. Thread t2 = new Thread(t); // 创建一个线程
  32. t1.start();
  33. // 主线程停10ms,此刻能运行的线程只有t1
  34. try { Thread.sleep(10); } catch(Exception e) {}
  35. t.flag = false;
  36. // 主线程醒了之后,t1和t2线程同时运行
  37. t2.start();
  38. }
  39. }

又思考:如果同步函数被静态修饰后,使用的锁是什么呢?
通过验证,发现不再是this,因为静态方法中也不可以定义this。静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象:类名.class,该对象的类型是Class
静态的同步方法,使用的锁是该方法所在类的字节码文件对象。即类名.class
程序验证同上:

  1. class Ticket implements Runnable {
  2. private static int tick = 100;
  3. // Object obj = new Object();
  4. boolean flag = true;
  5. public void run() {
  6. if(flag) {
  7. while(true) {
  8. synchronized(Ticket.class) {
  9. if(tick > 0) {
  10. try { Thread.sleep(10); } catch(Exception e) {}
  11. System.out.println(Thread.currentThread().getName()+"...code:"+tick--);
  12. }
  13. }
  14. }
  15. } else {
  16. while(true)
  17. show();
  18. }
  19. }
  20. public static synchronized void show() {
  21. if(tick > 0) {
  22. try { Thread.sleep(10); } catch(Exception e) {}
  23. System.out.println(Thread.currentThread().getName()+"...show...:"+tick--);
  24. }
  25. }
  26. }
  27. class StaticMethodDemo {
  28. public static void main(String[] args) {
  29. Ticket t = new Ticket();
  30. Thread t1 = new Thread(t); // 创建一个线程
  31. Thread t2 = new Thread(t); // 创建一个线程
  32. t1.start();
  33. // 主线程停10ms,此刻能运行的线程只有t1
  34. try { Thread.sleep(10); } catch(Exception e) {}
  35. t.flag = false;
  36. // 主线程醒了之后,t1和t2线程同时运行
  37. t2.start();
  38. }
  39. }

单例设计模式

饿汉式:

  1. class Single {
  2. private static final Single s = new Single();
  3. private Single() {}
  4. public static Single getInstance() {
  5. return s;
  6. }
  7. }

懒汉式:

  1. class Single {
  2. private static Single s = null;
  3. private Single() {}
  4. public static Single getInstance() {
  5. if(s == null) {
  6. synchronized(Single.class) {
  7. if(s == null)
  8. s = new Single();
  9. }
  10. }
  11. return s;
  12. }
  13. }

面试时,可能会问懒汉式与饿汉式有什么不同?
答:懒汉式的特点用于实例的延迟加载。
又问懒汉式的延迟加载有没有问题?
答:有,如果多线程访问时会出现安全问题。
又问怎么解决?
答:可以加同步来解决,用同步代码块和同步函数都行,但是稍微有一些低效,用双重判断的形式能解决这个效率问题。
最后问加同步时,使用的锁是哪一个?
答:该类所属的字节码文件对象。

面试题:请给我写一个延时加载的单例模式示例?

死锁

同步中嵌套同步。
例,

  1. class Ticket implements Runnable {
  2. private int tick = 1000;
  3. Object obj = new Object();
  4. boolean flag = true;
  5. public void run() {
  6. if(flag) {
  7. while(true) {
  8. synchronized(obj) {
  9. show();
  10. }
  11. }
  12. } else {
  13. while(true)
  14. show();
  15. }
  16. }
  17. public synchronized void show() {
  18. synchronized(obj) {
  19. if(tick > 0) {
  20. try { Thread.sleep(10); } catch(Exception e) {}
  21. System.out.println(Thread.currentThread().getName()+"...code:"+tick--);
  22. }
  23. }
  24. }
  25. }
  26. class DeadLockDemo {
  27. public static void main(String[] args) {
  28. Ticket t = new Ticket();
  29. Thread t1 = new Thread(t); // 创建一个线程
  30. Thread t2 = new Thread(t); // 创建一个线程
  31. t1.start();
  32. // 主线程停10ms,此刻能运行的线程只有t1
  33. try { Thread.sleep(10); } catch(Exception e) {}
  34. t.flag = false;
  35. // 主线程醒了之后,t1和t2线程同时运行
  36. t2.start();
  37. }
  38. }

面试题:写一个死锁程序。

  1. class Test implements Runnable {
  2. private boolean flag;
  3. Test(boolean flag) {
  4. this.flag = flag;
  5. }
  6. public void run() {
  7. if(flag) {
  8. while(true) {
  9. synchronized(MyLock.locka) {
  10. System.out.println("if locka");
  11. synchronized(MyLock.lockb) {
  12. System.out.println("if lockb");
  13. }
  14. }
  15. }
  16. } else {
  17. while(true) {
  18. synchronized(MyLock.lockb) {
  19. System.out.println("else lockb");
  20. synchronized(MyLock.locka) {
  21. System.out.println("else locka");
  22. }
  23. }
  24. }
  25. }
  26. }
  27. }
  28. // 锁
  29. class MyLock {
  30. static Object locka = new Object();
  31. static Object lockb = new Object();
  32. }
  33. class DeadLockTest {
  34. public static void main(String[] args) {
  35. Thread t1 = new Thread(new Test(true));
  36. Thread t2 = new Thread(new Test(false));
  37. t1.start();
  38. t2.start();
  39. }
  40. }

线程间通信

其实就是多个线程在操作同一个资源,但是操作的动作不同。
notifyAll():唤醒线程池中所有等待的线程。
notify():唤醒线程池中第一个等待的线程。
wait()/notify()/notifyAll():都使用在同步中,因为要对持有监视器(锁)的线程操作,所以要使用在同步中,因为只有同步才具有锁。
思考:wait(),notify(),notifyAll(),用来操作线程为什么定义在了Object类中?
答:

  1. 这些方法存在与同步中。
  2. 使用这些方法时必须要标识所属的同步的锁。
  3. 锁可以是任意对象,所以任意对象调用的方法一定定义在Object类中。

因为这些方法在操作同步中线程时,都必须标识它们所操作线程持有的锁,只有同一个锁上的被等待线程,才可以被同一个锁上的notify唤醒,不可以对不同锁中的线程进行唤醒。也就是说,等待和唤醒必须是同一个锁。而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object类中。

例,

  1. class Res {
  2. String name;
  3. String sex;
  4. boolean flag = false;
  5. }
  6. class Input implements Runnable {
  7. private Res r;
  8. // Object obj = new Object();
  9. Input(Res r) {
  10. this.r = r;
  11. }
  12. public void run() {
  13. int x = 0;
  14. while(true) {
  15. synchronized(r) {
  16. if(r.flag)
  17. try { r.wait(); } catch(Exception e) {} // wait持有r锁的线程
  18. if(x == 0) {
  19. r.name = "mike";
  20. r.sex = "man";
  21. } else {
  22. r.name = "丽丽";
  23. r.sex = "女女女女女";
  24. }
  25. x = (x+1)%2;
  26. r.flag = true;
  27. r.notify(); // notify持有r锁的等待线程
  28. }
  29. }
  30. }
  31. }
  32. class Output implements Runnable {
  33. private Res r;
  34. // Object obj = new Object();
  35. Output(Res r) {
  36. this.r = r;
  37. }
  38. public void run() {
  39. while(true) {
  40. synchronized(r) {
  41. if(!r.flag)
  42. try { r.wait(); } catch(Exception e) {}
  43. System.out.println(r.name+"...."+r.sex);
  44. r.flag = false;
  45. r.notify();
  46. }
  47. }
  48. }
  49. }
  50. class InputOutputDemo {
  51. public static void main(String[] args) {
  52. Res r = new Res();
  53. Input in = new Input(r);
  54. Output out = new Output(r);
  55. Thread t1 = new Thread(in);
  56. Thread t2 = new Thread(out);
  57. t1.start();
  58. t2.start();
  59. }
  60. }

思考:wait()sleep()有什么区别?
答:

以上代码经过优化后:

  1. class Res {
  2. private String name;
  3. private String sex;
  4. private boolean flag = false;
  5. public synchronized void set(String name, String sex) {
  6. if(flag)
  7. try { this.wait(); } catch(Exception e) {}
  8. this.name = name;
  9. this.sex = sex;
  10. flag = true;
  11. this.notify();
  12. }
  13. public synchronized void out() {
  14. if(!flag)
  15. try { this.wait(); } catch(Exception e) {}
  16. System.out.println(name+"........"+sex);
  17. flag = false;
  18. this.notify();
  19. }
  20. }
  21. class Input implements Runnable {
  22. private Res r;
  23. // Object obj = new Object();
  24. Input(Res r) {
  25. this.r = r;
  26. }
  27. public void run() {
  28. int x = 0;
  29. while(true) {
  30. if(x == 0)
  31. r.set("mike", "man");
  32. else
  33. r.set("丽丽", "女女女女女");
  34. x = (x+1)%2;
  35. }
  36. }
  37. }
  38. class Output implements Runnable {
  39. private Res r;
  40. Output(Res r) {
  41. this.r = r;
  42. }
  43. public void run() {
  44. while(true) {
  45. r.out();
  46. }
  47. }
  48. }
  49. class InputOutputDemo2 {
  50. public static void main(String[] args) {
  51. Res r = new Res();
  52. new Thread(new Input(r)).start();
  53. new Thread(new Output(r)).start();
  54. }
  55. }

为了理解,我画了一个示意图,如下:
等待唤醒机制

线程操作案例——生产者和消费者(单个)

  1. public class ProducerConsumerDemo {
  2. public static void main(String[] args) {
  3. Resource r = new Resource();
  4. Producer pro = new Producer(r);
  5. Consumer con = new Consumer(r);
  6. Thread t1 = new Thread(pro);
  7. Thread t2 = new Thread(con);
  8. t1.start();
  9. t2.start();
  10. }
  11. }
  12. class Resource {
  13. private String name;
  14. private int count = 1;
  15. private boolean flag = false;
  16. public synchronized void set(String name) {
  17. if(flag) {
  18. try {
  19. this.wait();
  20. } catch (InterruptedException e) {
  21. }
  22. }
  23. this.name = name+"--"+count++;
  24. System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
  25. flag = true;
  26. this.notify();
  27. }
  28. public synchronized void out() {
  29. if(!flag) {
  30. try {
  31. wait();
  32. } catch (InterruptedException e) {
  33. }
  34. }
  35. System.out.println(Thread.currentThread().getName()+"...消费者........"+this.name);
  36. flag = false;
  37. notify();
  38. }
  39. }
  40. class Producer implements Runnable {
  41. private Resource res;
  42. Producer(Resource res) {
  43. this.res = res;
  44. }
  45. public void run() {
  46. while(true) {
  47. res.set("+商品+");
  48. }
  49. }
  50. }
  51. class Consumer implements Runnable {
  52. private Resource res;
  53. Consumer(Resource res) {
  54. this.res = res;
  55. }
  56. public void run() {
  57. while(true) {
  58. res.out();
  59. }
  60. }
  61. }

运行以上代码是只有一个生成者和一个消费者,当有多个生产者和多个消费者时,会产生异常情况:即重复生产或重复消费
生产者和消费者(多个)

  1. class ProducerConsumerDemo {
  2. public static void main(String[] args) {
  3. Resource r = new Resource();
  4. Producer pro = new Producer(r);
  5. Consumer con = new Consumer(r);
  6. Thread t1 = new Thread(pro);
  7. Thread t2 = new Thread(pro);
  8. Thread t3 = new Thread(con);
  9. Thread t4 = new Thread(con);
  10. t1.start();
  11. t2.start();
  12. t3.start();
  13. t4.start();
  14. }
  15. }
  16. class Resource {
  17. private String name;
  18. private int count = 1;
  19. private boolean flag = false;
  20. public synchronized void set(String name) {
  21. while(flag)
  22. try { this.wait(); } catch(Exception e) {}
  23. this.name = name+"--"+count++;
  24. System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
  25. flag = true;
  26. this.notifyAll();
  27. }
  28. public synchronized void out() {
  29. while(!flag)
  30. try { this.wait(); } catch(Exception e) {}
  31. System.out.println(Thread.currentThread().getName()+"...消费者........"+this.name);
  32. flag = false;
  33. this.notifyAll();
  34. }
  35. }
  36. class Producer implements Runnable {
  37. private Resource res;
  38. Producer(Resource res) {
  39. this.res = res;
  40. }
  41. public void run() {
  42. while(true) {
  43. res.set("+商品+");
  44. }
  45. }
  46. }
  47. class Consumer implements Runnable {
  48. private Resource res;
  49. Consumer(Resource res) {
  50. this.res = res;
  51. }
  52. public void run() {
  53. while(true) {
  54. res.out();
  55. }
  56. }
  57. }

对于多个生产者和消费者,为什么一定要定义while判断标记?
原因:让被唤醒的线程再一次判断标记。
为什么定义notifyAll
原因:因为需要唤醒对方线程,因为只用notify,容易出现只唤醒本方线程的情况,导致程序中的所有线程都等待。
JDK1.5版本中提供了多线程的升级解决方案。

  1. 将同步synchronized替换成了显示的Lock操作。
  2. Object中的wait/notify/notifyAll替换成Condition对象。该对象可以通过Lock锁进行获取。

该示例实现了本方只唤醒对方的操作

  1. import java.util.concurrent.locks.*;
  2. class ProducerConsumerDemo2 {
  3. public static void main(String[] args) {
  4. Resource r = new Resource();
  5. Producer pro = new Producer(r);
  6. Consumer con = new Consumer(r);
  7. Thread t1 = new Thread(pro);
  8. Thread t2 = new Thread(pro);
  9. Thread t3 = new Thread(con);
  10. Thread t4 = new Thread(con);
  11. t1.start();
  12. t2.start();
  13. t3.start();
  14. t4.start();
  15. }
  16. }
  17. class Resource {
  18. private String name;
  19. private int count = 1;
  20. private boolean flag = false;
  21. private Lock lock = new ReentrantLock();
  22. // 返回绑定到此Lock实例的新Condition实例,Lock可以支持多个相关的Condition对象
  23. private Condition condition_pro = lock.newCondition();
  24. private Condition condition_con = lock.newCondition();
  25. public void set(String name) throws InterruptedException {
  26. lock.lock();
  27. try {
  28. while(flag)
  29. condition_pro.await(); // 抛出异常
  30. this.name = name+"--"+count++;
  31. System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
  32. flag = true;
  33. condition_con.signal(); // 唤醒某一个消费者
  34. } finally {
  35. lock.unlock(); // 释放锁资源的动作一定要执行
  36. }
  37. }
  38. public void out() throws InterruptedException {
  39. lock.lock();
  40. try {
  41. while(!flag)
  42. condition_con.await(); // 抛出异常
  43. System.out.println(Thread.currentThread().getName()+"...消费者........"+this.name);
  44. flag = false;
  45. condition_pro.signal(); // 唤醒某一个生产者
  46. } finally {
  47. lock.unlock();
  48. }
  49. }
  50. }
  51. class Producer implements Runnable {
  52. private Resource res;
  53. Producer(Resource res) {
  54. this.res = res;
  55. }
  56. public void run() {
  57. while(true) {
  58. try {
  59. res.set("+商品+");
  60. } catch(InterruptedException e) {
  61. }
  62. }
  63. }
  64. }
  65. class Consumer implements Runnable {
  66. private Resource res;
  67. Consumer(Resource res) {
  68. this.res = res;
  69. }
  70. public void run() {
  71. while(true) {
  72. try {
  73. res.out();
  74. } catch(InterruptedException e) {
  75. }
  76. }
  77. }
  78. }

面试时,可能会问生产者与消费者有什么替代方案呢?
答:JDK1.5版本提供了显示的锁机制,以及显示的锁对象上的等待唤醒操作机制。同时将等待唤醒进行了封装,封装完,一个锁对应多个Condition对象,之前一个锁对应一个Condition对象。

停止线程

如何停止线程?
stop()已经过时,只有一种,即run()结束。
开启多线程运行,运行代码通常都是循环结构。只要控制住循环,就可以让run()结束,也就是线程结束。
例,

  1. class StopThread implements Runnable {
  2. private boolean flag = true;
  3. public void run() {
  4. while(flag) {
  5. System.out.println(Thread.currentThread().getName()+"....run");
  6. }
  7. }
  8. public void changeFlag() {
  9. flag = false;
  10. }
  11. }
  12. class StopThreadDemo {
  13. public static void main(String[] args) {
  14. StopThread st = new StopThread();
  15. Thread t1 = new Thread(st);
  16. Thread t2 = new Thread(st);
  17. t1.start();
  18. t2.start();
  19. int num = 0;
  20. while(true) {
  21. if(num++ == 60) {
  22. st.changeFlag();
  23. break;
  24. }
  25. System.out.println(Thread.currentThread().getName()+"........."+num);
  26. }
  27. System.out.println("over");
  28. }
  29. }

特殊情况:当线程处于了冻结状态,就不会读取到标记,那么线程就不会结束。即:

  1. class StopThread implements Runnable {
  2. private boolean flag = true;
  3. public synchronized void run() {
  4. while(flag) {
  5. try {
  6. wait();
  7. } catch(InterruptedException e) {
  8. System.out.println(Thread.currentThread().getName()+"....exception");
  9. }
  10. System.out.println(Thread.currentThread().getName()+"....run");
  11. }
  12. }
  13. public void changeFlag() {
  14. flag = false;
  15. }
  16. }
  17. class StopThreadDemo {
  18. public static void main(String[] args) {
  19. StopThread st = new StopThread();
  20. Thread t1 = new Thread(st);
  21. Thread t2 = new Thread(st);
  22. t1.start();
  23. t2.start();
  24. int num = 0;
  25. while(true) {
  26. if(num++ == 60) {
  27. st.changeFlag();
  28. break;
  29. }
  30. System.out.println(Thread.currentThread().getName()+"........."+num);
  31. }
  32. System.out.println("over");
  33. }
  34. }

当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除,强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束。Thread类中提供了该方法:interrupt()

  1. class StopThread implements Runnable {
  2. private boolean flag = true;
  3. public synchronized void run() {
  4. while(flag) {
  5. try {
  6. wait();
  7. } catch(InterruptedException e) {
  8. System.out.println(Thread.currentThread().getName()+"....exception");
  9. flag = false;
  10. }
  11. System.out.println(Thread.currentThread().getName()+"....run");
  12. }
  13. }
  14. public void changeFlag() {
  15. flag = false;
  16. }
  17. }
  18. class StopThreadDemo {
  19. public static void main(String[] args) {
  20. StopThread st = new StopThread();
  21. Thread t1 = new Thread(st);
  22. Thread t2 = new Thread(st);
  23. t1.start();
  24. t2.start();
  25. int num = 0;
  26. while(true) {
  27. if(num++ == 60) {
  28. st.changeFlag();
  29. t1.interrupt();
  30. t2.interrupt();
  31. break;
  32. }
  33. System.out.println(Thread.currentThread().getName()+"........."+num);
  34. }
  35. System.out.println("over");
  36. }
  37. }

线程类的其他方法

  1. class StopThread implements Runnable {
  2. private boolean flag = true;
  3. public synchronized void run() {
  4. while(flag) {
  5. try {
  6. wait();
  7. } catch(InterruptedException e) {
  8. System.out.println(Thread.currentThread().getName()+"....exception");
  9. flag = false;
  10. }
  11. System.out.println(Thread.currentThread().getName()+"....run");
  12. }
  13. }
  14. public void changeFlag() {
  15. flag = false;
  16. }
  17. }
  18. class StopThreadDemo {
  19. public static void main(String[] args) {
  20. StopThread st = new StopThread();
  21. Thread t1 = new Thread(st);
  22. Thread t2 = new Thread(st);
  23. t1.setDaemon(true);
  24. t2.setDaemon(true);
  25. t1.start();
  26. t2.start();
  27. int num = 0;
  28. while(true) {
  29. if(num++ == 60) {
  30. break;
  31. }
  32. System.out.println(Thread.currentThread().getName()+"........."+num);
  33. }
  34. System.out.println("over");
  35. }
  36. }
  1. class Demo implements Runnable {
  2. public void run() {
  3. for (int x = 0; x < 70; x++) {
  4. System.out.println(Thread.currentThread().toString()+"...."+x);
  5. }
  6. }
  7. }
  8. class JoinDemo {
  9. public static void main(String[] args) throws Exception {
  10. Demo d = new Demo();
  11. Thread t1 = new Thread(d);
  12. Thread t2 = new Thread(d);
  13. t1.start();
  14. // t1.setPriority(Thread.MAX_PRIORITY); //设置进程优先级,默认为5
  15. t2.start();
  16. t1.join(); // 主线程冻结
  17. for (int x = 0; x < 80; x++) {
  18. System.out.println("main...."+x);
  19. }
  20. System.out.println("over");
  21. }
  22. }
  1. class Demo implements Runnable {
  2. public void run() {
  3. for (int x = 0; x < 70; x++) {
  4. System.out.println(Thread.currentThread().toString()+"...."+x);
  5. Thread.yield(); //将CPU的执行权释放出去,作用:让线程都有平均运行的机会
  6. }
  7. }
  8. }
  9. class JoinDemo {
  10. public static void main(String[] args) throws Exception {
  11. Demo d = new Demo();
  12. Thread t1 = new Thread(d);
  13. Thread t2 = new Thread(d);
  14. t1.start();
  15. t2.start();
  16. t1.join(); // 主线程冻结
  17. for (int x = 0; x < 80; x++) {
  18. System.out.println("main...."+x);
  19. }
  20. System.out.println("over");
  21. }
  22. }

实际开发时,怎么使用多线程呢?
以下例进行讲解,假设main()里有3段循环代码运行。可用多线程来实现(代码如下):

  1. class ThreadTest1 {
  2. public static void main(String[] args) {
  3. new Thread() {
  4. public void run() {
  5. for(int x = 0; x < 100; x++) {
  6. System.out.println(Thread.currentThread().getName()+"....."+x);
  7. }
  8. }
  9. }.start();
  10. for(int x = 0; x < 100; x++) {
  11. System.out.println(Thread.currentThread().getName()+"....."+x);
  12. }
  13. Runnable r = new Runnable() {
  14. public void run() {
  15. for(int x = 0; x < 100; x++) {
  16. System.out.println(Thread.currentThread().getName()+"....."+x);
  17. }
  18. }
  19. };
  20. new Thread(r).start();
  21. }
  22. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注