java面试复习3
解决线程安全问题: Lock锁
Lock锁 和synchronized区别?
相同: 都可以解决线程安全问题
不同 synchronized机制在执行完相应的同步代码以后,自动释放同步监视器, Lock需要 手动的启动同步(Lock()), 结束时也需要手动结束(unlock())
优先使用顺序:
Lock 同步代码块(已经进入了方法体,分配了相应资源) 同步方法 (在方法体之外)
解决线程安全问题有几种?
-
Lock
-
synchronized(代码块和方法)
练习:
代码: 银行有一个账户,有两个储户分别向同一个账户存3000元,每次存1000,存3次。每次存完打印账户余额。
-
分析:
-
- 明确哪些代码是多线程? 是, 两个储蓄线程
-
- 是否有共享数据? 有, 账户余额
-
3.是否有线程安全问题? 有。有共享数据, 有两个线程都对它操作。
-
- 如何解决线程安全问题? 同步机制: 3种
线程的通信
使用两个线程打印 1-100。线程1, 线程2 交替打印:
-
涉及到3个方法:
-
wait():线程进入阻塞,并释放同步监视器
-
notify()“就会环形被wait的一个线程,如果有多个,就唤醒优先级高的
-
notifyAll(): *
-
说明: 这三个方法只有在synchronized方法或synchronized代码块中才能使用,否则会报 java.lang.IllegalMonitorStateException异常。
-
这三个方法的调用者必须是同步代码块或者同步方法中的同步监视器
-
-
这三个方法是定义在object类下面的,不是Thread类下面的
-
* 面试题: sleep()和wait()异同?
-
共同点: 一旦执行,都可以使得当前线程进入阻塞
-
不同点: 1. 两个方法声明的位置不同: Thread类中声明sleep(), Object类中申明wait()
-
不同点: 2. 调用要求不同: sleep()可以在任何需要的场景下调用, wait()必须在同步代码块或者同步方法
-
不同点: 3. 释放同步监视器的问题: 如果两个方法都用在同步代码块或者同步方法中,sleep()不会释放释放同步监视器, wait()会释放释放同步监视器
练习:经典例题:生产者/消费者问题
生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处 取走产品,店员一次只能持有固定数量的产品(比如:20),如果生产者试图 生产更多的产品,店员会叫生产者停一下,如果店中有空位放产品了再通 知生产者继续生产;如果店中没有产品了,店员会告诉消费者等一下,如 果店中有产品了再通知消费者来取走产品。
这里可能出现两个问题:
生产者比消费者快时,消费者会漏掉一些数据没有取到。
消费者比生产者快时,消费者会取相同的数据。
创造多线程的第三种方式:实现Callable接口
与使用Runnable相比, Callable功能更强大些
- 相比run()方法,可以有返回值
- 方法可以抛出异常
- 支持泛型的返回值
- 需要借助FutureTask类,比如获取返回结果
步骤: 1, 2, 3.创建callable接口实现类的对象
4.将此Callable接口实现类的对象作为传递到FutureTask构造器中
5.将FutureTask对象作为参数传到Thread类的构造器中,创建Thread对象,并启动
创造多线程的第四种方式:线程池
提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。
好处:
-
提高响应速度(减少了创建新线程的时间)
-
降低资源消耗(重复利用线程池中线程,不需要每次都创建)
-
便于线程管理
-
corePoolSize:核心池的大小
-
maximumPoolSize:最大线程数
-
keepAliveTime:线程没有任务时最多保持多长时间后会终止
Ok, It’s time for String