线程基础之notify和wait的异同
同步代码出现的问题 IllegalMonitorStateException
在学习多线程的过程中,接触到wait
wait 是来自Object类,
sleep 是指Thread类,这里的sleep是谁调用,谁睡眠
如果你定义A线程,A线程下面有B,A调用B的sleep方法,A睡眠;
sleep不会释放锁
wait可以释放锁,进入线程等待池等待,使得其他线程可以使用同步控制块或者方法。
- Thread.Sleep(0) 的作用是“触发操作系统立刻重新进行一次CPU竞争”。
java.lang.IllegalMonitorStateException是在调用object的wait和notify,notifyAll方法的时候可能会出现的异常
在调用上述三个方法的时候,线程必须获得该对象的对象级别锁,换句话说,出现这个异常的原因是因为,调用wait和notify,notifyAll的对象没有在同步方法(synchronized修饰的方法)或者同步代码块(synchronized(x){})中。
第一:必须要在被synchronized关键字控制的同步代码块中,才能调用这些方法。
第二,调用者必须为你当前的锁对象。
class DemoOne12345 {
public static void main(String[] args) {
Object object = new Object();
ThreadImpl thread = new ThreadImpl(object);
ThreadImpl thread1 = new ThreadImpl(object);
// 开启一个线程
thread1.start();
thread.start();
}
}
//直接调用wait()方法,此时实际上是this.wait(),实际调用者为ThreadImp的实例对象thread。
// 而在修改后的代码中,wait()方法的调用者为lock。
class ThreadImpl extends Thread {
// 线程锁对象
private Object lock;
public ThreadImpl(Object lock) {
this.lock = lock;
}
// 重写线程的run方法,线程的业务执行逻辑
@Override
public void run() {
synchronized (lock) {
// 执行一个for循环,打印0~9之间的数字
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "当前数字为:" + i);
}
try {
/** 调用wait方法控制线程
如果是this.wait说明线程本身并没有
调用object的wait方法,是指,你的锁对象object在管理这些线程,因此在调用过程中,
调用这应该是object对象,就是你的说对象
*/
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.notifyAll();
}
}
}
}
class DemoOne12345 {
public static void main(String[] args) {
Object object = new Object();
ThreadImpl thread = new ThreadImpl(object);
ThreadImpl thread1 = new ThreadImpl(object);
// 开启一个线程
thread1.start();
thread.start();
}
}
//直接调用wait()方法,此时实际上是this.wait(),实际调用者为ThreadImp的实例对象thread。
// 而在修改后的代码中,wait()方法的调用者为lock。
class ThreadImpl extends Thread {
// 线程锁对象
private Object lock;
public ThreadImpl(Object lock) {
this.lock = lock;
}
// 重写线程的run方法,线程的业务执行逻辑
@Override
public void run() {
synchronized (this) {
// 执行一个for循环,打印0~9之间的数字
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "当前数字为:" + i);
}
try {
/** 调用wait方法控制线程
如果是this.wait说明线程本身并没有
调用object的wait方法,是指,你的锁对象object在管理这些线程,因此在调用过程中,
调用这应该是object对象,就是你的说对象
*/
this.wait(1000);
} catch (Exception e) {
System.out.println(e);
e.printStackTrace();
}finally {
this.notifyAll();
}
}
}
}
- sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常
分析下 this锁和object锁
如果要调用某个对象的wait方法,那么必须这么调用
- 这里咱们接着说
已经知道了只有在同步方法块中才可以使用对应的wait方法
这样就到了如何正确使用对用的锁
现在这个维度,锁有两种:类锁和对象锁
下面的第一个得到的是lock锁,就是指的当前的对象锁
而第二个方法锁的是lock,但是却想让这个类去等待,显然 是不行的
// 正确
Object lock = new Object();
synchronized(lock){
lock.wait();
}
// 错误
Object lock = new Object();
synchronized(lock){
this.wait();
}
分析一下,对于单个object,我们new多个线程,去操作一个object,这里的synchronized锁住object,那么,这些线程需要先得到这个object‘的锁,就是object锁
// 正确
Object lock = new Object();
synchronized(lock){
lock.wait();
}
分析一下,对于this,我们new多个线程,去操作这个类,这里的synchronized锁住this,那么,这些线程需要先得到这个类的锁,就是this锁
// 正确
synchronized(lock){
this.wait();
}
这里继续更新,关于类锁,可以通过一下三种方式实现
- 说白了,也是两种:静态资源加锁 / 对当前类 .class 属性加锁
给出几个case,关于顺序打印问题
class TestDemp{
public static void main(String[] args) throws InterruptedException {
Object object = new Object();
SolutionTempDempOne1 s = new SolutionTempDempOne1(object);
new Thread(()->{
try {
s.sy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "cur1").start();
new Thread(()->{
try {
s.sy();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"cur2").start();
}
}
class SolutionTempDempOne1 {
private Object object;
public SolutionTempDempOne1(Object object){
System.out.println("new object: " + Thread.currentThread().getName());
this.object = object;
}
public void sy() throws InterruptedException {
synchronized(object){
try{
object.wait(2000);
System.out.println("currentThread"+Thread.currentThread().getName());
System.out.println("this is 123");
}catch (Exception e){
System.out.println(e);
}finally {
object.notifyAll();
}
}
}
}
对象锁和类锁再次case
这里的case给出的是另一种情况,顺序增加数;
给出不加锁:
class ThreadNoStaticDemo{
private static Integer num = 0;
private static Object lock = new Object();
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
ThreadNoStaticDemo s = new ThreadNoStaticDemo();
try {
s.addNum();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"curThread " + i).start();
// System.out.println(ThreadNoStaticDemo.num);
}
// System.out.println(ThreadNoStaticDemo.num);
}
public int getNum(){
return num;
}
public void addNum() throws InterruptedException {
// synchronized(lock) {
num++;
// Thread.sleep(200);
System.out.println(Thread.currentThread().getName() + ": " + num);
// }
}
}
这里的结果不仅仅不等于10,而且不是顺序打印。
下面是静态加锁:
class ThreadNoStaticDemo{
private static Integer num = 0;
private static Object lock = new Object();
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
ThreadNoStaticDemo s = new ThreadNoStaticDemo();
try {
s.addNum();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"curThread " + i).start();
// System.out.println(ThreadNoStaticDemo.num);
}
// System.out.println(ThreadNoStaticDemo.num);
}
public int getNum(){
return num;
}
public void addNum() throws InterruptedException {
synchronized(lock) {
num++;
Thread.sleep(200);
System.out.println(Thread.currentThread().getName() + ": " + num);
}
}
}
结果可以看出,由于操作的静态对象
其已经被锁住,所以每个时刻的锁释放后才可以被获得。
class ClassLock {
private static Object lock = new Object();
public static void main(String[] args){
for (int i = 0; i < 5; i++) {
new Thread(()->{
ClassLock classLock = new ClassLock();
// 方式 1
try {
classLock.lockStaticObjectField();
} catch (InterruptedException e) {
e.printStackTrace();
}
// System.out.println(Thread.currentThread().getName() + ": starting!");
},"Thread"+i).start();
}
}
/**
* 锁住静态变量
* @throws InterruptedException
*/
public void lockStaticObjectField() throws InterruptedException{
synchronized (lock){
System.out.println(Thread.currentThread().getName());
Thread.sleep(2*1000);
}
}
}
class TestDemp{
public static void main(String[] args) throws InterruptedException {
Object object = new Object();
SolutionTempDempOne1 s = new SolutionTempDempOne1(object);
for (int i = 0; i < 10; i++) {
new Thread(()->{
try {
s.sy();
} catch (InterruptedException e) {
}
}, "cur"+ i).start();
}
}
}
class SolutionTempDempOne1 {
private Object object;
public SolutionTempDempOne1(Object object){
System.out.println("new object: " + Thread.currentThread().getName());
this.object = object;
}
public void sy() throws InterruptedException {
synchronized(object){
try{
object.wait(2000);
System.out.println("currentThread"+Thread.currentThread().getName());
System.out.println("this is 123");
}catch (Exception e){
System.out.println(e);
}finally {
object.notify();
}
}
}
}
间隔顺序打印
class TestDemp{
public static void main(String[] args) throws InterruptedException {
Object object = new Object();
SolutionTempDempOne1 s = new SolutionTempDempOne1(object);
for (int i = 0; i < 10; i++) {
s.sy();
// new Thread(()->{
// try {
// s.sy();
// } catch (InterruptedException e) {
// }
// }, "cur"+ i).start();
}
}
}
class SolutionTempDempOne1 {
private Object object;
public SolutionTempDempOne1(Object object){
System.out.println("new object: " + Thread.currentThread().getName());
this.object = object;
}
public synchronized void sy() throws InterruptedException {
try{
this.wait(2000);
System.out.println("currentThread"+Thread.currentThread().getName());
System.out.println("this is 123");
}catch (Exception e){
System.out.println(e);
}finally {
}
}
}