一、synchronized简单介绍
互斥同步是一种最常见也是最主要的并发正确性保障手段。同步是指在多个线程并发访问共享数据时,保证共享数据在同一个时刻只被一条(或者是一些, 当使用信号量的时候)线程使用。而互斥是实现同步的一种手段,临界区(Critical Section)、互斥量 (Mutex)和信号量(Semaphore)都是常见的互斥实现方式。因此在“互斥同步”这四个字里面,互斥是因,同步是果;互斥是方法,同步是目的。
在Java里面,最基本的互斥同步手段就是synchronized关键字 。
synchronized的包含三个特性:
原子性:使用synchronized实现了同步,同步实现了原子性,保证了被同步的代码块在同一时间只有一个线程在执行。
可见性:当一个线程修改了共享变量的值时,其他线程能够立即得知这个修改。
有序性:禁止代码重排序
二、synchronized使用
例如,我们在超市售货系统中,代码会先判断某种商品库存数量是否大于0,如果大0就出售。但当两个线程同时访问这段代码时,如果某种商品只有1件库存了,则第一个程将该商品卖出,第二个线程也已经执行完判断是否有库存的操作,于是也出售,这样商就重复出售了,这就是丢失修改的情况。为了避免这种情况发生,在编写多线程程序时,该使用同步机制。
public class Sy implements Runnable {
private int goodsCount = 100;
public void sellGoods(){
if(goodsCount>0){
System.out.println(Thread.currentThread().getName()+"正在出售第"+(5-goodsCount+1)+"件库存商品,"+"还剩"+(--goodsCount)+"库存");
}else {
System.out.println("该商品已经卖完!");
}
}
public void run(){
while(goodsCount>0){
sellGoods();
try{
Thread.sleep(100);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
public class test {
public static void main(String[] args){
Sy runGoods = new Sy();
Thread th1 = new Thread(runGoods,"窗口1");
Thread th2 = new Thread(runGoods,"窗口2");
th1.start();
th2.start();
}
}
而运行时窗口1和窗口2有时会同时出售同一件商品,这是因为这些线程同时访问共享数据,没有进行同步控制,看可以进行对方法同步或对代码块同步进行改进。
1.对方法进行同步,在定义方法时加上关键字synchronized
public synchronized void sellGoods(){
if(goodsCount>0){
System.out.println(Thread.currentThread().getName()+"正在出售第"+(5-goodsCount+1)+"件库存商品,"+"还剩"+(--goodsCount)+"库存");
}else {
System.out.println("该商品已经卖完!");
}
}
2.对代码块进行同步,在需要同步的代码块前加上关键字synchronized
public void sellGoods(){
synchronized (this) {
if (goodsCount > 0) {
System.out.println(Thread.currentThread().getName() + "正在出售第" + (5 - goodsCount + 1) + "件库存商品," + "还剩" + (--goodsCount) + "库存");
} else {
System.out.println("该商品已经卖完!");
}
}
}
这样商品就不会被同时出售