当前位置: 首页 > >

Java并发编程二线程常用方法

发布时间:



Java中线程方法
start方法sleep方法interrupt方法join方法yield方法wait和notify用户线程和守护线程



在上一章Java并发编程一我们对Java内存模型和线程有所了解,接下来继续学*线程的方法。


start方法

上一章讲解了三种创建线程的方法(继承Thread,实现Runnable或Callable接口),
调用start方法才是真正启动线程,调用run方法只是调用当前线程的普通方法。


sleep方法

Thread中静态方法sleep(),当一个线程调用sleep方法时,会暂时让出当前CPU的执行权,等到时间到时,会重新获取执行权,不会释放锁。代码比较简单就不多解释了。


interrupt方法

通过我们都会主动停止线程,而线程中stop,resume已经被摒弃了,通过interrupt我们可以完成中断线程,抛出InterruptedException异常。


public class InterruptThread extends Thread{
InterruptThread(){
this.start();
}
@Override
public void run() {
try {
System.out.println("Interrupted begin");
sleep(5000);
System.out.println("Interrupted end");
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("Interrupted");
}
}
public static void main(String[] args) throws InterruptedException {
InterruptThread thread = new InterruptThread();
// 是否被中断
System.out.println(thread.isInterrupted());
Thread.sleep(1000);
// 中断线程
thread.interrupt();
}
}


运行结果:



join方法

我们肯定会遇见这样一个场景,就是需要等待几件事情完成再往下执行,比如多线程计算每个月的账单,等计算完毕后再进行汇总处理。Thread中join方法可以完成这样的操作。



运行结果:
join 开始
one join
two join
join 结束



yield方法

Thread中静态方法,正常情况下线程使用完时间片后等待下一次分配,而线程执行yield方法是自己占有CPU时间片不想用了,然后处于就绪状态,线程调度器会从线程就绪队列中获取一个优先级最高的,当然也有可能会执行刚才放弃CPU的线程。Java中是通过setPriority()设置优先级,但是没什么效果。


public class YieldThread implements Runnable {
YieldThread(){
new Thread(this).start();
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
if(i==0) {
System.out.println(Thread.currentThread().getName()+":yield 让出cpu");
//Thread.yield();
//Thread.sleep(500);
}
}
System.out.println(Thread.currentThread().getName()+":yield over");
}
public static void main(String[] args) {
new YieldThread();
new YieldThread();
new YieldThread();
new YieldThread();
}
}

开启四个线程,每个线程循环五次,当i==0时执行yield方法,注释掉yield方法,执行结果:
解开Thread.yield()再次执行:

通过两次结果可以看出,当线程执行到yield时,会让出CPU执行权,等待下次执行,调用Sleep方法也是可以实现上面效果。
两者不同:sleep方法会将线程转为阻塞状态,等待时间到时再执行,让线程级别低的可能被执行,yield方法会将线程转为就绪状态,让线程级别高优先执行或者自身执行。


wait和notify

当线程调用一个共享变量的wait方法时,该线程就会挂起,并且让出同步锁,让其他线程可以获得锁,直到其他线程调用这个变量的notify或者notifyAll方法唤醒或者其他线程调用了该线程的interrupt()方法,该线程抛出InterruptExcetion异常返回。需要注意的是,如果调用wait方法没有获取对象的监视器锁,则会抛出IllegalMonitorStateException异常。这两个必须配合synchronized一起使用,接下来通过一个生产者消费者来说明wait和notify。
首先是定义了一个list用于存储生产的数据,定义一个变量来声明最多可添加多少:

然后是生产者:
开启五个线程,判断list是否已满, 这里判断为什么是while而不是if是因为如果此时满了消费者消费了一个,此时唤醒生产者生产,如果使用if唤醒是从wait往下继续执行,在执行list.add(random),有可能其他生产线程就已经添加完,再添加就超过容器范围。如果使用while会再次判断是否已满,防止超出。百分之九九的情况下wati和while一起使用。list不满,则随机生产100以内的数字添加,然后使用notifyAll通知所有消费者消费数据。notify和notifyAll区别就是唤醒一个和唤醒所有wati的线程。
最后消费者:

开启10个线程去消费,如果list为空,则阻塞等待,不为空则消费数据,唤醒阻塞中的生产者继续生产。
由于机试中遇见到,还是比较经典的题,当时不会写只知道概念,还有Lock、BlockingQueue写法。


用户线程和守护线程

用户线程:前台线程,我们运行的main方法和三种方法创建的线程都是用户线程,jvm会等到用户线程使用完后才结束。
守护线程:后台线程,比如GC线程,通过设置线程的setDaemon方法(必须设置在start之前),默认为false,如果当前只有守护线程,那么jvm直接结束。


public class DaemonThread extends Thread{
@Override
public void run() {
while (true){
System.out.println("守护线程");
}
}
public static void main(String[] args) {
DaemonThread thread = new DaemonThread();
// 设置守护线程
//thread.setDaemon(true);
thread.start();
}}

当线程开启时,如果解开注释,jvm就会退出,如果不解开,那么jvm就会等用户线程运行完再退出。。
以上就是线程的几种常用方法,当然还有一些延迟方法,比如wait(Long time)等等一些,基本用法一样。



友情链接: