线程常用方法[run()方法必需是public访问权限,返回值类型为void ]
Start()方法
start()用来启动一个线程,当调用start方法后,系统才会开启一个新的线程来执行用户定义的子任务,在这个过程中,会为相应的线程分配需要的资源。Start()方法返回了,线程就就绪了。
Run()方法
run()方法是不需要用户来调用的,当通过start方法启动一个线程之后,当线程获得了CPU执行时间,便进入run方法体去执行具体的任务。注意,继承Thread类必须重写run方法,在run方法中定义具体要执行的任务。
sleep方法
1: 线程暂停执行一段时间,让其他线程有机会继续执行,
2: 但它并不释放对象锁(所以他是在同步代码块中使用)。
3: 注意该方法必须捕获InterruptedException异常
4: sleep()可以使低优先级的线程得到执行的机会,
5:程睡眠时间满后,不一定会立即得到执行,因为此时可能CPU正在执行其他的任务所以说调用sleep方法相当于让线程进入阻塞状态。
sleep方法有两个重载版本:
1 2 3 |
sleep(long millis) //参数为毫秒 sleep(long millis,int nanoseconds)//第一参数为毫秒,第二个参数为纳秒 |
||
22 23 24 25 26 27 28 29 30 |
try { System.out.println("线程"+Thread.currentThread().getName()+"进入睡眠状态"); Thread.currentThread().sleep(10000); } catch (InterruptedException e) { // TODO: handle exception } |
||
4)yield()方法
1让当前线程交出CPU权限,但不能控制具体的交出CPU的时间
2由用户指定暂停多长时间,
3能让同优先级的线程有执行的机会。
4不会释放锁
5:调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待新获取CPU执行时间,马上又被执行。
5)join()方法
1:等待调用该方法的线程执行完毕后,当前线程再往下继续执行。
2:注意该方法也需要捕捉异常。
3:实际上调用join方法是调用了Object的wait方法,
4:wait方法会让线程进入阻塞状态,释放锁,并交出CPU执行权限。
5:由于wait方法会让线程释放对象锁,所以join方法同样会让线程释放对一个对象持有的锁。
join方法有三个重载版本:
1 2 3 |
join() join(long millis) //参数为毫秒 join(long millis,int nanoseconds) //第一参数为毫秒,第二个参数为纳秒 |
如果调用的是无参join方法,则等待thread执行完毕,如果调用的是指定了时间参数的join方法,则等待一定的时间。
实际上调用join方法是调用了Object的wait方法,可以通过查看源码得知:
wait()和notify()、notifyAll() :注意这三个方法都是java.lang.Object的方法。 如何能在当前线程还没退出synchronized数据块时让其他线程也有机会访问共享数据呢?
1: 调用wait()方法使当前线程暂停执行并释放对象锁标示,其他线程进入synchronized块,
2:当前线程被放入对象等待池中。
3:调用notify()方法后,将从对象的等待池中移走一个任意的线程并放到锁标志等待池中。
调用notifyAll()则从对象等待池中移走所有等待那个对象的线程并放到锁标志等待池中。
[只有锁标志等待池中线程能够获取锁标志;锁标志等待池中没有线程,则notify()不起作用]
总结:
1:wait()方法与notify()必须要与synchronized(resource)一起使用。Obj.wait(),Obj.notify必须在synchronized(Obj){...}语句块内。
2:Thread.sleep()与Object.wait()二者都可以暂停当前线程,释放CPU控制权,主要的区别在于Object.wait()在释放CPU同时,释放了对象锁的控制,而在同步块中的Thread.sleep()方法并不释放锁,仅释放CPU控制权。
interrupt()方法
1:单独调用interrupt方法可以使得处于阻塞状态的线程抛出一个异常,中断一个正处于阻塞状态的线程.
6 7 8 9 10 11 12 13 14 15 16 17 |
public class Test { public static void main(String[] args) throws IOException { Test test = new Test(); MyThread thread = test.new MyThread(); thread.start(); try { Thread.currentThread().sleep(2000); } catch (InterruptedException e) { } thread.interrupt(); } |
2:那么能不能中断处于非阻塞状态的线程呢?
但是如果配合isInterrupted()能够中断正在运行的线程,因为调用interrupt方法相当于将中断标志位置为true,那么可以通过调用isInterrupted()判断中断标志是否被置位来中断线程的执行。比如下面这段代码:
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public class Test { public static void main(String[] args) throws IOException { Test test = new Test(); MyThread thread = test.new MyThread(); thread.start(); try { Thread.currentThread().sleep(2000); } catch (InterruptedException e) { } thread.interrupt(); } class MyThread extends Thread{ @Override public void run() { int i = 0; while(!isInterrupted() && i<Integer.MAX_VALUE){ System.out.println(i+" while循环"); i++; }}}} |
运行会发现,打印若干个值之后,while循环就停止打印了。
但是一般情况下不建议通过这种方式来中断线程,一般会在MyThread类中增加一个属性 isStop来标志是否结束while循环,然后再在while循环中判断isStop的值。那么就可以在外面通过调用setStop方法来终止while循环。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class MyThread extends Thread{ private volatile boolean isStop = false; @Override public void run() { int i = 0; while(!isStop){ i++; } } public void setStop(boolean stop){ this.isStop = stop; } } |
stop方法
stop方法已经是一个废弃的方法,它是一个不安全的方法。因为调用stop方法会直接终止run方法的调用,并且会抛出一个ThreadDeath错误,如果线程持有某个对象锁的话,会完全释放锁,导致对象状态不一致。所以stop方法基本是不会被用到的。
8)destroy方法
destroy方法也是废弃的方法。基本不会被使用到。
以下是关系到线程属性的几个方法:
1)getId
用来得到线程ID
2)getName和setName
用来得到或者设置线程名称。
3)getPriority和setPriority
用来获取和设置线程优先级。
4)setDaemon和isDaemon
用来设置线程是否成为守护线程和判断线程是否是守护线程。
守护线程和用户线程的区别:
守护线程依赖于创建它的线程,而用户线程则不依赖。举个简单的例子:如果在main线程中创建了一个守护线程,当main方法运行完毕之后,守护线程也会随着消亡。而用户线程则不会,用户线程会一直运行直到其运行完毕。在JVM中,像垃圾收集器线程就是守护线程。Thread类有一个比较常用的静态方法currentThread()用来获取当前线程。
在上面已经说到了Thread类中的大部分方法,那么Thread类中的方法调用到底会引起线程状态发生怎样的变化呢?下面一幅图就是在上面的图上进行改进而来的: