1. 传统线程技术回顾
继承线程与实现Runnable的差异?为什么那么多人都采取第二种方式?
因为第二种方式更符合面向对象的思维方式。创建一个线程,线程要运行代码,而运行的代码都封装到一个独立的对象中去。一个叫线程,一个叫线程运行的代码,这是两个东西。两个东西一组合,就表现出了面向对象的思维。如果两个线程实现数据共享,必须用Runnable的方式。
查看Thread类的run()方法的源代码,可以看到其实这两种方式都是在调用Thread对象的run方法,如果Thread类的run方法没有被覆盖,并且为该Thread对象设置了一个Runnable对象,该run方法会调用Runnable对象的run方法。
问题:如果在Thread子类覆盖的run方法中编写了运行代码,也为Thread子类对象传递了一个Runnable对象,那么,线程运行时的执行代码是子类的run方法的代码?还是Runnable对象的run方法的代码?子类的run方法。
示例代码
1 new Thread(new Runnable() { 2 public void run() { 3 while (true) { 4 System.out.println("run:runnable"); 5 } 6 } 7 }) { 8 public void run() { 9 while (true) { 10 try { 11 Thread.sleep(1000); 12 } catch (Exception e) { 13 } 14 System.out.println("run:thread"); 15 } 16 } 17 }.start();
该线程会运行重写的Thread中的run方法,而不是Runnable中的run方法,因为在传统的Thread的run方法是:
1 public void run() { 2 if (target != null) { 3 target.run(); 4 } 5 }
如果想要运行Runnable中的run方法,必须在Thread中调用,但是此时我重写了Thread中的run方法,导致if (target != null) { target.run(); }不存在,所以调用不了Runnable中的run方法。
注意:多线程的执行,会提高程序的运行效率吗?为什么会有多线程下载?
不会,有时候还会降低程序的运行效率。因为CPU只有一个,在CPU上下文切换的时候,可能还会耽误时间。
多线程下载:其实你的机器没有变快,而是你抢了服务器的带宽。如果你一个人下载,服务器给你提供的是20K的话,那么10个人的话,服务器提供的就是200K。这个时候你抢走200k,所以感觉变快了。
2. 传统定时器技术回顾(jdk1.5以前)Timer、TimerTask
Timer:一种工具,线程用其安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。
scheduleAtFixedRate(TimerTask task, Date firstTime, long period):
安排指定的任务在指定的时间开始进行重复的固定速率执行。
schedule(TimerTask task, long delay, long period): 安排指定的任务从指定的延迟后开始进行重复的固定延迟执行。
作业调度框架 Quartz(专门用来处理时间时间的工具)。你能够用它来为执行一个作业而创建简单的或复杂的调度。{ http://www.oschina.net/p/quartz}
问题:每天早晨3点来送报纸。
问题:每个星期周一到周五上班,周六道周日不上班。
示例代码:(间隔不同时间,执行不同事件)
1 package com.chunjiangchao.thread; 2 3 import java.util.Timer; 4 import java.util.TimerTask; 5 /** 6 * 重复执行某项任务,但是时间间隔性不同是2,4这种状态 7 * @author chunjiangchao 8 * 9 */ 10 public class TimerDemo02 { 11 12 private static long count = 1; 13 public static void main(String[] args) { 14 Timer timer = new Timer(); 15 timer.schedule(new Task(), 1000); 16 /* 17 测试打印结果如下: 18 执行任务,当前时间为:1460613231 19 执行任务,当前时间为:1460613235 20 执行任务,当前时间为:1460613237 21 执行任务,当前时间为:1460613241 22 */ 23 24 new Thread(new Runnable() { 25 public void run() { 26 while (true) { 27 System.out.println("run:runnable"); 28 } 29 } 30 }) { 31 public void run() { 32 while (true) { 33 try { 34 Thread.sleep(1000); 35 } catch (Exception e) { 36 } 37 System.out.println("run:thread"); 38 } 39 } 40 }.start(); 41 42 43 } 44 45 static class Task extends TimerTask{ 46 47 @Override 48 public void run() { 49 System.out.println("执行任务,当前时间为:"+System.currentTimeMillis()/1000); 50 new Timer().schedule(new Task(), 2000*(1+count%2)); 51 count++; 52 } 53 54 } 55 56 }
3. 传统线程互斥技术
本道例题:关键在于说明:要想实现线程间的互斥,线程数量必须达到两个或者两个以上,同时,访问资源的时候要用同一把锁(这个是必须的)。如果两个线程都访问不同的同步代码块,而且它们的锁对象都不相同,那么这些线程就没有达到同步的目的。
示例代码:(访问同一个资源对象,但是锁对象不同,同样没有达到同步的目的)
1 package com.chunjiangchao.thread; 2 /** 3 * 线程间同步与互斥 4 * @author chunjiangchao 5 * 因为print1方法与print3方法锁对象相同,所以在调用的时候,会产生互斥的现象,而print2的锁是当前正在执行对象print2方法的对象, 6 * 与print1和print3同时执行,打印结果就不是期望的结果 7 * 8 */ 9 public class TraditionalThreadSynchronizedDemo { 10 11 public static void main(String[] args) { 12 final MyPrint myPrint = new MyPrint(); 13 //A 14 new Thread(new Runnable() { 15 16 @Override 17 public void run() { 18 myPrint.print1("chunjiangchao"); 19 } 20 }).start(); 21 //B 22 // new Thread(new Runnable() { 23 // 24 // @Override 25 // public void run() { 26 // myPrint.print2("fengbianyun"); 27 // } 28 // }).start(); 29 //C 30 new Thread(new Runnable() { 31 32 @Override 33 public void run() { 34 myPrint.print3("liushaoyue"); 35 } 36 }).start(); 37 } 38 static class MyPrint{ 39 public void print1(String str){ 40 synchronized (MyPrint.class) { 41 for(char c :str.toCharArray()){ 42 System.out.print(c); 43 pSleep(200); 44 } 45 System.out.println("print1当前已经打印完毕"); 46 } 47 48 } 49 public synchronized void print2(String str){ 50 for(char c :str.toCharArray()){ 51 System.out.print(c); 52 pSleep(200); 53 } 54 System.out.println("print2当前已经打印完毕"); 55 } 56 public synchronized static void print3(String str){ 57 for(char c :str.toCharArray()){ 58 System.out.print(c); 59 pSleep(200); 60 } 61 System.out.println("print3当前已经打印完毕"); 62 } 63 private static void pSleep(long time){ 64 try { 65 Thread.sleep(time); 66 } catch (InterruptedException e) { 67 e.printStackTrace(); 68 } 69 } 70 71 } 72 73 }