一、线程的实现方式
方式一:继承Thread类
一个类只要继承了Thread类,并重写run()方法,则就可以实现多线程的操作。
public class ThreadDemo01 { public static void main(String[] args) { Thread1 thread1 = new Thread1("thread1"); Thread2 thread2 = new Thread2("thread2"); System.out.println(thread1.getName()); System.out.println(thread2.getName()); thread1.start();//启动线程 thread2.start(); } } class Thread1 extends Thread { public Thread1(String name) { super(name); } @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("Thread1 : " + i); } } } class Thread2 extends Thread { public Thread2(String name) { super(name); } @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("Thread2 : " + i); } } }
方式二:实现Runnable接口
一个类只要实现了Runnable类,并重写run()方法,则就可以实现多线程的操作。
public class ThreadDemo03 { public static void main(String[] args) { Thread thread1 = new Thread(new MyThread1(), "thread1"); Thread thread2 = new Thread(new MyThread2(), "thread2"); System.out.println(thread1.getName()); System.out.println(thread2.getName()); thread1.start();//启动线程 thread2.start(); } } class MyThread1 implements Runnable { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("MyThread1 :" + i); } } } class MyThread2 implements Runnable { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("MyThread2 :" + i); } } }
两种启动方式的比较:
将我们希望线程执行的代码放到run方法中,然后通过start方法来启动线程,start方法首先为线程的执行准备好系统资源,然后再去调用run方法;
两种方法均需执行线程的start方法为线程分配必须的系统资源,调度线程运行并执行线程的run方法;在使用的时候使用实现接口优先(避免单继承);实现Runnable接口的方式能够实现资源的共享。
二、Thread的JDK源码分析
Thread thread1 = new Thread(new MyThread1(), "thread1");
Thread 构造方法源码如下:
public Thread(String name) { init(null, null, name, 0); } public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0); } public Thread(Runnable target) { init(null, target, "Thread-" + nextThreadNum(), 0); } public Thread(Runnable target, String name) { init(null, target, name, 0); }
private void init(ThreadGroup g, Runnable target, String name, long stackSize) { ….. this.name = name.toCharArray(); this.target = target; ….. }
private static int threadInitNumber; private static synchronized int nextThreadNum() { return threadInitNumber++; }
public void run() { if (target != null) { target.run(); } }
1)当生成一个线程对象时,如果没有为其设定名字,那么线程对象的名字将使用如下形式:Thread-number,该number将是自动增加的,并被所有的Thread对象所共享(因为它是static的成员变量);
2)当使用继承Thread类来生成线程对象时,我们需要重写run()方法,因为Thread类的run()方法此时什么事情都没做(target==null);
3)当使用实现Runnable接口来生成线程对象时,我们需要实现Runnable接口的run()方法,然后使用new Thread(new MyThread())(假使MyThread已经实现了Runnable接口)来生成线程对象,这时的线程对象的run()方法就会调用MyThread类的run方法,这样我们自己编写的run()方法就执行了。
三、线程中成员变量和局部变量的使用
1)如果一个变量是成员变量,那么多个线程对同一个对象的成员变量进行操作时,他们对该成员变量是彼此影响的(也就是说一个线程对成员变量的改变会影响到另一个线程)。
public class ThreadDemo04 { public static void main(String[] args) { HelloThread r = new HelloThread(); Thread thread1 = new Thread(r); Thread thread2 = new Thread(r); thread1.start(); thread2.start(); } } class HelloThread implements Runnable { int i; //成员变量 @Override public void run() { while (true) { System.out.println("number: " + this.i++); try { Thread.sleep((long) (Math.random() * 1000)); } catch (InterruptedException e) { e.printStackTrace(); } if (10 == this.i) { break; } } } }
结果是:
number: 0 number: 1 number: 2 number: 3 number: 4 number: 5 number: 6 number: 7 number: 8 number: 9
2)如果一个变量是局部变量,那么每个线程都会有一个该局部变量的拷贝,一个线程对该局部变量的改变不会影响到其他的线程。
public class ThreadDemo05 { public static void main(String[] args) { WorldThread r = new WorldThread(); Thread thread1 = new Thread(r); Thread thread2 = new Thread(r); thread1.start(); thread2.start(); } } class WorldThread implements Runnable { @Override public void run() { int i = 0; while (true) { System.out.println("number: " + i++); try { Thread.sleep((long) (Math.random() * 1000)); } catch (InterruptedException e) { e.printStackTrace(); } if (10 == i) { break; } } } }
结果是:
number: 0 number: 0 number: 1 number: 1 ……
四、卖票示例
50张票,3个窗口买,每个窗口相当于一个线程。
使用继承Thread类的方式卖票:
class MyThread1 extends Thread { private Integer num = 50; public MyThread1(String name) { super(name); } public void run() { int count = 0; for (int i = 1; i <= 200; i++) { if(num > 0){ count ++; System.out.println(getName() + "卖出第--->" + num-- +"张票"); } } System.err.println(getName()+"卖了"+ count +"张票" ); } }
getName()方法来自于Thread类中,故可以直接获得。
使用实现Runnable的方式卖票:
class MyThread2 implements Runnable{ private Integer num = 50; public void run() { int count = 0; for (int i = 0; i < 200 ; i++) { if(num > 0){ count ++; System.out.println(Thread.currentThread().getName() + "卖出第--->" + num-- +"张票"); } } System.err.println(Thread.currentThread().getName() + "卖了"+ count +"张票" ); } }
由于该类实现了Runnable接口,该接口中并没有获取线程方法名称的方法,故只能采用Thread.currentThread().getName()这种最通用的方法来获得正在执行的线程名称。
两种卖票方式的比较:
继承方式:资源不能共享;一共卖出去150张票,每个窗口各50张;由于继承了Thread类之后就不能再继承其他类了;
接口方式:资源共享;一共卖出去50张票;方便以后扩展。