Java并发之Thread讲解

Java线程的生命周期

1:线程的创建即我们常说的线程的new过程,但并未启动线程。

2:线程的就绪。线程在调用start()方法之后,线程进入就绪状态,JVM会为其创建函数调度栈和计数器并初始化成员变量,此时线程并未正式运行,而是等待CPU调度。

3:线程的运行。当准备就绪的线程获取到CPU的调度之后,就开始进入线程的运行状态了。

4:线程的阻塞。由于线程在运行的过程中我们可能调用了Thread.sleep()或者Object.wait()或者等待其他线程join()执行完毕的过程,会将当前运行的线程暂停让出CPU调度给其他线程使用。等到sleep()方法时间执行完毕或者有其他线程唤醒通过nontify()方法缓存正在wait()方法的线程。

5:线程的消亡。当线程执行完毕之后或者线程被杀死即线程结束进入消亡了。

Java中创建线程的四种方式

(一)显示创建线程

/**

* @ClassName NewThread

* @Description 线程的第一种启动方式 显示的创建线程

* @Author huangwb

* @Date 2019-02-21 22:18

* @Version 1.0

**/

public class NewThread {

    private static int count = 0;





    public static void main(String[] args) throws InterruptedException {

        Thread thread = new Thread(new Runnable() {

            @Override

            public void run() {

                for (int i = 0; i < 200000; i++) {

                    count++;

                }

            }

        });

        thread.start();

        //等待子线程thread执行完毕

        thread.join();

        System.out.println("线程执行完毕,count=" + count);

    }

}

(二)继承Thread类方式

/**

* @ClassName ExtendThread

* @Description TODO 通过继承Thread方式创建线程

* @Author huangwb

* @Date 2019-02-21 22:24

* @Version 1.0

**/

public class ExtendThread extends Thread {

    private static int count = 0;





    @Override

    public void run() {

        for (int i = 0; i < 20000; i++) {

            count++;

        }

    }





    public static void main(String[] args) throws InterruptedException {

        ExtendThread extendThread = new ExtendThread();

        extendThread.start();

        extendThread.join();

        System.out.println(count);

    }

}

(三)实现Runnable接口的方式

/**

* @ClassName ImplementsRunnable

* @Description TODO

* @Author huangwb

* @Date 2019-02-21 22:28

* @Version 1.0

**/

public class ImplementsRunnable implements Runnable {

    private static int count = 0;





    @Override

    public void run() {

        for (int i = 0; i < 20000; i++) {

            count++;

        }

    }





    public static void main(String[] args) throws InterruptedException {

        Thread thread = new Thread(new ImplementsRunnable());

        thread.start();

        thread.join();

        System.out.println(count);

    }

}

(四)实现Callable接口的方式

/**

* @ClassName ImplementsCallable

* @Description TODO 使用实现Callable和Future创建具备返回值的线程

* @Author huangwb

* @Date 2019-02-21 22:31

* @Version 1.0

**/

public class ImplementsCallable implements Callable {

    private static int count = 0;





    @Override

    public Object call() throws Exception {

        for (int i = 0; i < 20000; i++) {

            count++;

        }

        return count;

    }





    public static void main(String[] args) throws ExecutionException, InterruptedException {

        ImplementsCallable implementsCallable = new ImplementsCallable();

        //需要创建futureTask方法用来包装Callable对象

        FutureTask<Integer> futureTask = new FutureTask<>(implementsCallable);

        new Thread(futureTask).start();

        //无需Thread.join()等待线程执行完毕拿值

        System.out.println(futureTask.get());

    }

}

四种方式的比较

第一种显示创建线程优点方便快捷,能够直接创建线程并且使用。

第二种继承Thread实现线程的方式,优点编写简单,如果需要访问多线程直接使用this即可。缺点:在Java中类都是单继承多实现的方式,所以该类不能再继承其他父类。

第三种和第四种类都只实现了Runnable和Callable方法,能够继承其他类。非常适合多个线程处理同一份资源的情况,可以将数据和代码分开。Callable能够拿到具有返回值的线程,并且能够使用FutureTask进行包装,能够拿到执行过后的结果。而第一种到第三种都必须显示调用Thread.join()方法才能拿到线程的结果。如果当我们需要父线程获取子线程的结果时这个方法会变得很有用。Future和FutureTask中有很多非常有有用的方法来使用,后面有时间使用到ExecutorService也会去讲解Future接口和FutureTask类。

 

Thread中经常使用方法讲解

1、start()方法

启动一个线程,一个线程只能start一次,这个前面也有测试就不多使用了。

run()方法,不需要用户来调用的,在讲线程生命周期时,当线程启动之后就会等待CPU执行调度。

 

2、sleep()

在Thread中sleep有两个重载的方法

sleep(long millis); 

sleep(long millis,int nanoseconds);

第一个参数为毫秒,第二个参数会纳秒

sleep从英文理解就是睡眠,在线程调用sleep的执行过程中,会交出CPU,让CPU去执行其他任务。

注意点:sleep方法并不会释放锁,会持续占有锁,其他线程并不能继续访问这个线程的执行。

/**

* @ClassName ThreadSleep

* @Description TODO

* @Author huangwb

* @Date 2019-02-21 23:04

* @Version 1.0

**/

public class ThreadSleep {

    public static void main(String[] args) {

        Thread thread1 = new Thread(new Runnable() {

            @Override

            public void run() {

                System.out.println("CPU执行线程一");

                //沉睡两秒钟

                try {

                    Thread.sleep(2000);

                    System.out.println("线程一进入沉睡");

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

                System.out.println("线程一执行完毕");

            }

        });

        thread1.start();

    }

}
CPU执行线程一

线程一进入沉睡

线程一执行完毕

3、yield()

调用yield方法会直接让出CPU权限,让CPU去执行其他线程,与sleep方法类似 但不同的是yield()方法并不会阻塞线程。

注意,调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,这一点是和sleep方法不一样的。

 

4、join方法()

 join方法有三个重载版本

join();

join(long millis);

join(long millis,int nanoseconds);

join()实际是利用了wait(),只不过它不用等待notify()/notifyAll(),且不受其影响。它结束的条件是:1)等待时间到;2)目标线程已经run完(通过isAlive()来判断)。

我们在上面线程的四种实现方法的前三种都用到了join,就是为了能够更好的观察到结果,如果不使用的话,打印出来的结果在mian主线程中即为0

 

5、interupt();

此操作会中断等待中的线程,并将线程的中断标志位置位。如果线程在运行态则不会受此影响。

可以通过以下三种方式来判断中断:

(1)isInterrupted()

此方法只会读取线程的中断标志位,并不会重置。

(2)interrupted()

此方法读取线程的中断标志位,并会重置。

3)throw InterruptException

抛出该异常的同时,会重置中断标志位。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

难搞哦!!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值