Java关键字synchronized的使用
该篇文章主要内容有:
- 使用wait、notify实现线程间的通信
- 生产者/消费者模式的实现
- 方法join的使用
- ThreadLocal类的使用
线程与线程之间不是独立的个体,它们彼此可以互相通信和协作。
方法wait()的作用是使当前执行代码的线程进行等待,在调用该方法之前, 线程必须获得该对象的对象级别锁,即只能在同步方法或同步块中调用,不然将会抛出异常IllegalMonitorStateException。
方法notify()也要在同步方法或同步块中调用,调用该方法时,如果有多个线程等待该对象锁,则由线程规划器随机挑选一个呈wait状态的线程,对其发出notify通知。需要注意的是:在执行notify方法后,当前线程并不会马上释放对象锁,wait状态的线程也不会马上获得锁,要等到执行notify线程将程序执行完,即退出同步代码后才释放锁。如果没有调用notify,即使该线程释放了锁,wait状态的线程也无法再次获得锁 。
注:
- wait()释放锁,notify不释放锁。
- wait()方法与sleep()方法区别也是sleep不会释放锁
- wait(long)表示等待该时间内是否有线程对其进行唤醒,否则超过这个时间自动唤醒。
当线程处于wait()状态时,调用该对象的interrupt方法会出现异常。
1、使用多次notify()
2、使用notifyAll()一次唤醒全部
注:
1. notify方法的执行不应该在wait()方法之前执行,不然会打乱正常的运行顺序。
2. 还需要注意的是,wait等待的条件发生了变化,也会打乱正常的运行顺序。
出现错误的原因是
notifyAll唤醒了两个等待的进程,而list只有一个数据可供删除,所以出现了索引错误。解决办法就是将if判断设置为while循环判断。
从结果可以看出来此时两个线程是交替执行的,但是如果是多线程(多生产者与消费者)的话,则可能出现假死现象,因为你使用notify唤醒的线程一旦是同类,例如:生产者唤醒了生产者,此时所有线程都无法继续运行下去了。这个问题的解决办法就是使用notidyAll唤醒所有的线程,但效率不高。
MyStack.java
package com.zjw;
import java.util.ArrayList;
public class MyStack {
private String lock;
private ArrayListlist;
private int i=0;
public MyStack(String lock){
this.lock=lock;
list=new ArrayList<>();
}
public void p(){
try {
Thread.sleep(100);//设置睡眠时间防止同一线程一直争抢到锁
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock) {
while (list.size()>50) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
list.add(i);
System.out.println("我是生产者"+Thread.currentThread().getName()+"我生产了数据"+i);
i++;
lock.notifyAll();
}
}
public void c(){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock) {
while (list.size()==0) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int j=list.remove(0);
System.out.println("我是消费者"+Thread.currentThread().getName()+"我去除了数据"+j);
lock.notifyAll();
}
}
}
方法join()的作用是主线程(在线程内使用该方法)等待子线程执行完成之后再结束。
如果在main方法不使用join的话,则无法判断何时才执行后面的代码,join有点类似与同步的运行效果,在内部使用wait()方法进行等待,而synchronized关键字使用的是“对象监视器”。
join(long)方法是设定等待的时间,那么它与sleep(long)的方法的区别呢?那就是它内部使用的是wait(long),它在等待的时候会释放锁。
注:因为join()与释放锁这个操作,所以可能释放之后其他线程争抢锁有不同的情况,导致程序结果也有不同的结果。
类ThreadLocal可以实现每一个线程都有自己的共享变量,解决变量在不同线程之间的隔离性,使每个线程拥有自己的值。
注:第一次调用get()方法(如果之前没有使用set方法的话)返回的是NULL,如果想让它第一次不返回NULL,则可以自己继承ThreadLocal然后重写 initialValue方法。



