线程的实现/创建方式
1.继承Thread类
Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方 法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线 程,并执行run()方法。
public class MyThread extends Thread {
public void run() {
System.out.println("MyThread.run()");
}
}
MyThread myThread1 = new MyThread();
myThread1.start();
本机测试
/**
* 多线程的创建, 方式一:继承Thread类
* 1,创建一个继承与Thread类的子类
* 2,重写Thread类的run()方法 -->将此线程执行的操作声明在run方法中
* 3,创建Thread类的子类对象
* 4,通过此对象调用start方法
*
* 例子:遍历100以内的所有偶数
*
* @Author zfj
* @create 2019/10/25 13:59
*/
class MyThread extends Thread{
public void run() {
for(int i=0;i<=100;i++){
System.out.println(i);
}
}
}
public class ThreadTest {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start();
//问题二,再启动一个线程
MyThread t2 = new MyThread();
t2.start();
for(int i=0;i<=100;i++){
System.out.println(i+"*");
}
}
}
2.实现Runnable接口
如果自己的类已经extends另一个类,就无法直接extends Thread,此时,可以实现一个 Runnable接口
public class MyThread extends OtherClass implements Runnable{
public void run(){
System.out.println("My Thread.run()");
}
}
//启动MyThread,需要首先实例化一个Thread,并传入自己的MyThread实例:
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread);
thread.start();
//事实上,当传入一个Runnable target参数给Thread后,Thread的run()方法就会调用 target.run()
public void run(){
if(target !=null){
target.run();
}
}
本机
/**
* 创建多线程的方式二: 实现runnable接口
* 1,创建一个实现类runnable接口的类
* 2,实现类去实现runnable中的抽象方法:run()
* 3,创建实现类的对象
* 4,将此对象最为参数传递到Thread类的构造器中,创建Thread类的对象
* 5,通过Thread类的对象调用start()
*
*
* 比较两种创建线程的方式。
*
* 开发中:优先选择: 实现runnable接口的方式
* 原因:1,实现的方式没有类的单继承的局限性
* 2, 实现的方式更适合来处理来处理多个线程有共享数据的情况
* 联系:public class Thread impliments Runnable
* 相同点:两种方式都重写了run方法,将线程要执行的逻辑声明在run里面。
* @Author zfj
* @create 2019/10/26 13:26
*/
class MThread implements Runnable{
public void run() {
for(int i=0;i<100;i++){
if(i%2==0)
System.out.println(Thread.currentThread().getName()+":"+i+":");
}
}
}
public class ThreadTest1 {
public static void main(String[] args) {
MThread mThread=new MThread();
Thread t1 = new Thread(mThread);
Thread t2=new Thread(mThread);
t1.setName("线程一");
t2.setName("线程二");
t1.start();
t2.start();
}
}
3。实现callable
/**
*
* 创建线程的方式三:实现callable接口。--------JDK5.0
*
*
* 如何理解实现callable接口的方式创建多线程比实现runnable接口创建多线程方式强大?
* 1.call可以有返回值
* 2.call可以抛出异常,被外面的操作捕获,获取异常信息
* 3.callable是支持泛型的
*
* @Author zfj
* @create 2019/10/28 10:55
*/
//1.创建一个实现callable接口的实现类
class NumThread implements Callable{
//2.实现call方法,将此线程需要执行的操作声明在call方法中
public Object call() throws Exception {
int sum=0;
for(int i=1;i<=100;i++){
if(i%2==0){
System.out.println(i);
sum+=i;
}
}
return sum;
}
}
public class ThreadNew {
public static void main(String[] args) {
//3.创建callable接口实现类的对象
NumThread num=new NumThread();
//4.将此callable接口实现类的对象作为参数传递到Future Task构造器中,创建FutureTask对象
FutureTask futureTask = new FutureTask(num);
Object sum = null;
//5.将futuretask的对象作为参数传递到thread类的构造器中
new Thread(futureTask).start();
try {
//get方法的返回值即为 FutureTask构造器参数Callable实现类重写的call方法的返回值
//6.获取callable中call的返回值
sum = futureTask.get();
System.out.println("和为:"+sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
4.使用线程池
/**
*
* 创建线程的方式四:使用线程池
*
* 好处:
* 1.提高响应速度(减少了创建线程的时间)
* 2.降低资源消耗(重复利用线程池中的线程,不需要每次都创建)
* 3.便于管理
*
*
*
*
* 面试题:创建多线程有几种方式? 四种
*
* @Author zfj
* @create 2019/10/28 11:27
*/
class NumberThread implements Runnable{
public void run() {
for(int i=0;i<=100;i++){
if(i%2==0){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
class NumberThread1 implements Runnable{
public void run() {
for(int i=0;i<=100;i++){
if(i%2!=0){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
public class ThreadPool {
public static void main(String[] args) {
//1.提供指定数量的线程池
ExecutorService executorService = Executors.newFixedThreadPool(10);
ThreadPoolExecutor service1=(ThreadPoolExecutor)executorService;
//设置线程池的属性
//System.out.println(executorService.getClass());
//service1.setCorePoolSize(15);
//service1.setKeepAliveTime(10000);
//2。执行指定的线程的操作。需要提供实现runnable接口或callable几口实现类的对象
executorService.execute(new NumberThread());//适合使用runnable
executorService.execute(new NumberThread1());//适合使用runnable
// executorService.submit();//适合使用callable
executorService.shutdown();
}
}
生命周期
当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。 在线程的生命周期中,它要经过新建(New)、就绪(Runnable)、运行(Running)、阻塞 (Blocked)和死亡(Dead)5种状态。尤其是当线程启动以后,它不可能一直"霸占"着CPU独自 运行,所以CPU需要在多条线程之间切换,于是线程状态也会多次在运行、阻塞之间切换
-
新建状态(new)
当程序使用new关键字创建了一个线程之后,该线程就处于新建状态,此时仅由JVM为其分配 内存,并初始化其成员变量的值 -
就绪状态(runnable)
当线程对象调用了start()方法之后,该线程处于就绪状态。Java虚拟机会为其创建方法调用栈和 程序计数器,等待调度运行 -
运行状态(running)
如果处于就绪状态的线程获得了CPU,开始执行run()方法的线程执行体,则该线程处于运行状 态 -
阻塞状态(blocked)
阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu timeslice,暂时停止运行。 直到线程进入可运行(runnable)状态,才有机会再次获得cpu timeslice 转到运行(running)状 态。
阻塞的情况分三种- 等待阻塞 ( o.wait-> 等待对列 ) :
运行(running)的线程执行o.wait()方法,JVM会把该线程放入等待队列(waitting queue) 中。 - 同步阻塞 (lock-> 锁池 ) 运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线 程放入锁池(lock pool)中。
- 其他阻塞 (sleep/join) 运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时, JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O 处理完毕时,线程重新转入可运行(runnable)状态。
- 等待阻塞 ( o.wait-> 等待对列 ) :
线程死亡
线程会以下面三种方式结束,结束后就是死亡状态。
正常结束
- run()或call()方法执行完成,线程正常结束。
异常结束 - 线程抛出一个未捕获的Exception或Error。
调用 stop - 直接调用该线程的stop()方法来结束该线程—该方法通常容易导致死锁,不推荐使用。