2-29号 牛客刷题

volatile关键字

Java中的volatile关键字的功能

volatile是java中的一个类型修饰符。它是被设计用来修饰被不同线程访问和修改的变量。如果不加入volatile,基本上会导致这样的结果:要么无法编写多线程程序,要么编译器 失去大量优化的机会。

1,可见性

可见性指的是在一个线程中对该变量的修改会马上由工作内存(Work Memory)写回主内存(Main Memory),所以会马上反应在其它线程的读取操作中。顺便一提,工作内存和主内存可以近似理解为实际电脑中的高速缓存和主存,工作内存是线程独享的,主存是线程共享的。

2,禁止指令重排序优化

禁止指令重排序优化。大家知道我们写的代码(尤其是多线程代码),由于编译器优化,在实际执行的时候可能与我们编写的顺序不同。编译器只保证程序执行结果与源代码相同,却不保证实际指令的顺序与源代码相同。这在单线程看起来没什么问题,然而一旦引入多线程,这种乱序就可能导致严重问题。volatile关键字就可以从语义上解决这个问题。

注意,禁止指令重排优化这条语义直到jdk1.5以后才能正确工作。此前的JDK中即使将变量声明为volatile也无法完全避免重排序所导致的问题。所以,在jdk1.5版本前,双重检查锁形式的单例模式是无法保证线程安全的。因此,下面的单例模式的代码,在JDK1.5之前是不能保证线程安全的。

volatile不能保证线程安全;

类多态问题

package Wangyi;
class Base
{
    public void method()
    {
        System.out.println("Base");
    } 
}
class Son extends Base
{
    public void method()
    {
        System.out.println("Son");
    }
     
    public void methodB()
    {
        System.out.println("SonB");
    }
}
public class Test01
{
    public static void main(String[] args)
    {
        Base base = new Son();
        base.method();
        base.methodB();
    }
}

编译不通过
Base base=new Son(); 是多态的表示形式。父类对象调用了子类创建了Son对象。
base调用的method()方法就是调用了子类重写的method()方法。
而此时base还是属于Base对象,base调用methodB()时Base对象里没有这个方法,所以编译不通过。
要想调用的话需要先通过SON son=(SON)base;强制转换,然后用son.methodB()调用就可以了。
这类多态问题中,无论向上或向下转型,都记住一句话就可以了。
编译看左边,运行看右边。意思编译时候,看左边有没有该方法,运行的时候结果看 new 的对象是谁,就调用的谁。

线程

下列关于JAVA多线程的叙述正确的是()
正确答案: B C
A. 调用start()方法和run()都可以启动一个线程
B. CyclicBarrier和CountDownLatch都可以让一组线程等待其他线程
C. Callable类的call()方法可以返回值和抛出异常
D. 新建的线程调用start()方法就能立即进行运行状态

A. start()方法来启动线程,真正实现了多线程运行,调用了run()方法;run()方法当作普通方法的方式调用。
B. CyclicBarrier让一组线程等待其他线程;CountDownLatch让一组线程等待某个事件发生。
C. Callable能够抛出checked exception。
D. start()方法让thread进去可运行状态(runnable),等待获取CPU的使用权。

区分“终止” 和 “阻塞”:
终止:这个线程不会在进入“就绪态”,宣告死亡,即“死亡状态”。
阻塞:进入阻塞态的线程还可以再进入“就绪态”,等待下一次 CPU 时间。
然后是对线程的 5 个状态的理解:

  1. 新建,刚刚新建的线程,还未进入就绪队列
  2. 就绪,进入就绪队列的线程拥有了获得 CPU 时间的机会,但不是一定会马上执行,与线程调度有关。
  3. 运行,获得了 CPU 时间,正在被执行的线程。
  4. 阻塞,进入阻塞状态的线程只是暂时失去了 CPU 时间,该类线程没有结束,“阻塞态”的线程只能进入到“就绪态”。
  5. 死亡,死亡的线程即彻底结束了。

下面总结下使一个线程进入阻塞状态的方法:

  1. sleep() / suspend()
  2. 发生IO阻塞
  3. 等待同步锁
  4. 等待通知

解除一个线程的阻塞状态,使之进入就绪态的方法:

  1. sleep() 指定的睡眠时间到了
  2. IO阻塞解除
  3. 获得同步锁
  4. 收到通知
  5. 调用了 suspend() 的线程阻塞后,再调用 resume() 解除阻塞

集合框架

集合框架

杂项

Which statement declares a variable a which is suitable for referring to an array of 50 string objects?(Java)
正确答案: B C F
A. char a[][];
B.String a[];
C.String[] a;
D.Object a[50];
E.String a[50];
F.Object a[];

在java 中,声明一个数组时,不能直接限定数组长度,只有在创建实例化对象时,才能对给定数组长度.。
A:char[][] 定义了二位字符数组。在Java中,使用字符串对char数组赋值,必须使用toCharArray()方法进行转换。所以A错误。
B、C:在Java中定义String数组,有两种定义方式:String a[]和String[] a。所以B、C正确。
D、E:数组是一个引用类型变量 ,因此使用它定义一个变量时,仅仅定义了一个变量 ,这个引用变量还未指向任何有效的内存 ,因此定义数组不能指定数组的长度。所以D、E错误。
F:Object类是所有类的父类。子类其实是一种特殊的父类,因此子类对象可以直接赋值给父类引用变量,无须强制转换,这也被称为向上转型。这体现了多态的思想。所以F正确。

最后选B、C、F

面向对象程序设计语言的三个基本特征是封装、继承、多态
StringBuffer是线程安全,StringBuilder不是

接口和抽象

  1. 一个子类只能继承一个抽象类,但能实现多个接口
  2. 抽象类可以有构造方法,接口没有构造方法
  3. 抽象类可以有普通成员变量,接口没有普通成员变量
  4. 抽象类和接口都可有静态成员变量,抽象类中静态成员变量访问类型任意,接口只能public static final(默认)
  5. 抽象类可以没有抽象方法,抽象类可以有普通方法,接口中都是抽象方法
  6. 抽象类可以有静态方法,接口不能有静态方法
  7. 抽象类中的方法可以是public、protected;接口方法只有public

==和equal

==是比较两个内存地址是否相同,相同为true,不相同为false;
在字符串缓冲池中,若已有字符串,则直接赋值时不再新创建一个字符串,如果是new 关键字,则新创建一个不同的字符串。
String类的equals被重新复写,只要字符串相等,则返回true。

1、基本型和基本型封装型进行== 运算符的比较,基本型封装型将会自动拆箱变为基本型后再进行比较,因此Integer(0)会自动拆箱为int类型再进行比较,显然返回true
2、两个Integer类型进行“==”比较,如果其值在-128至127,那么返回true,否则返回false, 这跟Integer.valueOf()的缓冲对象有关,这里不进行赘述。
3、两个基本型的封装型进行equals()比较,首先equals()会比较类型,如果类型相同,则继续比较值,如果值也相同,返回true
4、基本型封装类型调用equals(),但是参数是基本类型,这时候,先会进行自动装箱,基本型转换为其封装类型,再进行3中的比较。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值