从今天起我们来看一下Android中的多线程的知识,Android入门容易,但是要完成一个完善的产品却不容易,让我们从线程开始一步步深入Android内部。
一、线程基础回顾
package com.maso.test; public class TraditionalThread { public static void main(String[] args) { /* * 线程的第一种创建方式 */ Thread thread1 = new Thread(){ @Override public void run() { try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } while(true){ System.out.println(Thread.currentThread().getName()); } } }; thread1.start(); /* *线程的第二种创建方式 */ Thread thread2 = new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } while (true) { System.out.println(Thread.currentThread().getName()); } } }); thread2.start(); /* * 线程的调用优先级 */ new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } while(true){ System.out.println("Runnable"); } } }){ public void run() { try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } while(true){ System.out.println("Thread"); } }; }.start(); } }上面代码中是我们都很熟悉的线程的两种创建方式,如果对这些还感到陌生请先看Java线程基础。
打开Thread类的源码可以看到Thread类有8个构造函数,我们先看看上面的两种构造函数的源码。
public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0); }在构造的时候直接调用了init方法
private void init(ThreadGroup g, Runnable target, String name, long stackSize) { if (name == null) { throw new NullPointerException("name cannot be null"); } Thread parent = currentThread(); SecurityManager security = System.getSecurityManager(); if (g == null) { /* Determine if it's an applet or not */ /* If there is a security manager, ask the security manager what to do. */ if (security != null) { g = security.getThreadGroup(); } /* If the security doesn't have a strong opinion of the matter use the parent thread group. */ if (g == null) { g = parent.getThreadGroup(); } } /* checkAccess regardless of whether or not threadgroup is explicitly passed in. */ g.checkAccess(); /* * Do we have the required permissions? */ if (security != null) { if (isCCLOverridden(getClass())) { security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); } } g.addUnstarted(); this.group = g; this.daemon = parent.isDaemon(); this.priority = parent.getPriority(); this.name = name.toCharArray(); if (security == null || isCCLOverridden(parent.getClass())) this.contextClassLoader = parent.getContextClassLoader(); else this.contextClassLoader = parent.contextClassLoader; this.inheritedAccessControlContext = AccessController.getContext(); this.target = target; setPriority(priority); if (parent.inheritableThreadLocals != null) this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); /* Stash the specified stack size in case the VM cares */ this.stackSize = stackSize; /* Set thread ID */ tid = nextThreadID(); }里面的东西比较多,但是我们可以看到会初始化一个变量Runnable target;
下面我们再来看看run方法中是个什么东东?
@Override public void run() { if (target != null) { target.run(); } }原来run方法中会先判断是否初始化了Runnable target变量,如果没有则空实现,如果target不为空则先执行Runnable接口中的run方法。有的朋友可能会猜想下面的代码会先调用Runnable接口中的run方法,然后才调用Thread实现类中的run方法。
/* * 线程的调用优先级 */ new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } while(true){ System.out.println("Runnable"); } } }){ public void run() { try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } while(true){ System.out.println("Thread"); } }; }.start();其实事实并非如此,因为上面代码中是一个匿名内部类,实际上是一种从Thread的继承和实现,所以下面的run方法覆盖了Thread中的run方法,所以Runnable中的run方法根本不会执行。
下面再看看Runnable接口的源代码
public interface Runnable { /** * When an object implementing interface <code>Runnable</code> is used * to create a thread, starting the thread causes the object's * <code>run</code> method to be called in that separately executing * thread. * <p> * The general contract of the method <code>run</code> is that it may * take any action whatsoever. * * @see java.lang.Thread#run() */ public abstract void run(); }发现Runnable接口只有一个抽象的run方法。
为什么要搞一个Runnable接口来实现多线程呢?从Thread继承不是更方便吗?Runnable接口有如下优势,所以我们常常会选择实现Runnable接口:
1、适合多个程序代码的线程去处理同一个资源。
public class ThreadTest1 extends Thread { private int count = 5; public void run() { for (int i = 0; i < 7; i++) { if (count > 0) { System.out.println("count= " + count--); } } } public static void main(String[] args) { //这样实际上是创建了三个互不影响的线程实例 ThreadTest1 t1 = new ThreadTest1(); ThreadTest1 t2 = new ThreadTest1(); ThreadTest1 t3 = new ThreadTest1(); t1.start(); t2.start(); t3.start(); } }
public class ThreadTest1{ public static void main(String [] args) { MyThread my = new MyThread(); //开启了三个线程,但是操作的是同一个run方法 new Thread(my, "1号窗口").start(); new Thread(my, "2号窗口").start(); new Thread(my, "3号窗口").start(); } } class MyThread implements Runnable{ private int ticket = 5; //5张票 public void run() { for (int i=0; i<=20; i++) { if (this.ticket > 0) { System.out.println(Thread.currentThread().getName()+ "正在卖票"+this.ticket--); } } } }
2、避免Java特性中的单根继承的限制。
3、可以保持代码和数据的分离(创建线程数和数据无关)。
4、更能体现Java面向对象的设计特点。