1.1什么是多线程编程
多线程编程就是以线程为基本抽象单位的一种编程范式,和面向对象编程是可以相容的,事实上Java平台中的一个线程就是一个对象。多线程编程不是线程越多越好,就像“和尚挑水”的故事一样。
1.2为什么使用多线程
提高程序运行效率。
1.3线程的创建、启动和运行
java.lang.Thread就是java平台对线程的实现。Thread类的两个常用构造器是:Thread()和Thread(Runnable target)。1.使用第一种构造器。创建Thread类的实现子类,即继承Thread类的类。2.使用第二种构造器。创建Runnable接口的实例,即实现Runnable接口的类。
1.3.1 继承Thread类
1 public class demo{ 2 public static void main(String[] args) { 3 Thread thread = new myDemo(); // 创建 4 thread.start(); //启动 5 System.err.println("thread1"+Thread.currentThread().getName()); 6 7 } 8 9 class myDemo extends Thread{ 10 11 @Override 12 public void run() { // 运行 13 System.err.println("thread2"+Thread.currentThread().getName()); 14 } 15 } 16 }
1.3.2实现Runnable接口
1 public class demo{ 2 3 public static void main(String[] args) { 4 Thread thread = new Thread(new myDemo()); // 创建 5 thread.start(); // 启动 6 System.err.println("thread1"+Thread.currentThread().getName()); 7 8 } 9 10 class myDemo implements Runnable{ 11 12 @Override 13 public void run() { // 运行 14 System.err.println("thread2"+Thread.currentThread().getName()); 15 } 16 } 17 18 }
1.3.3 注意
1.线程属于“一次性用品”,不能多次调用同一线程的start方法,否则抛出 java.lang.IllegalThreadStateException异常
1 public class demo{ 2 3 public static void main(String[] args) { 4 Thread thread = new Thread(new myDemo()); // 创建 5 thread.start(); // 启动 thread.start(); // 启动 thread.start(); // 启动 6 System.err.println("thread1"+Thread.currentThread().getName()); 7 8 } 9 10 class myDemo implements Runnable{ 11 12 @Override 13 public void run() { // 运行 14 System.err.println("thread2"+Thread.currentThread().getName()); 15 } 16 } 17 18 }
2.不建议直接调用run方法,线程的run方法是由Java虚拟机直接调用的,如果在main方法中直接调用,运行是可以的,但是违背创建线程的初衷,线程的运行仍是mian线程。
1.4线程创建的区别
1.4.1 创建一个线程与创建其他类型的Java对象的不同。
创建线程对象比创建其他类型的对象成本要高。这是因为Java虚拟机会为每个线程分配调用栈(Call Stack)所需的内存空间,调用栈用于跟踪Java代码方法间的调用关系。第二是因为每个线程可能有一个内核线程与之对应,与java虚拟机的实现有关。
1.4.2 线程两种创建方式的区别
1.一种是基于继承的技术,创建Thread类的子类。另一种是基于组合的技术,以Runnable接口实例为构造器参数,创建Thread实例。基于组合相对于继承来说,类与类之间的耦合性更低,因此更灵活。
2.从对象共享的角度来看,Runnable实例当作为多个线程的共享实例时,会出现竞态和线程安全问题
1 public class demo { 2 3 public static void main(String[] args) { 4 Thread t; 5 CountingTask ct = new CountingTask(); //创建Runnable实例 6 // 获取处理器个数 7 final int numberofProceesors = Runtime.getRuntime().availableProcessors(); 8 System.err.println("获取处理器个数"+numberofProceesors); 9 for(int i=0; i<2*numberofProceesors; i++){ 10 //直接创建线程 11 t = new Thread(ct); //实例被共享 12 t.start(); 13 } 14 15 for(int i=0;i<2*numberofProceesors; i++){ 16 //以子类方式创建线程 17 t = new CountingThread(); 18 t.start(); 19 } 20 } 21 22 static class Counter{ 23 private int count = 0; 24 public void increment(){ 25 count++; 26 } 27 public int value(){ 28 return count; 29 } 30 } 31 32 static class CountingTask implements Runnable{ 33 34 private Counter counter = new Counter(); 35 @Override 36 public void run() { 37 for(int i=0;i<10;i++){ 38 doSomething(); 39 counter.increment(); 40 } 41 System.err.println("Runnable"+counter.value()); 42 } 43 44 } 45 static class CountingThread extends Thread{ 46 private Counter counter = new Counter(); 47 @Override 48 public void run() { 49 for(int i=0;i<10;i++){ 50 doSomething(); 51 counter.increment(); 52 } 53 System.err.println("Thread"+counter.value()); 54 } 55 } 56 static void doSomething(){ 57 try { 58 Thread.currentThread().sleep(1000); 59 } catch (InterruptedException e) { 60 // TODO Auto-generated catch block 61 e.printStackTrace(); 62 } 63 } 64 }
观察结果:
1 获取处理器个数4 2 Runnable64 3 Runnable67 4 Runnable69 5 Runnable68 6 Runnable67 7 Runnable65 8 Runnable64 9 Runnable64 10 Thread10 11 Thread10 12 Thread10 13 Thread10 14 Thread10 15 Thread10 16 Thread10 17 Thread10
3.从对象创建成本来看,创建一个线程实例要比创建一个普通的Runnable实例更昂贵。我的理解是new Thread( new Runnable()) = new Thread(),意思是一个线程实例(继承Thread)new Thread()的成本,等于实现Runnable接口方式下创建一个线程new Thread(参数)加 一个Runnable实例 ,类似于1+1=2,1<2的问题。