1.wait()和notify()
public class MyThread {
public static List<String> list = new ArrayList<String>();
public static void main(String[] args) {
Thread thread = new Thread(){
@Override
public void run() {
synchronized (MyThread.class) {
if(list.size() == 0){
try {
MyThread.class.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
list.remove(0);
System.out.println("消费了一个");
}
}
};
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
thread.start();
list.add("aaa");
System.out.println("生产了一个");
synchronized (MyThread.class) {
MyThread.class.notify();
}
}
}
wait和notify方法都必须在synchronized代码块中执行,
源码中有以下注释
* This method should only be called by a thread that is the owner
* of this object's monitor. See the {@code notify} method for a
* description of the ways in which a thread can become the owner of
* a monitor.
synchronized代码块通过javap生成的字节码中包含 monitorenter 和 monitorexit 指令。而wait()和notify()方法执行时都需要拿到当前对象的监视器。
运行以上代码得到如下
如果去掉synchronized则会报错
另外补充很重要的一点,jdk在wait()方法注释中不推荐使用if推荐使用while来进行判断。
* As in the one argument version, interrupts and spurious wakeups are
* possible, and this method should always be used in a loop:
* <pre>
* synchronized (obj) {
* while (<condition does not hold>)
* obj.wait();
* ... // Perform action appropriate to condition
* }
* </pre>
因为线程在等待时可能由于伪唤醒而进入运行状态。伪唤醒是指由于操作系统、cpu调度的原因等不是由java代码进行的唤醒操作。
因此代码作如下改动:
public class MyThread {
public static List<String> list = new ArrayList<String>();
public static void main(String[] args) {
Thread thread = new Thread(){
@Override
public void run() {
synchronized (MyThread.class) {
while(list.size() == 0){
try {
MyThread.class.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
list.remove(0);
System.out.println("消费了一个");
}
}
};
thread.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
list.add("aaa");
System.out.println("生产了一个");
synchronized (MyThread.class) {
MyThread.class.notify();
}
}
}
wait()和sleep()区别
1.sleep()属于Thread类,wait()属于object类
2.sleep()会出让cpu但不会释放对象锁,wait()则会释放对象锁等待唤醒
2.LockSupport中的park() 和 unpark()
public class MyThread2 {
public static volatile List<String> list = new ArrayList<String>();
public static void main(String[] args) {
Thread thread = new Thread(){
@Override
public void run() {
if(list.size() == 0){
LockSupport.park(MyThread2.class);
}
list.remove(0);
System.out.println("消费了一个");
}
};
thread.start();
list.add("aaa");
System.out.println("生产了一个");
LockSupport.unpark(thread);
}
}
park()、unpark()和wait()、notify()的区别
1.park、unpark不需要写在synchronized代码块中
2.先执行多次unpark后,在第一次执行执行park时会立刻接着执行
3.volatile和synchronized
一般来说,我们拿到变量都是拿到其在内存中的一个副本,修改也是对副本进行修改。在一个代码块中对其进行操作完成后才更新的内存之中。volatile修饰的变量能确保变量每次进行修改都是直接在内存上进行修改。
synchronized确保操作的原子性,就是要么全都执行要么全都不执行。但synchronized会导致指令重排序,在一个线程拿到对象的同步锁之后,其他所有线程都处于等待。释放锁之后,其他想要拿到该对象的同步锁的线程是随机一个拿到锁,并不是按时间顺序拿到锁,因此可能不是按代码顺序来执行的。而volatile禁止指令重排序,同时性能上也优于synchronized;
synchronized和volatile不能互相替代。synchronized确保可见性、原子性,不能确保有序性;volatile确保可见性、有序性,不能确保原子性。
补充:synchronized修饰方法时拿到的是对象锁
4.双重检查锁
高并发下单例的获取方法
public class InstanceFactory {
public volatile static Instance instance;
public Instance getInstance(){
if(instance == null){
synchronized (InstanceFactory.class) {
if(instance == null){
instance = new Instance();
}
}
}
return instance;
}
}
高并发下缓存的获取
public class CacheDemo {
public <T>T getSomeInfo(){
T t = getCache();//获取缓存
if(t == null){
synchronized (CacheDemo.class) {
t = getCache();//获取缓存
if(t == null){
t = getDB();//从数据库获取
setCache(t);
}
}
}
return t;
}
private <T> T getDB() {
// TODO Auto-generated method stub
return null;
}
private <T> T getCache() {
// TODO Auto-generated method stub
return null;
}
private <T> void setCache(T t){
// TODO Auto-generated method stub
}
}
5.join()
join()是Thread类里的方法
调用t.join()方法会阻塞当前线程,直到线程t结束