以前学习基础的时候学习了一段时间的多线程,上课的时候老师也讲过一遍,那时候感觉学的似懂非懂。因为到现在很长一段时间没有用到多线程的知识,所以现在基本上忘了差不多了。但是下个星期要面试了,所以今天特意又研究了一下多线程,免得被问到多线程问题时什么都不记得了那就麻烦了。现在对java比较熟悉了,所以再一次学习多线程知识,感觉没有那么难了(记得刚接触多线程的时候,感觉非常吃力)。
首先讲一下进程和线程的区别:
进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程。
线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。
线程和进程一样分为五个阶段:创建、就绪、运行、阻塞、终止。
多进程是指操作系统能同时运行多个任务(程序)。
多线程是指在同一程序中有多个顺序流在执行。
在java中创建一个线程有两种方法:
①实现java.lang.Runnable接口,重写run()方法,启动:new Thread(this).start()。
1 package com.thread; 2 3 public class ThreadTest1 { 4 public static void main(String[] args) { 5 Runnable1 r = new Runnable1(); 6 //r.run();并不是线程开启,而是简单的方法调用 7 Thread t = new Thread(r);//创建线程 8 //t.run(); //如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。 9 t.start(); //线程开启 10 for (int i = 0; i < 100; i++) { 11 System.out.println("main:"+i); 12 } 13 } 14 } 15 class Runnable1 implements Runnable{ 16 public void run() { 17 for (int i = 0; i < 100; i++) { 18 System.out.println("Thread-----:"+i); 19 } 20 } 21 }
要注意的是:
1.r.run()并不是启动线程,而是简单的方法调用。
2.Thread也有run()方法,如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。
3.并不是一启动线程(调用start()方法)就执行这个线程,而是进入就绪状态,什么时候运行要看CUP。
②继承java.lang.Thread类,重写run()方法。
1 package com.thread; 2 3 public class TestThread2 { 4 public static void main(String[] args) { 5 Thread1 t = new Thread1(); 6 //t.run(); //这里也不能直接调用方法 7 t.start(); 8 for (int i = 0; i < 100; i++) { 9 System.out.println("main:"+i); 10 } 11 } 12 } 13 14 //尽量使用实现Runnnable接口,因为接口比较灵活 15 class Thread1 extends Thread{ 16 @Override 17 public void run() { 18 for (int i = 0; i < 100; i++) { 19 System.out.println("Thread-----:"+i); 20 } 21 } 22 }
虽然两种方法都可行,但是最好还是用第一种方法,因为使用接口灵活性好,java中时单继承、多实现。
Thread类中常用的方法有:
①sleep(long millis): 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)。
1 package com.thread; 2 import java.util.Date; 3 /** 4 * sleep()指在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)。 5 * @author Administrator 6 * 7 */ 8 public class SleepTest { 9 public static void main(String[] args) { 10 Thread2 t = new Thread2(); 11 t.start(); 12 try { 13 Thread.sleep(10000); //主线程睡眠10秒钟 14 } catch (InterruptedException e) { 15 e.printStackTrace(); 16 } 17 //主线程睡眠10秒钟后结束t线程 18 //t.interrupt(); //这种结束方式比较粗暴,如果t线程打开了某个资源还没来得及关闭也就是run方法还没有执行完就强制结束线程,会导致资源无法关闭 19 //t.stop();也是结束某个线程,这种方式比interrupt()更粗暴 20 t.flag = false; 21 } 22 } 23 class Thread2 extends Thread{ 24 boolean flag = true; //用这种方式结束线程很不错,用一个变量控制run方法什么时候不再执行,不会出现run方法没有执行完毕就结束 25 @Override 26 public void run() { //run方法一结束,整个线程就终止了 27 while(flag){ 28 System.out.println("---"+new Date()+"---"); 29 try { 30 sleep(1000); 31 } catch (InterruptedException e) { 32 return; 33 } 34 } 35 } 36 }
②join():指等待t线程终止。也可以理解为将t线程合并到当前线程来,等待t线程结束后再往下执行。相当于方法调用
1 package com.thread; 2 3 import java.util.Date; 4 5 /* 6 * t.join()方法指等待t线程终止。也可以理解为将t线程合并到当前线程来,等待t线程结束后再往下执行。相当于方法调用 7 */ 8 public class TestJoin { 9 public static void main(String[] args) { 10 Thread t = new Thread3("abc"); 11 t.start(); 12 for (int i = 0; i < 20; i++) { 13 System.out.println("我是main线程"); 14 if(i==10){ 15 try { 16 t.join(); 17 } catch (InterruptedException e1) { 18 // TODO Auto-generated catch block 19 e1.printStackTrace(); 20 } 21 } 22 try { 23 Thread.sleep(1000); 24 } catch (InterruptedException e) { 25 e.printStackTrace(); 26 } 27 } 28 } 29 } 30 class Thread3 extends Thread{ 31 public Thread3(String s) { //给该线程取一个名字,用getName()方法可以去到该名字 32 super(s); 33 } 34 @Override 35 public void run() { 36 for (int i = 0; i < 20; i++) { 37 System.out.println("我是"+getName()+"线程"); 38 try { 39 sleep(1000); 40 } catch (InterruptedException e) { 41 e.printStackTrace(); 42 } 43 } 44 } 45 }
运行该程序结果为:
1 我是main线程 2 我是abc线程 3 我是main线程 4 我是abc线程 5 我是main线程 6 我是abc线程 7 我是main线程 8 我是abc线程 9 我是main线程 10 我是abc线程 11 我是main线程 12 我是abc线程 13 我是main线程 14 我是abc线程 15 我是main线程 16 我是abc线程 17 我是main线程 18 我是abc线程 19 我是main线程 20 我是abc线程 21 我是main线程 22 我是abc线程 23 我是abc线程 24 我是abc线程 25 我是abc线程 26 我是abc线程 27 我是abc线程 28 我是abc线程 29 我是abc线程 30 我是abc线程 31 我是abc线程 32 我是main线程 33 我是main线程 34 我是main线程 35 我是main线程 36 我是main线程 37 我是main线程 38 我是main线程 39 我是main线程 40 我是main线程
可以看到从第22行起就开始顺序执行了,因为i=10的时候就将该形成合并了。
③yield():暂停当前正在执行的线程对象,并执行其他线程。
④setPriority(): 更改线程的优先级。
MIN_PRIORITY = 1
NORM_PRIORITY = 5
MAX_PRIORITY = 10
1 package com.thread; 2 3 4 /*t.yield()暂停当前正在执行的线程对象,并执行其他线程。 5 * 6 * MIN_PRIORITY 1 7 * NORM_PRIORITY 5 8 * MAX_PRIORITY 10 9 */ 10 public class TestYield { 11 public static void main(String[] args) { 12 Thread4 t1 = new Thread4("t1"); 13 Thread4 t2 = new Thread4("t2"); 14 t1.setPriority(Thread.MAX_PRIORITY); 15 t2.setPriority(Thread.MIN_PRIORITY); 16 System.out.println(t1.getPriority()); 17 System.out.println(t2.getPriority()); 18 t1.start(); 19 t2.start(); 20 21 } 22 } 23 class Thread4 extends Thread{ 24 public Thread4(String s) { 25 super(s); 26 } 27 @Override 28 public void run() { 29 for (int i = 0; i < 1000; i++) { 30 System.out.println("我是"+getName()+"线程"+i); 31 if(i%10 == 0){ 32 yield(); 33 } 34 } 35 } 36 }
由于运行结果太长就没有贴上来了,运行该程序,可以看到t1和t2两个进程每次当i为10的倍数时都会让给其他线程执行。
⑤interrupt():中断某个线程,这种结束方式比较粗暴,如果t线程打开了某个资源还没来得及关闭也就是run方法还没有执行完就强制结束线程,会导致资源无法关闭
要想结束进程最好的办法就是用sleep()函数的例子程序里那样,在线程类里面用以个boolean型变量来控制run()方法什么时候结束,run()方法一结束,该线程也就结束了。
⑥还有很多的方法就不一一列举了.........
当然,多线程难点不在这些,多线程的难点在于多线程之间的协调。关于多线程的协调待续........