基础知识巩固五(问题)

一、没有引用变量,那些在堆里new出来的值何时回收

首先引用变量是在栈里的,new 出来的对象是在堆里的,二者的联系也就是堆里的对象的地址赋给了引用变量,引用变量存储的也就是这个对象的引用

那么如果没有引用变量,也就是new Circle()这种,没有引用变量,它首先会在堆里创建一个属于它的内存空间,然后如果是new Circle().method()这样

可以使用对象的方法,否则,当系统内存不足或者程序运行完后,堆里面的这块内存都会被虚拟机自动收回。

存储区域存储内容优点缺点回收
基本类型的变量和对象的引用变量 存取速度比堆要快,仅次于寄存器,栈数据可以共享 存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。栈中主要存放一些基本类型的变量 当超过变量的作用域后,Java会自动释放掉该变量,内存空间可以立即被另作他用
由new等指令创建的对象和数组 可以动态地分配内存大小,生存期也不必事先告诉编译器 由于要在运行时动态分配内存,存取速度较慢 由Java虚拟机的自动垃圾回收器来回收不再使用的数据

参考链接:

http://blog.csdn.net/bangbt/article/details/6232299

http://blog.csdn.net/weixin_35813749/article/details/52374749

二、关于wait/notify与await/signal的

wait/notify都是在java.lang.object包下

wait() 与 notify/notifyAll 方法必须在同步代码块(synchronized)中使用

notify 通知的顺序不能错

假设在线程A中执行wait(),在线程B中执行notify()。但如果线程B先执行了notify()然后结束了,线程A才去执行wait(),那此时,线程A将无法

被正常唤醒了(还可以通过interrupt()方法以抛出异常的方式唤醒)。

 

await/signal都是java.util.concurrent.locks包的接口Condition的方法,Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,也就是说,await/signal比wait/notify更加的灵活。

synchronized 块或方法对于一个对象只有一个condition queue,这样如果在这个queue上如果有多个condition predicate, 比如isFull(),isEmpty() ,

就必须用notfiyAll()方法, 会有context switch及获取锁的性能损失。

Lock 接口的实现允许锁在不同的作用范围内获取和释放,并允许以任何顺序获取和释放多个锁

参考链接:

https://www.cnblogs.com/jalja/p/5895051.html

https://www.cnblogs.com/alphablox/archive/2013/01/20/2868479.html

代码部分: 1 public class Test {

 2     public static Buffer buffer = new Buffer();
 3     static StringBuffer sb = new StringBuffer();
 4 
 5     static class Producer implements Runnable{
 6         int i = 1;
 7         @Override
 8         public void run() {
 9             try{
10                 while (true){
11                     buffer.write(i++);
//sb.append("生产者生产产品:" + i).append(" ");
//今天在把输出控制台的语句放在了这里出现了问题,下边解释
12 Thread.sleep((int)(Math.random()*100)); 13 if (i >= 70) 14 break; 15 } 16 System.out.println(sb.toString()); 17 }catch (InterruptedException e){ 18 e.printStackTrace(); 19 } 20 21 } 22 } 23 24 static class Consumer implements Runnable{ 25 @Override 26 public void run() { 27 try { 28 while (true) { 29 buffer.read(); 30 Thread.sleep((int) (Math.random() * 100)); 31 } 32 } catch (InterruptedException e) { 33 e.printStackTrace(); 34 } 35 } 36 } 37 38 39 static class Buffer{ 40 public static Lock lock = new ReentrantLock(); 41 public static Condition isFull = lock.newCondition(); 42 public static Condition isEmpty = lock.newCondition(); 43 Queue<Integer> queue = new PriorityQueue<>(); 44 public void write(int i) { 45 lock.lock(); 46 try { 47 while(!queue.isEmpty()){ 48 isFull.await(); 49 } 50 queue.offer(i); 51 sb.append("生产者生产产品:" + i).append(" ");//important 52 isEmpty.signal(); 53 } catch (InterruptedException e) { 54 e.printStackTrace(); 55 }finally { 56 lock.unlock(); 57 } 58 } 59 60 public int read(){ 61 int result = 0; 62 lock.lock(); 63 try { 64 while(queue.isEmpty()){ 65 isEmpty.await(); 66 } 67 result = queue.remove(); 68 sb.append("消费者购买商品:" + result).append(" ");//important 69 isFull.signal(); 70 } catch (InterruptedException e) { 71 e.printStackTrace(); 72 }finally { 73 lock.unlock(); 74 return result; 75 } 76 } 77 } 78 public static void main(String[] args){ 79 ExecutorService executor = 80 Executors.newFixedThreadPool(2); 81 executor.execute(new Producer()); 82 executor.execute(new Consumer()); 83 executor.shutdown(); 84 } 85 86 }

总结:

使用condition对象必须先获取到锁,也就是必须要lock.lock().其次你想通过控制台输出语句的时候要注意,condition.signal()方法会唤醒原来阻塞的线程,然后来争夺资源,

这样控制台的输出语句可能就没法正常输出,也就是生产者会一次性生产两个,就是因为提前唤醒抢夺资源这个点(PS:连i++这个过程都没法正常进行,因为这个表达式不是原子的),所以针对不是原子的操作,要多注意线程唤醒语句:condition.signal().

三、关于LinkedList与Stack

LinkedList是通过双向链表实现的,他实现了Dueue双向队列接口

Stack是Vector的子类,是线程安全的,相当于数据结构里堆栈的结构,先进后出

额外补充:

ArrayList、Vector、Stack都是通过数组实现的

四、信号量(Semaphore)的运用

在java中,使用了synchronized关键字和Lock锁实现了资源的并发访问控制,在同一时间只允许唯一了线程进入临界区访问资源(读锁除外),这样子控制的主要目的是为了解决多个线程并发同一资源造成的数据不一致的问题。在另外一种场景下,一个资源有多个副本可供同时使用,比如打印机房有多个打印机、厕所有多个坑可供同时使用,这种情况下,Java提供了另外的并发访问控制--资源的多副本的并发访问控制,今天学习的信号量Semaphore即是其中的一种。

参考链接:

http://blog.csdn.net/zbc1090549839/article/details/53389602

https://www.cnblogs.com/XHJT/p/3910406.html

测试代码:

 1 public class Test {
 2    private static Semaphore semaphore = new Semaphore(2);
 3     
 4     public static void main(String[] args){
 5         for (int i = 0;i<10;i++){
 6             new Thread(new Runnable() {
 7 
 8                 @Override
 9                 public void run() {
10                     while(true) {
11                         try {
12                             semaphore.acquire(1);
13                             System.out.println(Thread.currentThread().getName());
14                             Thread.sleep(1000);
15                         } catch (InterruptedException e) {
16                             e.printStackTrace();
17                         }finally {
18                             semaphore.release(1);
19                         }
20                     }
21                 }
22             },"线程:"+i).start();
23         }
24 
25     }
26 
27 }
原文地址:https://www.cnblogs.com/shigeng/p/8551855.html