并发编程入门(二)
Thread join 方法
用途
某一线程执行完毕,才能执行后续的方法。
异常
join 方法会 throws InterruptedException,所以使用时,要么捕获,要么抛出。
使用
创建3个线程,等3个线程都执行完成再进行响应操作。
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new Runnable(){
@Override
public void run(){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread()+"sleep 1s");
}
});
Thread t2 = new Thread(new Runnable(){
@Override
public void run(){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread()+"sleep 5s");
}
});
Thread t3 = new Thread(new Runnable(){
@Override
public void run(){
try {
Thread.sleep(8000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread()+"sleep 8s");
}
});
t1.start();
t2.start();
t3.start();
t1.join();
t2.join();
t3.join();
System.out.println("end");
}
不使用 join 方法,先输出 “end” ,再输出 线程1,2,3的执行。
使用 join 之后,线程执行完成之后才会输出 “end”。有点像异步时设置参数为同步。
有参数的 join 方法
join(毫秒数) n毫秒之后不再等待。
Thread yield 方法
用途
谦让,在没有用完 CPU 分配的时间时,主动让出 CPU 时间,给其他同优先级的线程使用。
特点
static 静态的,Thread.yield() 直接调用。
native 本地,调用非 Java 语言实现的方法,操作操作系统。
使用
创建一个线程,执行1-10000 的累加,加入 yield 方法,执行速度较慢。
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new Runnable(){
@Override
public void run(){
int sum = 0;
for(int i=0;i<10000000;i++){
sum = sum + i;
Thread.yield();
}
}
});
long cur = System.currentTimeMillis();
t1.start();
t1.join();
System.out.println(System.currentTimeMillis()-cur);
}
sleep() yield() 区别
sleep() 方法,线程阻塞,允许其他所有线程先执行,会抛出异常,可移植性好。
yield() 方法,线程就绪,只允许优先级相同或更高的线程先执行,不会抛异常。
上下文切换和死锁
上下文切换
一个线程 CPU 分配的时间片用完,或者被中断,进入就绪状态,需要保存执行到的状态,让出 CPU 给其他线程执行,这就是上下文切换。
线程死锁:
两个或两个以上的线程,因争夺资源互相等待,没有外力的作用下,会一直等待下去。
死锁的必要条件
互斥:一个资源一段时间只能被同一个进程占用
不可剥夺:不能被强制终止。
请求与保持:占有一个资源,请求另一个资源
循环等待
死锁实现
public static void main(String[] args) throws InterruptedException {
Object a = 1;
Object b = 2;
Thread t1 = new Thread(new Runnable(){
@Override
public void run(){
synchronized (a){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("1 -> b" );
synchronized (b){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("end" );
}
});
Thread t2 = new Thread(new Runnable(){
@Override
public void run(){
synchronized (b){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("2 -> a" );
synchronized (a){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("end" );
}
});
t1.start();
t2.start();
}
破坏必要条件,避免死锁
只有请求保持和循环等待可以破坏,以上,通过调整获取资源的顺序,都是 先获取a,再获取b,避免循环等待,可以避免死锁。