Java线程基础知识

2017-09-28  本文已影响17人  稻田上的稻草人

线程的状态

shunxutu.png

线程的优先级

  /**
     * The minimum priority that a thread can have.
     */
    public final static int MIN_PRIORITY = 1;

   /**
     * The default priority that is assigned to a thread.
     */
    public final static int NORM_PRIORITY = 5;

    /**
     * The maximum priority that a thread can have.
     */
    public final static int MAX_PRIORITY = 10;

线程的使用

Thread t1 = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("t1 begin");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("t1 end");
    }
});

t1.start();

线程中特殊函数

join()

join方法是一个属于对象的方法,主要作用是是的调用join方法的这个线程对象先执行,调用方法所在的线程等执行完了,在执行。

Thread t1 = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("t1 begin");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("t1 end");
    }
});

t1.start();
t1.join();

Thread t2 = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("t2 begin");
        System.out.println("t2 end");
    }
});
t2.start();

输出的结果:


//注释t1.join()


t1 begin

t2 begin

t2 end

t1 end

//没有注释t1.join()


t1 begin

t1 end

t2 begin

t2 end

wait()

表示等待获取某个锁执行了该方法的线程释放对象的锁,JVM会把该线程放到对象的等待池中。该线程等待其它线程唤醒 notify() 执行该方法的线程唤醒在对象的等待池中等待的一个线程,JVM从对象的等待池中随机选择一个线程,把它转到对象的锁池中。使线程由阻塞队列进入就绪状态(只能在同步代码块中使用)上面尤其要注意一点,一个线程被唤醒不代表立即获取了对象的monitor,只有monitor,只有等调用完notify()或者notifyAll()并退出synchronized块,释放对象锁后,其余线程才可获得锁执行

sleep()

是一个类的方法,让当前线程停止执行,让出cpu给其他的线程,但是不会释放对象锁资源以及监控的状态,当指定的时间到了之后又会自动恢复运行状态。有一个用法可以代替yield函数——sleep(0)

yield()

这方法与sleep()类似,可以使用sleep(0)来达到相同的效果,只是不能由用户指定暂停多长时间,并且yield()方法只能让同优先级或者高优先级的线程有执行的机会,注意这里并不是一定,有可能又会执行当前线程,执行完后,这个线程的状态从执行状态转到了就绪状态

notify()

执行该方法的线程唤醒在对象的等待池中等待的一个线程,JVM从对象的等待池中随机选择一个线程,把它转到对象的锁池中。使线程由阻塞队列进入就绪状态。注意:这里必须持有相同锁的线程

interrupt()

中断线程,被中断线程会抛InterruptedException

线程的停止

当线程启动时,我们怎么去停止启动的线程呢?一般来说,有

run()和start()的区别

我们从源码来学习,这两个方法的不同,Thread类的方法:


    /**
     * Package-scope method invoked by Dalvik VM to create "internal"
     * threads or attach threads created externally.
     *
     * Don't call Thread.currentThread(), since there may not be such
     * a thing (e.g. for Main).
     */
    Thread(ThreadGroup group, String name, int priority, boolean daemon) {
        synchronized (Thread.class) {
            id = ++Thread.count;
        }
        if (name == null) {
            this.name = "Thread-" + id;
        } else {
            this.name = name;
        }
        if (group == null) {
            throw new InternalError("group == null");
        }
        this.group = group;
        this.target = null;
        this.stackSize = 0;
        this.priority = priority;
        this.daemon = daemon;
        /* add ourselves to our ThreadGroup of choice */
        this.group.addThread(this);
    }
    /**
     * Initializes a new, existing Thread object with a runnable object,
     * the given name and belonging to the ThreadGroup passed as parameter.
     * This is the method that the several public constructors delegate their
     * work to.
     *
     * @param group ThreadGroup to which the new Thread will belong
     * @param runnable a java.lang.Runnable whose method <code>run</code> will
     *        be executed by the new Thread
     * @param threadName Name for the Thread being created
     * @param stackSize Platform dependent stack size
     * @throws IllegalThreadStateException if <code>group.destroy()</code> has
     *         already been done
     * @see java.lang.ThreadGroup
     * @see java.lang.Runnable
     */
//带runnable参数的thread类的构造函数调用了这个方法
    private void create(ThreadGroup group, Runnable runnable, String threadName, long stackSize) {
        Thread currentThread = Thread.currentThread();
        if (group == null) {
            group = currentThread.getThreadGroup();
        }
        if (group.isDestroyed()) {
            throw new IllegalThreadStateException("Group already destroyed");
        }
        this.group = group;
        synchronized (Thread.class) {
            id = ++Thread.count;
        }
        if (threadName == null) {
            this.name = "Thread-" + id;
        } else {
            this.name = threadName;
        }
        //建立的runnable接口赋值给thread中的target
        this.target = runnable;
        this.stackSize = stackSize;
        this.priority = currentThread.getPriority();
        this.contextClassLoader = currentThread.contextClassLoader;
        // Transfer over InheritableThreadLocals.
        if (currentThread.inheritableValues != null) {
            inheritableValues = new ThreadLocal.Values(currentThread.inheritableValues);
        }
        // add ourselves to our ThreadGroup of choice
        this.group.addThread(this);
    }

run方法的源代码:


 public void run() {
        if (target != null) {
            target.run();
        }
    }

在run方法中,直接调用的是我们传入的target(Runnable对象)的run方法,并没有开启新的线程

start方法的源代码:


 public synchronized void start() {
        checkNotStarted();
        hasBeenStarted = true;
        nativeCreate(this, stackSize, daemon);
    }

start方法最后调用了nativeCreate的native方法,这个方法的主要作用是开启了一个新的线程。并且这个方法,会利用jni回调Thread的run方法。

总结:

  1. 如果直接调用run方法,并没有开启新的线程,而是直接运行run方法里面的内容,
  2. 而start方法,则会调用native方法 nativeCreate 开启线程

线程的停止

实际开发中,我们使用线程的场景一般是执行耗时任务,如果我们开启了多个新的线程来执行新的任务,最后又不在对他进行关闭,这样有时候会浪费资源和内存的泄露。那我们怎么来管理我们的线程呢?目前有两种方法:

今天我们不讲线程池,后面的文章会讲到。对于单个线程而言,上面我们将了他的启动,现在我们来讲他的关闭。

线程的关闭的二种方式:

1. 使用标志位

我们定义一个标志位,在线程的run方法中,不断的循环检测标志位,从而确定是否退出

public class ShutdownThread extends Thread {  
    public volatile boolean exit = false;   
        public void run() {   
        while (!exit){  
            //do something  
        }  
    }   
}  
2. 使用interrupt方法

这里可以分为两种情况:

public class ShutdownThread extends Thread {  
    public void run() {   
        while (true){  
            try{  
                    Thread.sleep(5*1000);阻塞5妙  
                }catch(InterruptedException e){  
                    e.printStackTrace();  
                    break;//捕获到异常之后,执行break跳出循环。  
                }  
        }  
    }   
}   
public class ShutdownThread extends Thread {  
    public void run() {   
        while (!isInterrupted()){  
            //do something, but no tthrow InterruptedException  
        }  
    }   
}  

为什么要区分进入阻塞状态和和非阻塞状态两种情况了,是因为当阻塞状态时,如果有interrupt()发生,系统除了会抛出InterruptedException异常外,还会调用interrupted()函数,调用时能获取到中断状态是true的状态,调用完之后会复位中断状态为false,所以异常抛出之后通过isInterrupted()是获取不到中断状态是true的状态,从而不能退出循环,因此在线程未进入阻塞的代码段时是可以通过isInterrupted()来判断中断是否发生来控制循环,在进入阻塞状态后要通过捕获异常来退出循环。

因此使用interrupt()来退出线程的最好的方式应该是两种情况都要考虑:

public class ThreadSafe extends Thread {  
    public void run() {   
        while (!isInterrupted()){ //非阻塞过程中通过判断中断标志来退出  
            try{  
                Thread.sleep(5*1000);//阻塞过程捕获中断异常来退出  
            }catch(InterruptedException e){  
                e.printStackTrace();  
                break;//捕获到异常之后,执行break跳出循环。  
            }  
        }  
    }   
}   
上一篇 下一篇

猜你喜欢

热点阅读