1、线程的状态图
1、状态图,5个状态:新建状态,可运行状态(就绪),阻塞状态(三种阻塞),正在运行状态,死亡状态(run完成)
2、一般的阻塞:调用 sleep, join后就会进入一般的阻塞
3、对象锁池阻塞 : 当一个子线程访问synchronized同步的访问或者代码块时,如果该线程没有获取到对象锁,那么该线程就会进入锁
池进入等待状态,直到该相应的对象锁释放且拿到对象锁之后才会继续执行之后的方法。
4、对象等待池阻塞:获取到锁的线程在同步方法或者代码块里面调用wait()方法后,该线程就会释放掉对象锁,然后进入等待池等待
2、线程的基本使用sleep, join,yield
1、sleep(long millis) 线程的休眠,会抛出InterruptedException异常 而且必须捕获更多内容见这里:
InterruptedException 简述
2、join() ,在当前线程中用另一个线程对象进行调用,调用之后要等到另一个线程执行完毕之后才会执行当前线程
3、yield() ,放弃当前cpu的使用机会
3、线程的同步 synchronized
1、两种使用方法
①方法的同步 (锁住当前方法所在的对象)
②代码块的同步 (锁住更加具体的对象,这样同步会更加准确) 详细说明:
synchronized两种写法的区别
2、synchronized 保证了代码块或者是方法在同一时刻只能有一个线程可以访问,但是外部的东西 synchronized就不管了,即 对象锁也只是在synchronized代码块里面会被锁住,对于其他代码块,没有被锁住的线程依然可以进行访问。
4、池 wait(), notify(), notifyAll()
1、等待池 wait():当线程执行包含对一个特定对象执行wait()调用的同步代码时,那个线程被放到与那个对象相关的等待
池中。此外,调用wait()的线程自动释放对象的锁标志。可以调用不同的wait():wait() 或 wait(long timeout);
2、
锁池:对一个特定对象执行notify()调用时(或者线程的interrupt()方法被调用时),将从对象的等待池中移走一个任意的线程,并放到锁池中,那里的对象一直在等待,直到可以获得对象的锁标记。 notifyAll()方法将从等待池中移走所有等待那个对象的线程并放到锁池中。只有锁池中的线程能获取对象的锁标记,锁标记允许线程从上次因调用wait()而中断的地方开始继续运行。
3、notify(), notifyAll()的区别:notify更容易造成死锁,notifyAll相对比较安全 。详细内容见notify和notifyAllde区别
5、线程的死锁
简单来说就是大家都进入了等待池,没人来notify了,下面是个经典的吃饭的例子
package b_deadBlockEat;
import java.util.Random;
//知识要求:了解
/**
* 演示了线程死锁。
*
* @see Thread
*/
public class Deadlock {
public static void main(String[] args) {
// 两个对象(资源)
final String chopsticks = "筷子";
final String bowl = "碗";
// 线程 1 先锁住筷子再锁住碗,然后才能吃饭
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("我要筷子...");
synchronized (chopsticks) {
System.out.println("我得到了筷子");
System.out.println("我要碗...");
sleep();
synchronized (bowl) {
System.out.println("我得到了碗");
sleep();
System.out.println("我吃饭咯");
}
}
}
}).start();
//注意:可能造成死锁
// 线程 2 先锁住碗再锁住筷子,然后才能吃饭 这里已经解决了
new Thread(new Runnable() {
@Override
public void run() {
synchronized (bowl) {
System.out.println(" 你要筷子...");
System.out.println(" 你得到了筷子");
sleep();
System.out.println(" 你要碗...");
synchronized (chopsticks) {
System.out.println(" 你得到了碗");
sleep();
System.out.println(" 你吃饭咯");
}
}
}
}).start();
}
static void sleep() {
try {
Thread.sleep(new Random().nextInt(500));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
解决方法
/*
* DeadlockResolved.java
* Copyright 2010 Binzhou University. All rights reserved.
*/
package b_deadBlockEat;
import java.util.Random;
//知识要求:了解
/**
* 一种避免死锁的方案。
*
* @see Thread
*/
public class DeadlockResolved {
public static void main(String[] args) {
// 两个对象(资源)
final String chopsticks = "筷子";
final String bowl = "碗";
// 线程 1 先锁住筷子再锁住碗,然后才能吃饭
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("我要筷子...");
synchronized (chopsticks) {
System.out.println("我得到了筷子");
System.out.println("我要碗...");
sleep();
synchronized (bowl) {
System.out.println("我得到了碗");
sleep();
System.out.println("我吃饭咯");
}
}
}
}).start();
//一种解决死锁的方法
// 线程 2 同样先锁住筷子再锁住碗,然后才能吃饭
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(" 你要筷子...");
synchronized (chopsticks) {
System.out.println(" 你得到了筷子");
System.out.println(" 你要碗...");
sleep();
synchronized (bowl) {
System.out.println(" 你得到了碗");
sleep();
System.out.println(" 你吃饭咯");
}
}
}
}).start();
}
static void sleep() {
try {
Thread.sleep(new Random().nextInt(500));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
6、被抛弃的方法 stop, suspend, resume
为什么被抛弃?oracle原文:https://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html
简单来说就是
1、stop执行后:会马上释放对象锁,但是这可能会带来不可预料的结果。
2、suspend 和 resume: suspend调用后会释放占用的锁,如果不能保证resume的调用的话很容易造成死锁问题。
为什么java线程不推荐调用stop,suspend,resume方法