1、线程中的常见方法,sleep wait notify notifyall join
sleep和wait不同点
-
sleep休眠期间被中断会抛出异常并且清除中断状态
-
wait必须在同步方法中进行,sleep不需要
-
在同步方法里执行sleep方法时,不会释放monitor锁,但是wait方法会释放monitor锁
-
sleep方法短暂休眠之后会主动退出阻塞,而没有指定时间的 wait方法则需要被其他线程中断后才能退出阻塞
-
sleep是Thread的方法,而wailt notify等是Object的方法
-
sleep不关心线程间同步问题,而wait解决线程间同步
sleep和wait相同点
-
都可以使线程阻塞
-
都可以响应中断
join方法是如何加入线程的
-
join线程时,会使原线程进入等待状态
-
wait为什么必须放在同步代码块,而且对象是Object
线程更安全,防止死锁和永久等待,放在Object的原因是,java设计的每个类都是一把锁,都有保存monitor的预留,不要用Thread类的对象进行wait,notify等操作,因为Thread类底层执行完后会有默认的唤醒操作,打乱我们正常的逻辑。
2、停止线程
停止线程是通知,而不是强制。否则会造成线程或者业务逻辑紊乱。
-
run方法中没有sleep以及wait方法的时候停止线程
-
设置响应中断条件boolean interrupted = Thread.interrupted(),判断线程是否收到了中断信号,进行响应
携带sleep时如何响应中断
sleep响应中断的方式是,直接抛出异常,并且会清除中断标志,会导致获取不到中断状态,可能在循环中会有中断不了线程的情况。最好的解决办法是传递中断信号
3、线程的状态及状态转换条件
-
新建线程时,线程进入了NEW状态
-
当调用了Thread.start()时,线程进行了Runnable状态
-
当进入了由synchronized修饰的代码块,并且没有拿到锁时,进入blocked状态
-
当获取到monitor时,返回到Runnable状态
-
当调用了Object.wait()方法,进入到waiting
-
当调用了join方法时主线程进入等待状态
-
调用了notify()方法或者notifyAll()方法时回到Runnable
-
当调用了sleep方法,wait方法,join方法 并指定了时间时 线程进入限时等待状态
-
当等待时间到了 或者线程被唤醒了 返回runnable状态
-
线程执行完成进入了终止状态
4、使用synchronized、wait、sleep模拟一个简易生产者消费者模式
public class ProduceConsumerBuild {
public static void main(String[] args) {
Storage storage = new Storage();
Produce produce = new Produce(storage);
Consumer consumer = new Consumer(storage);
new Thread(produce).start();
new Thread(consumer).start();
}
static class Storage {
private int maxSize;
private List<Date> queue;
Storage() {
maxSize = 10;// 最大容量10
queue = new ArrayList<>();// 初始化空队列
}
public synchronized void put() {
if (queue.size() == this.maxSize) {
System.out.println("队列满了...");
// 满了 进入等待
try {
wait();
} catch (InterruptedException e) {
System.out.println("生产数据时产生异常...");
}
}
queue.add(new Date());
System.out.println("队列中存有" + queue.size() + "个产品");
// 唤醒消费者
notify();
}
public synchronized void get() {
if (queue.size() == 0) {
System.out.println("队列空了...");
// 消费完了 空了 进入等待
try {
wait();
} catch (InterruptedException e) {
System.out.println("消费数据时产生错误...");
}
}
Date remove = queue.remove(0);
System.out.println("消费者消费了" + remove + " 还剩余" + queue.size() + "个产品");
// 唤醒消费者
notify();
}
}
// 生产者
static class Produce implements Runnable {
Storage storage;
Produce(Storage storage) {
this.storage = storage;
}
@Override
public void run() {
while(true){
storage.put();
}
}
}
// 消费者
static class Consumer implements Runnable {
Storage storage;
Consumer(Storage storage) {
this.storage = storage;
}
@Override
public void run() {
while(true){
storage.get();
}
}
}
}
5、如何使用redis实现一把完整的分布式锁