1.sleep\wait\yield
- yiled是让步,会使当前线程由运行状态进入到就绪状态,让其他优先级高线程先执行,但是如果是同一优先级的线程,那么谁先执行就不确定了.它不会释放锁。
- wailt等待,会使当前线程进入阻塞状态,并且会释放锁。
- sleep休眠,会使当前线程进入休眠阻塞状态,但不会释放锁。
public class SleepLockTest{
private static Object obj = new Object();
public static void main(String[] args){
ThreadA t1 = new ThreadA("t1");
ThreadA t2 = new ThreadA("t2");
t1.start();
t2.start();
}
static class ThreadA extends Thread{
public ThreadA(String name){
super(name);
}
public void run(){
// 获取obj对象的同步锁
synchronized (obj) {
try {
for(int i=0; i <10; i++){
System.out.printf("%s: %d\n", this.getName(), i);
// i能被4整除时,休眠100毫秒
if (i%4 == 0)
Thread.sleep(100);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
2.守护线程
1.Java分为两种线程:用户线程和守护线程。
所谓守护线程是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者。
这种线程并不属于程序中不可或缺的部分,因此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。
2.用户线程和守护线程区别:
如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了。 因为没有了被守护者,守护线程也就没有工作可做了,也就没有继续运行程序的必要了。
3.代码实现及注意事项
(1) thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。
(2) 在Daemon线程中产生的新线程也是Daemon的。
(3) 守护线程应该永远不去访问固有资源,如文件、数据库,因为它会在任何时候甚至在一个操作的中间发生中断。
3.锁机制
-
可重入锁,用于实现同步机制。
private Lock lock = new ReentrantLock(); function(){ lock.lock(); try{...} finally{ lock.unlock(); } }
-
锁的条件
private Condition c = lock.newCondition(); 方法(){ while(!(ok to operate)){ c.await(); } ... c.signalAll(); }
锁和条件的注意事项
1.锁用来保护代码片段,任何时刻只能有一个线程执行被保护的代码.
2.锁可以管理试图进入被保护代码段的线程.
3.锁可以拥有一个或多个相关的条件对象
4.每个条件对象管理那些已经进入被保护代码段但还不能运行的线程.
上述操作还可以用synchronized关键字和wait/notifyAll方法来完成。
synchronized <==> lock+unlock
wait/notifyAll <==> c.await/c.signalAll
4.Lock/Condition 和 同步方法的取舍
- 最好都不用,采用java.util.concurrent包里面提供的机制,它会为你处理所有的加锁,例如阻塞队列。
- synchronized可以减少代码编写数量和出错几率。
- 需要Lock/Condition的独有特性时才使用。
5.yield和join
1.yield
- yield是一个静态的原生(native)方法
- yield告诉当前正在执行的线程把运行机会交给线程池中拥有相同优先级的线程。
- yield不能保证使得当前正在运行的线程迅速转换到可运行的状态
- 它仅能使一个线程从运行状态转到可运行状态,而不是等待或阻塞状态
2.join
线程实例的方法join()方法可以使得一个线程在另一个线程结束后再执行。如果join()方法在一个线程实例上调用,当前运行着的线程将阻塞直到这个线程实例完成了执行。