进程:是一个正在执行中的程序。
每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元。扩展:其实更细节说明jvm,jvm启动不止一个线程,还有负责垃圾回收机制的线程。
如何在自定义的代码中,自定义一个线程呢?
通过对api的查找,java已经提供了对线程这类事物的描述。就Thread类。
一、创建线程的第一种方式:继承Thread类。
步骤:
1,定义类继承Thread。
2,复写Thread类中的run方法。
目的:将自定义代码存储在run方法。让线程运行。
3,调用线程的start方法,
该方法两个作用:启动线程,调用run方法。
class Test { public static void main(String[] args) { Demo d1=new Demo(); Demo d2=new Demo(); Demo d3=new Demo("3333333"); d1.start(); d2.setName("222222"); d2.start(); d3.start(); } } class Demo extends Thread { Demo(){} Demo(String name) { super(name); } public void run() { System.out.println(this.currentThread()+"....."+this.getName()+"......"+this.toString()+"........"); } }结果是:
Thread[Thread-0,5,main].....Thread-0......Thread[Thread-0,5,main]........
Thread[3333333,5,main].....3333333......Thread[3333333,5,main]........
Thread[222222,5,main].....222222......Thread[222222,5,main]........
二、创建线程的第二种方式:实现Runable接口
步骤:
1,定义类实现Runnable接口
2,覆盖Runnable接口中的run方法。将线程要运行的代码存放在该run方法中。
3,通过Thread类建立线程对象。
4,将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
为什么要将Runnable接口的子类对象传递给Thread的构造函数。
因为,自定义的run方法所属的对象是Runnable接口的子类对象。
所以要让线程去指定指定对象的run方法。就必须明确该run方法所属对象。
5,调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
class Test { public static void main(String[] args) { Demo d=new Demo(); Thread d1=new Thread(d); Thread d2=new Thread(d,"22222222"); d1.start(); d2.start(); } } class Demo implements Runnable { public void run() { System.out.println(Thread.currentThread()+"....."+Thread.currentThread().getName()+"......"+Thread.currentThread().toString()+"........"); } }结果是:
Thread[Thread-0,5,main].....Thread-0......Thread[Thread-0,5,main]........
Thread[22222222,5,main].....22222222......Thread[22222222,5,main]........
实现方式和继承方式有什么区别呢?
实现方式好处:
避免了单继承的局限性。
在定义线程时,建立使用实现方式。
两种方式区别:
继承Thread:线程代码存放Thread子类run方法中。
实现Runnable,线程代码存在接口的子类的run方法。
线程状态类型
1. 新建状态(New):新创建了一个线程对象。
2. 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
3. 运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
4. 阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。
(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5. 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
线程基本控制方法:
sleep():使程序暂停一段时间。sleep()使当前线程进入阻塞状态,在指定时间内不会执行。注意该方法要捕捉异常。
yield():该方法与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只能让同优先级的线程有执行的机会。
join():使调用该方法的线程在此之前执行完毕,也就是等待该方法的线程执行完毕后再往下继续执行。注意该方法也需要捕捉异常。如:t.join();//主要用于等待t线程运行结束,若无此句,main则会执行完毕,导致结果不可预测。
wait():使当前线程处于等待状态,直到另一个线程调用notify(),notifyAll()方法为止。
notify():唤醒在同步监听器上处于等待状态的单个线程,如果有多个线程在等待,则任意唤醒一个线程。
notifyAll():唤醒在同步监听器上处于等待状态的所有线程。
这三个方法都是java.lang.Object的方法。
interrupt() :中断线程。强制让线程恢复到运行状态中来。
currentThread():返回对当前正在执行的线程对象的引用。
isAlive() :测试线程是否处于活动状态
toString() :返回该线程的字符串表示形式,包括线程名称、优先级和线程组。
线程组:谁开启的线程,这个线程就属于谁的组。ThreadGroup类中可查阅
join():当A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行。
setDaemon():将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。该方法必须在启动线程前调用。
开发中多线程常用的方式:
class ThreadTest { public static void main(String[] args) { //1111111111111111111111 new Thread() { public void run() { for(int x=0; x<100; x++) { System.out.println(Thread.currentThread().getName()+"....."+x); } } }.start(); //2222222222222222222222 for(int x=0; x<100; x++) { System.out.println(Thread.currentThread().getName()+"....."+x); } //33333333333333333333333 Runnable r = new Runnable() { public void run() { for(int x=0; x<100; x++) { System.out.println(Thread.currentThread().getName()+"....."+x); } } }; new Thread(r).start(); //44444444444444444444444 new Test1().start(); } } class Test1 extends Thread { public void run() { for(int x=0; x<100; x++) { System.out.println(Thread.currentThread().getName()+"....."+x); } } }