一、线程的状态
因为调用sleep()和join()而阻塞的线程,仍然持有锁,待sleep()和join()结束,进入准备就绪状态;
而调用wait()的线程会放弃锁的持有,进而调用notify()唤醒之后,如同没有获取到Synchronized锁的线程一样,继续竞争锁,竞争到锁以后,进入准备就绪状态。
join
t.join()方法阻塞调用此方法的线程阻塞状态,直到线程t完成,此线程再继续(即主线程阻塞,等待t线程优先运行完毕)。
public class JoinTester01 implements Runnable {
private String name;
public JoinTester01(String name) {
this.name = name;
}
public void run() {
System.out.printf("%s begins: %s\n", name, new Date());
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("%s has finished: %s\n", name, new Date());
}
public static void main(String[] args) {
Thread thread1 = new Thread(new JoinTester01("One"));
Thread thread2 = new Thread(new JoinTester01("Two"));
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Main thread is finished");
}
}
如果去除join:
yeild
当前线程从运行状态变为可执行状态,放弃当前cpu的资源,将它让给其他任务。放弃的时间不确定,有可能立刻放弃,也可能过一段时间放弃,有可能刚刚放弃,马上又获得cpu的时间片。
二、Thread,Runnable
区别:
1、Thread也实现了Runnable接口
public class Thread implements Runnable {}
并进行了扩展,除了Runnable的wait、notify方法外,增加了interrupt、isInterrupted、join方法。
2、实现了Runnable的类可以放入线程池。
Thread中的run和start:
run()方法用的是当前线程(主线程)的调用栈,程序还是要顺序执行的;
start()方法的启动是在独立的调动栈中进行的,是真正的多线程。
public class RunAndStart extends Thread{
@Override
public void run() {
try {
for (int i = 0; i < 5; i++) {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + " " + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
RunAndStart thread1 = new RunAndStart();
RunAndStart thread2 = new RunAndStart();
thread1.run();
thread2.run();
}
}
结果:
main 0
main 1
main 2
main 3
main 4
main 0
main 1
main 2
main 3
main 4
public class RunAndStart extends Thread{
@Override
public void run() {
try {
for (int i = 0; i < 5; i++) {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + " " + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
RunAndStart thread1 = new RunAndStart();
RunAndStart thread2 = new RunAndStart();
thread1.start();
thread2.start();
}
}
结果:
Thread-1 0
Thread-0 0
Thread-1 1
Thread-0 1
Thread-0 2
Thread-1 2
Thread-1 3
Thread-0 3
Thread-0 4
Thread-1 4
同时,启动实现了Runnable接口的线程的正确方法是:
new Thread(runnable(这是Runnable的一个实例)).start;
而不是直接调用其run()方法。
三、Callable、Future
Callable接口:
public interface Callable<V> {
V call() throws Exception;
}
Future接口:
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
FutureTask类:
那么如何获取Callable的返回结果呢:
把Callable实例当作参数,生成一个FutureTask的对象,然后把这个对象当作一个Runnable,作为参数另起线程。由于FutureTask实现了Runnable,因此它既可以通过Thread包装来直接执行,也可以提交给ExecuteService来执行。
https://my.oschina.net/u/3572551/blog/1862260/
四、Conditon信号量
线程之间除了同步互斥,还要考虑通信。在Java5之前我们的通信方式为:wait 和 notify。那么Condition的优势是支持多路等待,就是我可以定义多个Condition,每个condition控制线程的一条执行通路,而传统方式只能是一路等待。
Condition对象是需要关联Lock对象的,调用Lock对象的newCondition()对象创建而来,也就是说Condition的使用是需要依赖Lock对象的:
{
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
Thread thread = new Thread(() -> {
lock.tryLock();
try {
System.out.println("wait signal");
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("got signal");
lock.unlock();
});
Thread thread2 = new Thread(() -> {
lock.tryLock();
System.out.println("i got the lock");
try {
TimeUnit.SECONDS.sleep(1);
condition.signal();
System.out.println("i send a signal");
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.unlock();
});
thread.start();
TimeUnit.MILLISECONDS.sleep(10);
thread2.start();
TimeUnit.SECONDS.sleep(2);
}
结果:
wait signal
i got the lock
i send a signal
got signal
使用Condition和普通队列实现一个阻塞队列:
public class BQueue {
private int size=5;
private Queue<Integer> queue=new LinkedList<Integer>();
private Lock lock = new ReentrantLock();
private Condition notFull = lock.newCondition();
private Condition notEmpty = lock.newCondition();
private void poll() {
lock.lock();
try {
while(queue.size() == 0){
try {
System.out.println("队列空,等待数据");
notEmpty.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
queue.poll();
notFull.signal();
System.out.println(Thread.currentThread().getName()+"从队列取走一个元素,队列剩余"+queue.size()+"个元素");
} finally{
lock.unlock();
}
}
private void add() {
lock.lock();
try {
while(queue.size() == size){
try {
System.out.println(Thread.currentThread().getName()+"队列满");
notFull.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
queue.add(1);
notEmpty.signal();
System.out.println(Thread.currentThread().getName()+"向队列插入一个元素,队列剩余"+queue.size()+"个元素");
} finally{
lock.unlock();
}
}
public static void main(String[] args) {
final BQueue q=new BQueue();
Runnable t1=new Runnable(){
public void run() {
while (true){
try {
TimeUnit.SECONDS.sleep(1);
q.add();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
};
Runnable t2=new Runnable(){
public void run() {
while (true) {
try {
TimeUnit.SECONDS.sleep(7);
q.poll();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
};
new Thread(t1).start();
new Thread(t1).start();
new Thread(t2).start();
}
}
结果:
Thread-1向队列插入一个元素,队列剩余1个元素
Thread-0向队列插入一个元素,队列剩余2个元素
Thread-0向队列插入一个元素,队列剩余3个元素
Thread-1向队列插入一个元素,队列剩余4个元素
Thread-0向队列插入一个元素,队列剩余5个元素
Thread-1队列满
Thread-0队列满
Thread-2从队列取走一个元素,队列剩余4个元素
Thread-1向队列插入一个元素,队列剩余5个元素
Thread-1队列满
Thread-2从队列取走一个元素,队列剩余4个元素
Thread-0向队列插入一个元素,队列剩余5个元素
Thread-0队列满
Condition原理: