java多线程
一、进程与线程
要理解多线程,首先要理解线程和进程的概念。
进程:狭义定义:进程是正在运行的程序的实例(an instance of a computer program that is being executed)。广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。
线程:线程,有时被称为轻量进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。
多线程的使用可以合理使用CPU资源,但是如果线程过多会导致性能降低(比如我们平时使用电脑,出现卡的情况)。CPU处理程序是通过快速切换完成的。
多线程执行时,在栈内存中,其实每一个执行线程都有一片自己所属的栈内存空间,进行方法的压栈和弹栈。当执行线程的任务结束了,线程自动在栈内存中释放了。当所有的执行线程都结束了,进程就结束了。
三、获取线程名称:
static Thread currentThread():返回当前正在执行的线程对象的引用。
String getName():返回此线程的名称。
Thread.currentThread().getName();
主线程名称:main
自定义线程:Thread-1 线程多个时,数字顺延。
四、多线程的异常信息
多线程中发生异常时,如果没有进行捕获处理,异常抛出后异常所在的线程终止,不会影响其他线程。异常信息中首先会表明异常所在的线程,如:
1 class Demo extends Thread{ 2 private String name; 3 Demo(String name){ 4 this.name=name; 5 } 6 @Override 7 public void run() { 8 for(int i=1;i<=20;i++) { 9 int [] arr=new int[3]; 10 System.out.println(arr[i]); 11 System.out.println("name="+name+"......."+i+Thread.currentThread().getName()); 12 } 13 14 } 15 } 16 17 public class ThreadDemo { 18 public static void main(String[] args) { 19 Demo d1=new Demo("小强"); 20 Demo d2=new Demo("旺财"); 21 d2.start();//开启多一个执行路径 22 d1.run();//由主线程负责 23 24 } 25 }
抛出异常:
Exception in thread "Thread-1" Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3
at Demo.run(ThreadDemo.java:12)
at ThreadDemo.main(ThreadDemo.java:26)
java.lang.ArrayIndexOutOfBoundsException: 3
at Demo.run(ThreadDemo.java:12)
五、线程的状态
八、其他细节问题
1、sleep()和wait()异同点
相同点:都可以让线程处于冻结状态。
不同点:(1)sleep()必须指定时间;wait可以指定时间,也可以不指定。
(2)sleep()时间到,线程处于临时阻塞或者运行;wait如果没有指定时间,必须要通过notify或者 notifyAll()唤醒。
(3)sleep()不一定非要定义在同步中;wait()必须定义在同步中。
(4)都定义在同步中时,线程执行到sleep()中时,不会释放锁;线程执行到wait()会释放锁。
(5)sleep()是Thread类的方法,wait()是Object类的方法。
2、线程如何停止?
(1)stop方法已过时。替代方法:让线程任务代码执行完。run()方法通常有循环,控制住循环就可以了。 定义标记控制循环,对外提供修改标记的方法。
1 //演示停止线程 2 class Demo01 implements Runnable{ 3 private boolean flag=true; 4 @Override 5 public void run() { 6 while(flag) { 7 System.out.println(Thread.currentThread().getName()+"------->"); 8 } 9 } 10 11 public void changeFlag() { 12 flag=false; 13 } 14 } 15 16 public class StopThreadDemo { 17 public static void main(String[] args) { 18 Demo01 d=new Demo01(); 19 Thread t1=new Thread(d); 20 Thread t2=new Thread(d); 21 t1.start(); 22 t2.start(); 23 24 int x=0; 25 while(true) { 26 if(++x==50) { 27 d.changeFlag();//条件满足,让其他线程也结束 28 break;//结束主线程 29 } 30 System.out.println("main------------>"+x); 31 } 32 System.out.println("over"); 33 } 34 }
(2)万一线程处于wait状态,此时不能判断标记。
1 //演示停止线程 2 class Demo01 implements Runnable{ 3 private boolean flag=true; 4 @Override 5 public synchronized void run() { 6 while(flag) { 7 try{ 8 wait(); 9 }catch(InterruptedException e){ 10 System.out.println(Thread.currentThread().getName()+"------->"+e.toString()); 11 changeFlag(); 12 } 13 System.out.println(Thread.currentThread().getName()+"------->"); 14 } 15 } 16 17 public void changeFlag() { 18 flag=false; 19 } 20 } 21 22 public class StopThreadDemo { 23 public static void main(String[] args) { 24 Demo01 d=new Demo01(); 25 Thread t1=new Thread(d); 26 Thread t2=new Thread(d); 27 t1.start(); 28 t2.start(); 29 30 int x=0; 31 while(true) { 32 if(++x==50) { 33 d.changeFlag();//条件满足,让其他线程也结束 34 break;//结束主线程 35 } 36 System.out.println("main------------>"+x); 37 } 38 System.out.println("over"); 39 } 40 }
如果目标线程等待很长时间(例如基于一个条件变量),则应使用 interrupt
方法来中断该等待。
所谓的中断并不是停止线程,interrupt的功能是将线程的冻结状态清除,让线程恢复到运行状态(让线程重新具备CPU执行资格)。
1 //演示停止线程 2 class Demo01 implements Runnable{ 3 private boolean flag=true; 4 @Override 5 public synchronized void run() { 6 while(flag) { 7 try{ 8 wait(); 9 }catch(InterruptedException e){ 10 System.out.println(Thread.currentThread().getName()+"------->"+e.toString()); 11 changeFlag(); 12 } 13 System.out.println(Thread.currentThread().getName()+"------->"); 14 } 15 } 16 17 public void changeFlag() { 18 flag=false; 19 } 20 } 21 22 public class StopThreadDemo { 23 public static void main(String[] args) { 24 Demo01 d=new Demo01(); 25 Thread t1=new Thread(d); 26 Thread t2=new Thread(d); 27 t1.start(); 28 t2.start(); 29 30 int x=0; 31 while(true) { 32 if(++x==50) { 33 //d.changeFlag();//条件满足,让其他线程也结束 34 t1.interrupt();//强制对t1线程进行中断状态的清除,让其恢复到运行状态。 35 t2.interrupt();//强制对t2线程进行中断状态的清除,让其恢复到运行状态。 36 break;//结束主线程 37 } 38 System.out.println("main------------>"+x); 39 } 40 System.out.println("over"); 41 } 42 }
因为是强制性的,所以会有异常发生,可以在catch中捕获异常,在异常处理中,改变标记让循环结束,线程停止。
3、守护线程
也就是后台线程。一般我们创建的是前台线程。
Object类中的
public final void setDaemon(boolean on)
将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。
前后台线程运行时都是一样的,获取CPU执行权执行。
只有结束的时候有些不同。
前台线程要通过run方法结束,线程结束。
后台线程也可以通过run方法结束,线程结束,还有另外一种情况:当进程中的所有前台线程都结束了,这是后台线程无论处于什么状态,都会结束,从而进程结束。
进程结束依赖的都是前台线程。
1 //演示停止线程 2 class Demo01 implements Runnable{ 3 private boolean flag=true; 4 @Override 5 public synchronized void run() { 6 while(flag) { 7 try{ 8 wait(); 9 }catch(InterruptedException e){ 10 System.out.println(Thread.currentThread().getName()+"------->"+e.toString()); 11 changeFlag(); 12 } 13 System.out.println(Thread.currentThread().getName()+"------->"); 14 } 15 } 16 17 public void changeFlag() { 18 flag=false; 19 } 20 } 21 22 public class StopThreadDemo { 23 public static void main(String[] args) { 24 Demo01 d=new Demo01(); 25 Thread t1=new Thread(d); 26 Thread t2=new Thread(d); 27 t1.start(); 28 t2.setDaemon(true); 29 t2.start(); 30 31 int x=0; 32 while(true) { 33 if(++x==50) { 34 //d.changeFlag();//条件满足,让其他线程也结束 35 t1.interrupt();//强制对t1线程进行中断状态的清除,让其恢复到运行状态。 36 t2.interrupt();//强制对t2线程进行中断状态的清除,让其恢复到运行状态。 37 break;//结束主线程 38 } 39 System.out.println("main------------>"+x); 40 } 41 System.out.println("over"); 42 } 43 }
4、线程的优先级
每个线程都有一个优先级,高优先级线程的执行优先于低优先级线程。
线程的优先级,用数字标识,1-10,默认的初始优先级为5。
void setPriority(int new Priority);// 更改线程的优先级。
public static final int MIN_PRIORITY 线程可以具有的最低优先级。
public static final int MAX_PRIORITY 线程可以具有的最高优先级。
public static final int NORM_PRIORITY 分配给线程的默认优先级。
例如:t1.setPriority(Thread.MAX_PRIORITY);
5、线程组
ThreadGroup :可以通过Thread的构造函数明确新线程对象所属的线程组;
线程组的好处:可以对多个同组线程,进行统一的操作。
默认都属于main线程组。
6、join()方法和yield()方法
1 class Demo00 implements Runnable{ 2 @Override 3 public void run() { 4 // TODO Auto-generated method stub 5 for(int i=0;i<20;i++) { 6 System.out.println(Thread.currentThread().getName()+"-------->"+i); 7 } 8 } 9 } 10 public class JoinThreadDemo { 11 public static void main(String[] args) { 12 Demo00 d=new Demo00(); 13 Thread t1=new Thread(d,"线程1"); 14 Thread t2=new Thread(d,"线程2"); 15 16 t1.start(); 17 t2.start(); 18 //主线程执行到这里,t1加入执行,主线程释放了执行权和执行资格, 19 //并处于冻结状态,等t1线程执行完(跟线程2没有关系)。 20 try { 21 t1.join();//用于临时加入一个运算的线程。让该线程运算完,程序才会继续执行。 22 } catch (InterruptedException e) { 23 // TODO Auto-generated catch block 24 e.printStackTrace(); 25 } 26 27 for(int i=0;i<50;i++) { 28 System.out.println("main---------->"+i); 29 } 30 System.out.println("over"); 31 } 32 }
1 class Demo00 implements Runnable{ 2 @Override 3 public void run() { 4 // TODO Auto-generated method stub 5 for(int i=0;i<20;i++) { 6 System.out.println(Thread.currentThread().getName()+"-------->"+i); 7 Thread.yield();//线程临时暂停,将执行权释放,让其他线程有机会获取。 8 } 9 } 10 } 11 public class YieldThreadDemo { 12 public static void main(String[] args) { 13 Demo00 d=new Demo00(); 14 Thread t1=new Thread(d,"线程1"); 15 Thread t2=new Thread(d,"线程2"); 16 t1.start(); 17 t2.start(); 18 for(int i=0;i<50;i++) { 19 System.out.println("main---------->"+i); 20 } 21 System.out.println("over"); 22 } 23 }
7、线程的匿名内部类使用
1 public class ThreadTest { 2 public static void main(String[] args) { 3 new Thread() { 4 public void run() { 5 for(int i=0;i<20;i++) { 6 System.out.println("X....."+i); 7 } 8 } 9 }.start(); 10 11 Runnable r=new Runnable() { 12 public void run() { 13 for(int i=0;i<20;i++) { 14 System.out.println("....Y....."+i); 15 } 16 } 17 }; 18 new Thread(r).start(); 19 20 for(int i=0;i<20;i++) { 21 System.out.println("........Z....."+i); 22 } 23 } 24 }
//观察以下代码:
1 public class ThreadTest { 2 public static void main(String[] args) { 3 new Thread(new Runnable() { 4 public void run() { 5 System.out.println("Runnable run"); 6 } 7 }) { 8 public void run() { 9 System.out.println("subThread run"); 10 } 11 }.start(); 12 } 13 }
代码运行输出:subThread run
子类重写了父类方法,相当于
1 public class ThreadTest { 2 public static void main(String[] args) { 3 Runnable r=new Runnable() { 4 public void run() { 5 System.out.println("Runnable run"); 6 }}; 7 Thread t=new Thread(r) { 8 public void run() { 9 System.out.println("subThread run"); 10 } 11 }; 12 t.start(); 13 } 14 }