什么是线程同步?
流水线组装汽车,可以视作一个线程,只有装好了上一步的门,才可以装下一步的门窗玻璃。没有装门能装上玻璃吗?显然不行。所以线程同步也是的,要将线程中的请求完成以后才会进行下一个请求。
A线程要请求某个资源,但是此资源正在被B线程使用中,因为同步机制存在,A线程请求不到,怎么办,A线程只能等待下去。
如何同步呢?可以使用synchronized同步锁关键词
public synchronized void run(){
//方法1:方法体
}
public void run(){
synchronized(this){
//方法2:方法体
}
}
什么是线程异步?
异步就更简单了。 A线程要请求某个资源,但是此资源正在被B线程使用中,因为同步机制不存在(没有使用啥关键词),那不就很舒服?我A想用就用,无需等待!
显然,同步是安全的,最保险的。而异步不安全,容易导致死锁,这样一个线程死锁就会导致整个进程崩溃。但是没有同步机制存在,性能又会提升。
- 接下来是线程同异步的代码实例。
public class test3 implements Runnable {
int count = 1;
public static void main(String[] args) {
test3 t = new test3();
Thread th1 = new Thread(t);
th1.start();
Thread th2 = new Thread(t);
th2.start();
}
@Override
public void run() {
try {
System.out.println("你是第"+count+"个使用线程的");
count ++;
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
异步时输出:
你是第1个使用线程的
你是第1个使用线程的
同步时输出:
你是第1个使用线程的
你是第2个使用线程的
什么是线程安全?
Java的内存模型中有主内存和线程工作内存之分。所有的变量(线程共享的变量)存放在主内存上,线程的工作内存是线程的私有内存空间,存放的是线程私有的变量(方法参数、局部变量)。线程在工作时如果要操作主内存上的共享变量,为了更好地执行性能并不是直接去修改主内存,而是会在线程私有工作内存创建一份公有变量的拷贝(缓存),在工作内存对拷贝的变量进行修改以后再把修改的值返回到主内存公有变量中去。JVM一共提供了8种原子操作来完成这一过程:lock, unlock, read, load, use, assign, store, write
在一个进程中只有一个线程当然不会有什么问题。但是如果同时几个线程同时操作主内存中的公有变量,因为8种原子操作的非连续性和线程抢占CPU执行的机制就会带来冲突问题,这里就是线程的安全问题。
线程安全的定义就是:线程在执行的过程中,不会产生共享资源的冲突就是线程安全的。
如何做到线程安全?
Java中一般用以下几种机制保证线程的安全:
- synchronized关键词(悲观锁)
这个最常用也最简单,例子上面就有不过多说明。需要提到的是它通过互斥的方式保证同步,同时他也是阻塞式的。
- ReentrantLock锁(悲观锁)
这个方法和synchronized差不多。
区别是:
1.synchronized是隐式的,只要代码块执行完就会释放当前锁。后者是显式的,需要调用unlock()方法手动释放,经常搭配try/finally方法
2.后者可以选择等待中断。意思是我不想等你了,我要去做和别的小朋友玩了。在当前持有锁线程长期不释放锁的情况下,正在等待的线程可以放弃等待选择处理其他事情
3.后者为公平锁,即先来后到按顺序排队使用
- CAS(乐观锁)
基于冲突检测的并发策略,不需要将线程挂起,因此又被成为非阻塞同步
- 无同步方案
1.可重入代码(重入代码)
是一种无同步方案,在执行的任何时候去中断,转而执行其他的代码;在重新返回代码后,不会出现任何的错误。可重入性->线程安全,充分不必要条件。即可重入性的代码都是线程安全的,但反之不一定。简单判断原则:一个方法的返回结果是可预测的,只要输入了相同的数据,就都能返回相同的结果
2.程本地存储:即利用ThreadLocal类