1)线程包括哪些状态,分别是什么?
1.新建状态(new) :线程对象被创建后,就进入了新建状态。例如,Thread thread = new Thread()
2.就绪状态(Runnable):也被称为“可执行状态”。线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程。例如,thread.start()。处于就绪状态的线程,随时可能被CPU调度执行
3.运行状态(Running):线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态
4.阻塞状态(Blocked): 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(01) 等待阻塞 -- 通过调用线程的wait()方法,让线程等待某工作的完成。
(02) 同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。
(03) 其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕
5.死亡状态(dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
2)实现多线程的方式
1.继承Thread类,重写run()方法
Thread本质上也是实现了Runnable接口的一个实例,它代表了一个线程的实例,并且,启动线程的唯一方法就是通过Thread类的start()方法
代码实现:
public class MyThread extends Thread { private int ticket = 10; public void run() { for (int i = 0; i < 20; i++) { if (this.ticket > 0) { System.out.println(this.getName() + " 卖票:ticket" + this.ticket--); } } } public static void main(String[] args) { MyThread thread1 = new MyThread(); MyThread thread2 = new MyThread(); MyThread thread3 = new MyThread(); thread1.start(); thread2.start(); thread3.start(); } }
运行结果:
Thread-1 卖票:ticket10 Thread-1 卖票:ticket9 Thread-1 卖票:ticket8 Thread-1 卖票:ticket7 Thread-1 卖票:ticket6 Thread-1 卖票:ticket5 Thread-1 卖票:ticket4 Thread-1 卖票:ticket3 Thread-1 卖票:ticket2 Thread-1 卖票:ticket1 Thread-0 卖票:ticket10 Thread-0 卖票:ticket9 Thread-0 卖票:ticket8 Thread-0 卖票:ticket7 Thread-2 卖票:ticket10 Thread-0 卖票:ticket6 Thread-2 卖票:ticket9 Thread-2 卖票:ticket8 Thread-2 卖票:ticket7 Thread-2 卖票:ticket6 Thread-2 卖票:ticket5 Thread-2 卖票:ticket4 Thread-2 卖票:ticket3 Thread-2 卖票:ticket2 Thread-2 卖票:ticket1 Thread-0 卖票:ticket5 Thread-0 卖票:ticket4 Thread-0 卖票:ticket3 Thread-0 卖票:ticket2 Thread-0 卖票:ticket1
2.实现Runnable接口,并实现run()方法
以下是主要步骤:
1)自定义类并实现Runnable接口,实现run()方法。
2)创建Thread对象,用实现Runnable接口的对象作为参数实例化该Thread对象
3)调用Thread的start()方法
代码实现:
public class MyThread implements Runnable { private int ticket = 10; public void run() { for (int i = 0; i < 20; i++) { if (this.ticket > 0) { System.out.println(Thread.currentThread().getName() + " 卖票:ticket" + this.ticket--); } } } public static void main(String[] args) { MyThread myThread=new MyThread(); Thread thread1=new Thread(myThread); Thread thread2=new Thread(myThread); Thread thread3=new Thread(myThread); thread1.start(); thread2.start(); thread3.start(); } }
运行结果:
Thread-0 卖票:ticket9 Thread-1 卖票:ticket10 Thread-2 卖票:ticket8 Thread-1 卖票:ticket6 Thread-0 卖票:ticket7 Thread-1 卖票:ticket4 Thread-2 卖票:ticket5 Thread-1 卖票:ticket2 Thread-0 卖票:ticket3 Thread-2 卖票:ticket1
说明:运行结果表示一共卖出了10张票,说明这三个线程共享了Runnable接口
3.实现Callable接口,重写call()方法(这种方法不常用)
Callable接口实际是属于Executor框架中的功能类...
3)run()方法和start()方法有什么区别
start()方法:作用为启动一个线程,启动后该线程处于就绪状态,而非运行状态,也就意味着这个线程可以被JVM来调度执行。
在调度过程中,JVM通过调用线程类的run()方法来完成实际的操作,当run()方法结束后,线程就会终止。
run()方法:如果直接调用线程类的run()方法,会被当做一个普通函数调用,程序中仍然只有主线程这一个线程,并不会产生新的线程!
总的来说:start()方法能够异步地调用run()方法,但是直接调用run()方法却是同步的,无法达到多线程的目的
代码示例:
public class ThreadDemo extends Thread{ @Override public void run() { System.out.println("ThreadDemo:begin"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("ThreadDemo:end"); } }
public class StartAndRunTest { /* test1调用start()方法 */ public static void test1(){ System.out.println("test1:begin"); Thread t1=new ThreadDemo(); t1.start(); System.out.println("test1:end"); } /* test2调用run()方法 */ public static void test2(){ System.out.println("test2:begin"); Thread t1=new ThreadDemo(); t1.run(); System.out.println("test2:end"); } public static void main(String[] args) { test1(); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(); test2(); } }
运行结果:
从test1的运行结果来看,线程t1是在test1方法结束后才执行的,不需要等待t1,start()运行结束后(
System.out.println("ThreadDemo:end")
)就可以执行,说明在test1中调用start()是新开启了一个线程,main线程和t1线程是异步执行的
从test2的执行结果来看,调用t1.run()是同步的调用,语句System.out.println("ThreadDemo:end")必须等t1.run()方法调用结束后
才能 执行
4)sleep()方法与wait()方法的区别
1.原理不同。sleep()方法是Thread类的静态方法,是线程用来控制自身流程的,它会使此线程暂停执行一段时间,而把执行机会让给
其他线程,等到计时时间一到,此线程会自动“苏醒”。
而wait()方法是Object类的方法,用于线程间的通信,这个方法会使当前拥有该对象锁的进程等待,wait()使用notify或者notifyAlll或者指定睡眠时间来唤醒当前的线程。
2.对锁的处理机制不同。调用sleep()方法不会释放锁,而调用wait()方法会释放它所占用的锁,从而使线程所在对象中的其他synchronized数据可被别的线程使用。
3.使用区域不同。由于wait()方法的特殊意义,因此它必须放在同步控制方法或者同步语句块中使用,而sleep()方法则可以放在任何地方使用
还有就是sleep()方法必须捕获异常,而wait()方法不需要