java并发编程
1.什么是并发编程?并发编程是指为了提高程序的执行速度,在宏观上使得多个任务(线程)同时执行。
2.并发编程中启动的线程越多并不是效果一定越好。
3.开发正确的高并发程序,需要注意的是:
1)程序的死锁:多个线程为了抢占某一资源造成的一种僵局状态
例:thread a lock1 -> get lock2
thread b lock2 -> get lock1
有两个资源lock1、 lock2,两个线程thread a、thread b ,当thread a获得lock1的时候,要再去执行的话就得去获得lock2,但是与此同时thread b也是获得了lock2
,它要去获得lock1,这种情况下thread a在等待获得lock2,thread b 在等待获得lock1,就形成了死锁问题。
2)上下文切换:线程执行是cpu通过给每一个线程去分配时间片,只有拿到时间片才可去执行;由于时间片很短,宏观上让人感觉线程是在并行操作。
3)线程不安全:多个线程访问同一资源,结果和预期一样就是线程安全,反之就是线程不安全;一个正确执行的并发程序,必须具备原子性、可见性、有序性。
4.在此我们就引出了并发编程的三大特性
1)原子性:原子性指在一次或者多次操作中,要么全部得到执行,要么所有操作都不执行。下面我们举几个简单的例子:
①int a = 10; ②a++;③ int b = a;④ a = a+1;
在这几个例子中①是符合原子性的,当赋的值变化时,也就意味着a的值也在随之变化,但是②③④都不符合原子性,像②的操作是先读取a,然后进行a+1,将结果赋值给a,很明显并不符合原子性的特征,③④同②。
2)可见性:可见性指当一个线程对共享变量进行了修改,那么另外的线程可以立即看到修改后的新值。下面我们也来看一个例子,直接上代码。
public class GY1 {
private static int initValue = 0;
private static final int MAX = 5;
public static void main(String[] args) {
new Thread("Reader"){
@Override
public void run() {
int localValue = initValue;
while(localValue > MAX){
if(initValue != localValue){
System.out.println("The initValue has been updated to "+initValue);
localValue = initValue;
}
}
}
}.start();
new Thread("Updater"){
@Override
public void run() {
int localValue = initValue;
while(localValue < MAX){
System.out.println("The initValue will be changed to "+(++localValue));
initValue = localValue;
try {
//短暂休眠,目的是为了使得Reader线程来得及输出变化内容
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
通过运行结果我们可以看到Updater线程对initValue的修改对Reader线程是不可见的,也就是另外的线程立即看到了修改后的新值,这体现了并发编程的原子性;
那么如果要可见的话我们进行了如下的修改,在堆初始的 initValue定义的时候前面添加了volatile关键字,我们继续看结果。
public class TestDemo {
private volatile static int initValue = 0;
private static final int MAX = 5;
public static void main(String[] args) {
new Thread("Reader"){
@Override
public void run() {
int localValue = initValue;
while(localValue < MAX){
if(initValue != localValue){
System.out.println("The initValue has been updated to "+initValue);
localValue = initValue;
}
}
}
}.start();
new Thread("Updater"){
@Override
public void run() {
int localValue = initValue;
while(localValue < MAX){
System.out.println("The initValue will be changed to "+(++localValue));
initValue = localValue;
try {
//短暂休眠,目的是为了使得Reader线程来得及输出变化内容
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
通过运行之后,发现修改后的结果Updater线程对initValue的修改对Reader线程成为了可见的。
3)有序性:所谓有序性是指程序代码在执行过程中过得先后顺序不会改变。由于Java在编译器以及运行期间的优化,导致了代码的执行顺序未必就是开发者编写代码的顺序。一般来说,处理器为了提高程序的运行效率,可能会对输入的代码指令做一定的优化,它不会百分之百的保证代码的执行顺序严格按照编写代码中的顺序来进行,但是它会保证程序的最终运算结果是编码时所期望的值。在单线程情况下,无论怎样的重排序最终都会保证程序的执行结果和代码顺序执行的结果是一致的,但是在多线程的情况下,如果有序性得不到保证,那么很有可能出现问题。