单核处理器支持多线程:CPU通过给线程分配时间片切换执行,因为时间片比较短,所以看起来像是线程在同时执行。
上下文切换:记住上个线程任务执行状态的同时切换到另一个线程执行,可以执行另一个线程任务后回到当前线程的执行状态继续执行。
死锁:多个任务执行在互相等待对方作占有的资源,陷入一种互相循环等待的死循环。
防止死锁:尽量保证一个线程一个锁一个资源。给锁加上时间限制,超时即采取强制释放资源的方式(定时锁),匹配锁需要的充足的资源。
原子操作:在多线程和操作系统中,原子操作较为常见,原子操作定义不可分割,只有开始和结束,无法改变中间过程的操作。在操作系统中可以使用总线锁和缓存锁,目的就是保证变量的一致性。
CAS:
(赶紧把小本本从包里掏出来记下来)
volatile关键字是Java中用来在多处理器或者多线程环境中保证共享变量的一致性和可见性。Java中允许线程访问共享变量,为了保证共享变量可见并且一致地被修改,Java推出volatile关键字。也就是说volatile修饰的变量对于所有能共享该变量的值是一致的。
volatile在多处理器下的一致性实现:在多处理器环境下,多线程的执行会导致每个处理器共享值不同时更新,JVM是跨平台虚拟机,在volatile关键词修饰的变量JVM会单独通知处理器在使用该变量的时候去检查该变量的时效性,更新到当前主存中的值,丢弃原来的各自的处理器中的无效值。所以JVM在其中起到了信号传递的作用。
测试代码如下:
public class JavaThread {
public static volatile boolean flag = true;
public static void main(String[] args) throws InterruptedException {
for (int i=0; i<=10; i++){
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("我在这里开始");
int i=0;
while (flag){
i++;
}
System.out.println("我在这里结束");
}
}
).start();
}
Thread.sleep(10);
flag = false;
}
}
不加volatile关键字,只有开始,没有结束,一旦陷入i++的深渊,便无法自拔,欲罢不能,控制不住(这是会看到左边那个红色小方块和听到电脑风扇的呜呜声):
加了volatile关键字,从哪里开始,到哪里结束,只要线程通知结束(即改变flag),所有的处理器和线程都会立刻反应并作出相应的操作:
synchronized关键字是用于对象,class和代码块的最常用的同步锁,通常需要设定一个信号机制,实际上没有信号机制synchronized同样起作用。synchronized关键字通常可以给类,对象和代码快加锁,其中,类锁和对象锁的区别是类锁加了static关键字来实现类的加锁,也就是实现了类的访问加锁。
代码测试如下:
import java.util.Arrays;
public class DeadLockDemo {
public static String [] arr = {"1","2","3","4","5","6","7","8","9","10"};
public static synchronized void changeList(String ii,String flag){
for (int i=0; i<arr.length; i++) {
arr[i] = ""+ii;
}
}
public static void main(String[] args) throws InterruptedException {
for (int i=0; i<=100;i++){
new Thread(new Runnable() {
@Override
public void run() {
String flag = "1";
changeList(Thread.currentThread().getName(),flag);
System.out.println(Arrays.toString(arr));
}
}).start();
}
Thread.sleep(1000);
}
}
这里是利用多个线程来同时修改数组里面的值,不加锁则会被乱改,谁碰着谁又改一下。加了synchronized关键字就会发现每一次输出的数组里的值都是统一的。效果如下:
是不是不一致哈。