Java虚拟机的主线程,它从启动类main()方法开始运行。用户也可以创建自己的线程,它将和主线程并发运行。创建线程的两种方式:
(1)继承java.lang.Thread类;
(2)实现Runnable接口。
1.扩展java.lang.Thread类,用户的线程只需要继承Thread类,覆盖Thread类的run()方法即可。
实例:run()方法指定这个线程所执行的代码。
public class Machine extends Thread { //重写Thread类的run()方法 public void run(){ for(int a=0;a<50;a++){ System.out.println(currentThread().getName()+":"+a); } try { sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void main(String[] args) { Machine m=new Machine(); //创建第一个machine对象 Machine m1=new Machine(); //创建第二个machine对象 m.start(); //启动第一个machine线程,因为Machine类继承Thread类,重写了run()方法 m1.start(); //启动第二个machine线程
m.run(); //主线程执行第一个Machine对象的run()方法 } }
1.1 上面,主线程执行main()方法时,会创建两个Machine对象,然后启动两个Machine线程。接着主线程开始执行第一个Machine对象的run()方法。在Java虚拟机中有三个线程并发执行Machine对象的run()方法。三个线程各自的方法栈中都有代表run()方法的栈帧,在这个帧中存放局部变量a,可见(3)每个线程都拥有自己的局部变量a,它们都从0增加到50。
run()方法中currentThread().getName()相当于:
Thread thread=new Thread(); //返回当前正在执行这行代码的线程的引用 String name=thread.getName(); //获得线程的名字
线程默认为Thread-0 Thread-1可以通过Thread类的setName()方法显式设置线程的名字。
1.2 多个线程共享同一个对象的实例变量
public class Machine extends Thread{ private int a; public void run(){ for(a=0;a<50;a++){ System.out.println(currentThread().getName()+":"+a); } try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void main(String[] args) { Machine m=new Machine(); m.start(); m.run(); } }
上面代码,主线程和Machine线程都会执行Machine对象的run()方法:主线程和Machine线程并发执行Machine对象的run()方法时,都会操纵同一个实例变量a,这两个线程轮流给变量a增加1.
public class TestOne extends Thread{ private int a; //重写Thread类的run()方法 public void run(){ for(a=0;a<30;a++){ System.out.println(currentThread().getName()+":"+a); } try { Thread.sleep(300); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void main(String[] args) { TestOne t1=new TestOne(); TestOne t2=new TestOne(); t1.start(); t2.start(); } }
以上代码,因为实例方法和静态方法的字节码都位于方法区,被所有线程共享。t1线程和t2线程分别执行t1对象和t2对象的run()方法,意味着t1执行run()方法时,会把run()方法中的变量a解析为t1对象的实例变量a,t2线程执行run()方法时,会把run()方法中的变量a解析为t2对象的实例变量a。所以,t1线程和t2线程分别操纵不同的实例变量a。各自输出0-29
1.3 不要随便覆盖Thread类的start()方法
随意覆盖,所有方法的调用都会变成主线程完成。假如一定要覆盖start()方法,那么应该先调用super.start()方法。
1.4 一个线程只能被启动一次
public static void main(String[] args) { TestOne t1=new TestOne(); TestOne t2=t1; t1.start(); t2.start(); }
如果将上面代码main部分改成这样,t2.start()会抛出java.lang.IllegalThreadStateException异常。
2.实现Runnable接口
public class Machine implements Runnable{
privat int i=0; @Override public void run() { for(i=0;i<40;i++){ System.out.println(Thread.currentThread().getName()+":"+i); } try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void main(String[] args) { Machine m=new Machine(); Thread t1=new Thread(m); Thread t2=new Thread(m); t1.start(); t2.start(); } }
如上,主线程创建了t1和t2两个线程对象。启动t1和t2线程将执行m变量所引用的Machine对象的run()方法。t1和t2共享同一个machine对象。因此执行run()方法时将操纵同一个实例变量i.