Java并发编程一 多线程

Java并发编程一 多线程

Java并发编程是一个很热点的话题,无论在面试时候还是在高并发的场景中。都会涉及到Java的并发编程相关的知识。Java的并发编程有两个主要的基础知识,一个是线程安全另一个是线程间通信。本Java并发编程系列博客作为博主系统学习Java并发编程的知识记录。也希望可以帮助其他人。
摘要
1,线程概念
2,Java线程的实现方式
3,Java线程状态流转介绍
4,Thread类中的常用方法分析

1.什么是线程

线程,是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组 成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个 进程的其它线程共享进程所拥有的全部资源,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程 在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。

2.Java的线程实现

Java线程实现方式有两种 第一种是继承Thread类 ,另一种是实现Runnable接口。而Thread类内部也同样实现了Runnable接口。Runnable接口只有一个run()方法。

3.Java线程状态

首先 看张线程状态流转图 这张图很重要
线程流转图

3.1 新建状态(New)

1.实现Runnable接口和继承Thread可以得到一个线程类,new一个实例出来,线程就进入了初始状态

3.2 可运行状态(Runnable)

1.可运行状态只是说你资格运行,调度程序没有挑选到你,你就永远是可运行状态。
2.调用线程的start()方法,此线程进入可运行状态。
3.当前线程sleep()方法结束,其他线程join()结束,等待用户输入完毕,某个线程拿到对象锁,这些线程也将进入可运行状态。
4.当前线程时间片用完了,调用当前线程的yield()方法,当前线程进入可运行状态。
5.锁池里的线程拿到对象锁后,进入可运行状态。

3.3 运行状态(Running)

1.线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一一种方式。

3.4 死亡状态(Dead)

1.当线程的run()方法完成时,或者主线程的main()方法完成时,我们就认为它死去。这个线程对象也许是活的,但是,它已经不是一个单独执行的线程。线程一旦死亡,就不能复生。
2.在一个死去的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。

3.5 阻塞状态(Blocked)

1.当前线程T调用Thread.sleep()方法,当前线程进入阻塞状态。
2.运行在当前线程里的其它线程t2调用join()方法,当前线程进入阻塞状态。
3.等待用户输入的时候,当前线程进入阻塞状态。

3.6 等待状态(Waiting)

1.调用obj的wait(), notify()方法前,必须获得obj锁,也就是必须写在synchronized(obj) 代码段内。
2.线程调用Object的wait()方法时,该线程释放锁。等待其他线程调用notify()进行唤醒该线程。

3.7 锁池状态(Lock)

当前线程想调用对象A的同步方法时,发现对象A的锁被别的线程占有,此时当前线程进入锁池状态。简言之,锁池里面放的都是想争夺对象锁的线程。
当一个线程1被另外一个线程2唤醒时,1线程进入锁池状态,去争夺对象锁。
锁池是在同步的环境下才有的概念,一个对象对应一个锁池。

4.Thread中常用方法

4.1 start()

Thread类中的start()方法通知“线程规划器”此线程已经准备就绪,等待调用线程对象的run()方法。这个过程其实就是让系统安排一个时间来调用run()方法。
start()方法具有异步执行效果。如果直接调用run()方法就不是异步执行了,而是同步,不是由“线程规划器”来进行处理,而是有main主线程来同步调用run()方法。
start()方法的执行顺序不代表线程的执行顺序。

4.2 setName()/getName()

对线程名称的操作。

4.3setPriority()/getPriority()

设置线程的优先级。(1到10之间)。优先级较高的线程得到的CPU资源较多,也就是CPU优先执行优先级较高的线程。
1.继承特性
线程优先级具有继承特性,如A线程的run()方法中启动B线程,则B线程的优先级与A线程相同。

4.4 isAlive()

判断当前线程是否处于活动状态,活动状态就是线程已启动且尚未终止。(其中包括阻塞状态和等待状态,锁池状态)。简单的说法就是当线程调用了start()方法后且线程没有完成都处于活动状态。

4.5interrupt()

interrupt():中断线程,被中断线程会抛InterruptedException。
interrupt()方法并不是立即停止线程,而是在当前线程上打一个停止的标识,并不是真正的停止线程。
interrupt
interrupted() :静态方法,判断当前线程是否中断,且具有清除状态效果
interrupted
isInterrupted():非静态方法,判断调用线程是否中断,没有清除状态效果
isInterrupted

正确的停止线程-异常法
使用异常法对线程进行停止,可以将停止事件向上传播。以便对线程停止进行处理。

public class MyThread1 extends Thread{
    @Override
    public void run() {
        try {
            for (int i = 0; i < 500000; i++) {
                if (this.isInterrupted())
                    throw new InterruptedException();
                System.out.println("run is " + i);

            }
            System.out.println("for 外面=========");
        }catch (InterruptedException e){
            System.out.println("进入MyThread.java类中的catch了。。。");
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Thread myThread = new MyThread1();
        myThread.start();
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        myThread.interrupt();
        System.out.println("myThread call interrupt================================================================");
    }
}

结果

4.6 join()

join():让父线程等待子线程结束之后才能继续运行。
eg:

public class MyThread1 extends Thread{

    @Override
    public void run() {
            for (int i = 0; i < 50; i++) {
                try {
                    sleep(10);
                    System.out.println("run is " + i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    }
    public static void main(String[] args) {
        MyThread1 myThread = new MyThread1();
        myThread.start();
        try {
            Thread.sleep(50);
            //注意:这里是父线程 调用子线程实例的join()方法 这时父线程会进入wait状态直到子线程运行完毕。父线程才会继续运行
            myThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("main is end================================================================");
    }
}

join()源码
join方法由synchronized修饰说明是同步方法。

 public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
        // isAlive() 这个方法是实例方法,返回的是子线程的活动状态
        //而调用join的线程是父线程,当线程进入while时,会判断子线程是否是活动状态,如果是活动状态则父线程释放锁进入等待状态。当子线程执行完毕TERMINATED状态时,父线程继续执行。
            while (isAlive()) {
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

4.7sleep()/yield()

sleep():静态方法,是在指定时间内让当前线程休眠,且不释放锁。
yield(): 静态方法,释放线程所占有的CPU资源,从而让其他线程有机会运行,但是并不能保证某个特定的线程能够获得CPU资源。谁能获得CPU完全取决于调度器,在有些情况下调用yield方法的线程甚至会再次得到CPU资源。所以,依赖于yield方法是不可靠的,它只能尽力而为。

4.8wait()/notify()

wait()/notify()这两个方法是等待和唤醒,都是Object方法。通常用作线程间通信时使用,必须配合synchronized关键字一起使用。
wait()方法,使当前线程释放锁,进入等待状态。
notity()方法,唤醒等待池里的一个线程进入锁池状态,且当前线程不会释放锁。

猜你喜欢

转载自blog.csdn.net/oyueyang1/article/details/80123050