线程安全如何实现
线程安全问题的产生本质上是因为多个线程在并发条件下对同一个共享资源的争抢,因此有两种方向来保证线程安全:
- 限制线程对资源的并发访问:这个方向的主要方式是加锁(阻塞),volatile(非阻塞)。
- 将该资源设置为线程独占的:这个方向主要实现方式是TreadLocal。
线程安全问题的产生本质上是因为多个线程在并发条件下对同一个共享资源的争抢,因此有两种方向来保证线程安全:
主要方向有两种:一是自旋,二是阻塞(加锁或信号量)
三个方法各自死循环,直到计数器对 3 取余是相应的数字。为了避免浪费资源,在自旋失败时让出 CPU 时间
public class PrintABC {
private volatile int counter = 0;
public void printA() {
while (true) {
while (counter % 3 != 0) {
Thread.yield();
}
System.out.print("A");
counter++;
}
}
public void printB() {
while (true) {
while (counter % 3 != 1) {
Thread.yield();
}
System.out.print("B");
counter++;
}
}
public void printC() {
while (true) {
while (counter % 3 != 2) {
Thread.yield();
}
System.out.print("C");
counter++;
}
}
public static void main(String[] args) {
PrintABC printABC = new PrintABC();
new Thread(()-> printABC.printA()).start();
new Thread(()-> printABC.printB()).start();
new Thread(()-> printABC.printC()).start();
}
}
PriorityQueue 是 Java 中的一个基于优先级堆的优先队列实现,它能够在 O(log n) 的时间复杂度内实现元素的插入和删除操作,并且能够自动维护队列中元素的优先级顺序。
通俗来说,PriorityQueue 就是一个队列,但是它不是先进先出的,而是按照元素优先级进行排序的。当你往 PriorityQueue 中插入一个元素时,它会自动根据元素的优先级将其插入到合适的位置。当你从 PriorityQueue 中删除一个元素时,它会自动将优先级最高的元素出队。
常用方法
不可变性
final修饰,每次修改都创建新字符串到常量池,相当于值引用
StringBuilder与StringBuffer
Buffer加了synchronized锁,因此常用Builder,并在并发下配合ThreadLocal
字符串常量池
String s = new String("ABC");
: 先在字符串常量池中创建对象“ABC”,然后再在堆上创建对象“ABC”
Java 中的 Finally 关键一般与try一起使用,在程序进入try块之后,无论程序是因为异常而中止或其它方式返回终止的,finally块的内容一定会被执行 。因此我们可以在 finally 代码块中执行关闭连接、关闭文件和释放线程的的操作。
public class Solution {
public int testFinally() {
int i=0;
try {
return i++;//先返回再加,因此返回0
} finally {
++i;//try中已经加了,这里再加一次
System.out.println("Finally: " + i);
}
}
public static void main(String[] args) {
Solution solution = new Solution();
System.out.println("Main: "+solution.testFinally());
}
}
//接口默认都是abstract的方法,因此也是默认abstract的,隐藏不写
public abstract interface TestInterface {
//变量默认都是public static final的,隐藏不写
public static final int a = 17;
}
抽象类: abstract修饰的类为抽象类,抽象类不能创建实例
抽象成员函数: abstract修饰的类为抽象成员函数,该成员函数为抽象成员函数,没有函数体。
抽象类与抽象成员函数有何联系: 有抽象函数的类必须被生命为抽象类,但是抽象函数中也可以有非抽象函数、也可以有构造函数。
抽象类可以有构造函数,可以通过构造函数初始化抽象类里的变量,主要是给子类通过 super()
调用。