ARTS 即 Algorithm,Review,Technology,Share。
本篇是本系列的第一篇,作者现在正处于大学阶段,所以很多部分还是会很弱的,希望各位谅解。这里只是想养成一个习惯
- 本次Algorithm是 Leetcode12 # 整数转罗马数字 ,难度为中等
- Review 暂时为空
- Technology 为 Volatile关键字实现原理
- Share 为 Markdown的一些基本用法
Algorithm
Leetcode12 c
罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。
字符 数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。
通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:
I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。
给定一个整数,将其转为罗马数字。输入确保在 1 到 3999 的范围内。
示例 1:
输入: 3
输出: "III"
if(num<1 || num >3999) return null;
int[] values = { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5,
4, 1 };
String[] strs = { "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X",
"IX", "V", "IV", "I" };
StringBuilder sb = new StringBuilder();
for (int i = 0; i < values.length; i++) {
while (num >= values[i]) {
num -= values[i];
sb.append(strs[i]);
}
}
return sb.toString();`
最暴力的解法,把5~1000的都定义好,然后直接通过转换即可,这里就不用多说咯
Review
这里暂时为空,
Technology
1.JAVA并发编程中的三个概念
- 1.原子性
即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。
比如:从账户A向账户B转1000元,那么必然包括2个操作:从账户A减去1000元,往账户B加上1000元。
所以这2个操作必须要具备原子性才能保证不出现一些意外的问题。
同样地反映到并发编程中会出现什么结果呢?
假如为一个32位的变量赋值过程不具备原子性的话,会发生什么后果?
i = 5;
一个线程执行到这个语句时,我暂且假设为一个32位的变量赋值包括两个过程:为低16位赋值,为高16位赋值。
那么就可能发生一种情况:当将低16位数值写入之后,突然被中断,而此时又有一个线程去读取i的值,那么读取到的就是错误的数据。
- 2.可见性
可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。 - 3.有序性
有序性:即程序执行的顺序按照代码的先后顺序执行。
Java 底层会涉及重排序
Volatile关键字Java并发编程里面常见的,volatile关键智能保证可见性和有序性,并不能保证原子性
public class VolatileTest {
public static volatile int race = 0;
public static void increase() {
race++;
}
private static final int THREADS_COUNT = 20;
public static void main(String[] args) {
Thread[] threads = new Thread[THREADS_COUNT];
for (int i = 0; i < THREADS_COUNT; i++) {
threads[i] = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
increase();
}
}
});
threads[i].start();
}
// 等待所有累加线程都结束
while (Thread.activeCount() > 1)
Thread.yield();
System.out.println(race);
}
}
这段代码发起了20个线程,每个线程对race变量进行1000次自增操作,如果这段代码能够正确并发的话,最后输出的结果应该是20000。读者运行完这段代码之后,并不会获得期望的结果,而且会发现每次运行程序,输出的结果都不一样,都是一个小于20000的数字。显然Volatile关键不保证原子性
解决这个问题,方法同步块包裹起来当然是一个办法,但是使用Atomic的原子自增运算,那效率将会提高许多
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicTest{
public static AtomicInteger race=new AtomicInteger(0);
public static void increase(){
race.incrementAndGet();
}
private static final int THREADS_COUNT=20;
public static void main(String[]args)throws Exception{
Thread[]threads=new Thread[THREADS_COUNT];
for(int i=0;i1)
Thread.yield();
System.out.println(race);
}
}
运行结果如下:
200000
使用AtomicInteger代替int后,程序输出了正确的结果,这一切都是要归功于 incrementAndGet()方法的原子性。它的实现其实非常简单
/**
*Atomically increment by one the current value.
*@return the updated value
*/
public final int incrementAndGet(){
for(;){
int current=get();
int next=current+1;
if(compareAndSet(current,next))
return next;
}
}
incrementAndGet()方法在一个无限循环中,不断尝试将比当前值大1的新值赋给自己。如果失败了,那就说明在执行“获取,设置”操作的时候只已经有了修改,于是再次进行下一次操作,知道成功为止。
尽管CAS(Compare And Swap)看起来很美,但显然这种操作无法涵盖互斥同步的所有使用场景,并且CAS从语义上来说并不是完美的,存在一个逻辑上的漏洞:如果一个变量V初次读取到的时候是A值,并且准备赋值的时候检查它仍然是A值,那么我们就说他的值没有被其他值改过了吗?如果这段期间它的值曾被改为B,后来又被改回了A,那CAS操作就会误认为它从来没有被改过,这个操作被称为ABA问题
Share
Markdown的一些基本用法
(#)标题对应级别
(#)一级标题
(##)二级标题
(*) 无序列表
- 列表1
- 列表2
> 引用
如果你需要引用一小段别处的句子,那么就>要用引用的格式。
引用
插入连接 baidu
[baidu](www.baidu.com
粗体与斜体
用两个 * 包含一段文本就是粗体的语法,用一个 * 包含一段文本就是斜体的语法。
表格
表格是我觉得 Markdown 比较累人的地方,例子如下:
| Tables | Are | Cool |
| ------------- |:-------------:| -----:|
| col 3 is | right-aligned | $1600 |
| col 2 is | centered | $12 |
| zebra stripes | are neat | $1 |
分割线
用 ***
代码块
用6个`包括起来
i = 1+1;