5、认识java线程

2019-04-15  本文已影响0人  小manong

一、线程生命周期

1、线程new状态

new状态可以转化为runnable状态

2、runnable状态

严格来说,runnable的线程只能意外终止或者进入running状态

3、running状态

1、直接进入terminated状态:使用stop(jdk不推荐)或者使用标志位或者意外死亡
2、进入blocked状态:使用sleep、wait等jdk方法;进行网络操作时候的读写阻塞等;为了获取到锁从而加入到了阻塞队列中
3、进入runnable状态:cpu的时间片用完,主动调yield方法放弃时间片。

4、blocked状态

1、直接进入terminated状态:使用stop(jdk不推荐)或者使用标志位或者意外死亡
2、进入runnable状态:比如网络操作阻塞结束、sleep休眠结束、被notify唤醒、获取到了某一个锁资源、线程阻塞期间被打断(比如interrupt方法)

5、terminated状态

二、创建及执行线程

1、start方法源码解析
//执行这个方法时候,jvm会调用该线程的run方法,而start0方法是本地方法,
//换言之就是说run方法是被本地方法start0调用的。
public synchronized void start() {
        if (threadStatus != 0)
            throw new IllegalThreadStateException();
        group.add(this);
        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
              
            }
        }
    }
//是一个本地方法
private native void start0();

1、thread被new之后,实际上此时的状态为0(threadState=0)
2、不能两次启动Thread,否则抛throw new IllegalThreadStateException();
3、线程启动后会被加入到ThreadGroup中
4、一个线程生命周期结束之后,也就是terminated状态,再次调用start方法是不被允许的,会抛异常。

2、模板方法在Thread中的使用(继承方式实现Thread)
private Runnable target;
 @Override
    public void run() {
      //如果实现了Runnable接口,就会执行实现Runnable接口的方法
        if (target != null) {
            target.run();
        }
    //否则就重写run中的方法
    }

public class TicketWindow extends Thread {
    /**
     * 柜台业务
     */
    private final String name;
    /**
     * 最大受理50单
     */
    private static final int MAX = 50;
    /**
     * 从编号为1的开始(注意是静态的,是类所有的,唯一的)
     */
    private static int index = 1;

    public TicketWindow(String name) {
        this.name = name;
    }
    @Override
    public void run() {
        //模板方法,子类实现
        while (index <= MAX) {
            System.out.println("柜台:" + name + ",当前的编号为:" + index++);
        }
    }
    public static void main(String[] args) {
        TicketWindow t1=new TicketWindow("t1");
        t1.start();
        TicketWindow t2=new TicketWindow("t2");
        t2.start();
        TicketWindow t3=new TicketWindow("t3");
        t3.start();
    }
}
3、策略模式在Thread中使用(Runnable接口实现)
public class TicketWindowRunnable implements Runnable {
    /**
     * 注意可以不需要static修饰
     */
    private int index = 1;

    private static final int MAX = 10;

    @Override
    public void run() {
        //模板方法,子类实现
        while (index <= MAX) {
            System.out.println("柜台:" + Thread.currentThread().getName() 
                                     + ",当前的编号为:" + index++);
        }
    }

    public static void main(String[] args) {
        TicketWindowRunnable task = new TicketWindowRunnable();
        Thread t1=new Thread(task,"t1");
        Thread t2=new Thread(task,"t2");
        Thread t3=new Thread(task,"t3");
        t1.start();
        t2.start();
        t3.start();
    }
}
4、Thread构造和Runnable构造线程的区别

(1)从面相对象角度看,Thread构造是通过继承的方式、而Runnable构造是通过组合的方式,继承的方式相对于组合的方式耦合更加紧密,因此优先推荐使用组合的方式
(2)从共享对象的角度看,Runnable构造方式意味着多个线程实例可以共享一个Runnable实例,而Thread构造不能共享run方法,如果需要实现共享,需要把相关的属性设置为static修饰的类变量。

5、线程与普通对象的区别

三、线程构造

1、线程名称

(1)默认命名

 public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }
/* For autonumbering anonymous threads. */
    private static int threadInitNumber;
    private static synchronized int nextThreadNum() {
        return threadInitNumber++;
    }

(2)线程命名

- public Thread(String name) 
- public Thread(ThreadGroup group, String name)
- public Thread(Runnable target, String name)

(3)修改线程命名

public final synchronized void setName(String name) {
        checkAccess();
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }

        this.name = name;
        if (threadStatus != 0) {
            setNativeName(name);
        }
    }
2、线程的父子关系
private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }
        this.name = name;
        //获取当前线程作为父线程
        Thread parent = currentThread();
...
3、Thread与虚拟机栈
4、守护线程
public class DeamonThread {
    public static void main(String[] args) throws InterruptedException {
        //1、开始main线程
        Thread thread=new Thread(()->{
            while (true){
                System.out.println("hahahha");
            }
        });
        //2、设置thread线程为守护线程
        thread.setDaemon(true);
        //3、开始thread线程
        thread.start();

        Thread.sleep(2000);
        //4、main线程结束
        System.out.println("main finished");
    }
}

(1) thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。
(2) 在Daemon线程中产生的新线程也是Daemon的。
(3) 守护线程应该永远不去访问固有资源,如文件、数据库,因为它会在任何时候甚至在一个操作的中间发生中断。

三、所线程的编程的优势及其风险



上一篇 下一篇

猜你喜欢

热点阅读