问题一:用程序实现两个线程交替打印0~100的奇偶数
基本思路:synchronized
package threadobjectclasscommonmethods;
import jdk.nashorn.internal.ir.Block;
//描述 两个线程交替打印0~100的奇偶数,用synchronized关键字实现
public class WaitNotifyPrintOddEvenSyn {private static int count;private static Object lock = new Object();//新建两个线程,一个处理偶数,一个处理奇数(位运算)//用synchronized来执行//偶数线程public static void main(String[] args) {new Thread(new Runnable() {@Overridepublic void run() {while (count < 100) {//加锁保证只有一个线程在运行,如果偶数线程抢到了多次锁也没有关系,// 因为在count++后就是奇数,奇数时if语句就会判断时非,就不会进入sout语句,count就不会加一,依旧不会进行偶数线程的打印synchronized (lock) {//使用位运算提高效率,取出最后一位,是1就是奇数,是0就是偶数if ((count & 1) == 0) {System.out.println(Thread.currentThread().getName() + ":" + count++);}}}}}, "偶数").start();//奇数线程new Thread(new Runnable() {@Overridepublic void run() {//控制在100以内数的循环while (count < 100) {synchronized (lock) {//使用位运算提高效率,取出最后一位,是1就是奇数,是0就是偶数if ((count & 1) == 1) {System.out.println(Thread.currentThread().getName() + ":" + count++);}}}}}, "奇数").start();}
}
优化方法
使用wait和notify
package threadobjectclasscommonmethods;//描述 两个线程交替打印0~100的奇偶数,用wait和notify
public class WaitNotifyPrintOddEveWait {//1.拿到锁,我们就打印//2.打印完,唤醒其他线程,自己就休眠static class TurningRunner implements Runnable {private static int count = 0;private static Object lock = new Object();public static void main(String[] args) throws InterruptedException {new Thread(new TurningRunner(), "偶数").start();new Thread(new TurningRunner(), "奇数").start();}@Overridepublic void run() {while (count <= 100) {synchronized (lock) {System.out.println(Thread.currentThread().getName() + ":" + count++);lock.notify();if (count <= 100) {try {//如果任务没有结束,就让出当前的锁,并且自己休眠lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}}}}}
}
问题二:手写生产者消费者设计模式
//用wait/notify来实现import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;public class ProducerConsumerModel {public static void main(String[] args) {EventStorage eventStorage = new EventStorage();Producer producer = new Producer(eventStorage);Consumer consumer = new Consumer(eventStorage);new Thread(producer).start();new Thread(consumer).start();}//生产类static class Producer implements Runnable {private EventStorage storage;public Producer(EventStorage storage) {this.storage = storage;}@Overridepublic void run() {for (int i = 0; i < 100; i++) {storage.put();}}}//消费类static class Consumer implements Runnable {private EventStorage storage;public Consumer(EventStorage storage) {this.storage = storage;}@Overridepublic void run() { for (int i = 0; i < 100; i++) {try {storage.take();} catch (InterruptedException e) {e.printStackTrace();}}}
}
static class EventStorage {private int maxSize;//存储库private LinkedList<Date> storage;public EventStorage() {maxSize = 10;storage = new LinkedList<Date>() {};}//生产者使用方法 public synchronized void put() {//如果生产者已经满了,等待while (storage.size() == maxSize) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}}//没有满就加入 storage.add(new Date());System.out.println("仓库里面有了" + storage.size()+ "个产品");notify();}//消费者使用方法public synchronized void take() throws InterruptedException {//消费者取空情况while (storage.size() == 0) {wait();}//poll方法拿到并删除System.out.println("拿到了" + storage.poll() + "现在仓库还剩下" + storage.size());notify();}}}
问题三:为什么wait()需要在同步代码块内使用,而sleep()不需要
同步代码块:使用synchronized关键字加上一个锁对象来定义一段代码,这就叫同步代码块
多个同步代码块如果使用相同锁对象,那么它们就是同步的
主要是为了通信的可靠一般wait和notify配合使用,防止死锁和永久等待的发生,在所以一般线程中需要配合的这种方式都放到同步代码块中去做,而sleep本身针对自己线程,和其他线程关系不大,所以就不需要放到同步代码块中
问题四:为什么线程通信的方法wait(),notify()和notifyAll()被定义在Object类里?而sleep定义在Thread类里?
java中的wait、notify、notifyAll是锁级别的操作,锁是属于某一个对象的,synchronized用的锁是存在Java对象头中的,每一个对象的对象头中都会保存对象状态的,锁是绑定某一个对象中,并不是线程中。如果把这几个方法定义到线程中,会有很大的局限性,比如某一个线程持有多把锁,就没有办法适应灵活的逻辑了,锁对于每一个对象都是适用的,所以就把这几个方法定义到Object里面
问题五:wait方法是属于Object对象的,那调用Thread.wait会怎么样?
由于Thread类比较特殊,它在线程退出的时候自动执行notify,这样就会让我们设计的流程有所干扰。所以当使用wait方法或者创建一个锁对象的时候,就不要用Thread类。
问题六:如何选择notify还是notifyAll?
notify和notifyAll的区别在于,notify只能唤醒等待池中的一个线程,而notifyAll可以唤醒等待池中的所有线程,被唤醒的锁会进入锁池进行锁的争夺。当有多个线程需要被唤醒的时候,使用notify不知道唤醒的是哪一个线程,而使用notifyAll可以通知到所有的线程,一般来说使用notifyAll比较稳妥一些
问题七:notifyAll之后所有的线程都会再次抢夺锁,如果某线程抢夺失败怎么办?
无非是继续等待,等待抢到锁的线程释放这把锁再继续抢夺
用suspend()和resume()来阻塞线程可以么?为什么?
不可以,suspend不会释放锁,如果resume发生在suspend的前面,锁就会被永久挂起,别的线程就无法使用此锁,容易造成死锁