写一个关于卖票的方法 - - 多线程操纵数据
public class TestSync {
public static void main(String[] args) throws InterruptedException {
MaiPiao piao = new MaiPiao();
Thread [] threads = new Thread[10];//10个窗口
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(new Window(piao));
}//对线程进行循环 , 把买的票传给他
for (int i = 0; i < threads.length; i++) {
threads[i].start();
}//让线程去启动
Thread.sleep(5000); //让主线程休5s 让那10个线程跑完
piao.tikectCheck(); //检查一下还有没有票
}
}
//窗口负责卖票
class Window implements Runnable{
//我持有一个票
private MaiPiao piao;
public Window(MaiPiao piao) {
this.piao = piao;
}
@Override
public void run() {
for (int i = 2; i < 100; i++) {
piao.sold();
}
}
}
class MaiPiao{
private int tikect=1000;
public void sold(){
tikect--;
}
public void tikectCheck(){
System.out.println(tikect);
}
}
预期结果是20 , ,但是有些运行结果并不是20,这是一个不安全的线程
如果在 public void sold(){ }中加一个synchronized ,private int tikect=1000;的数值变大一点, 输出结果还是20
synchronize是一个重量级锁
先来看下利用synchronized实现同步的基础:Java中的每一个对象都可以作为锁。具体表现 为以下3种形式:
①对于普通同步方法,锁是当前实例对象。
②对于静态同步方法,锁是当前类的Class对象。
③对于同步方法块,锁是Synchonized括号里配置的对象。
②的使用
改成静态的,相互之间是共享的 ;;; 输出结果是20
10个线程每个都持有一个票这个对象,对象只供自己的锁使用,又回到了开始的状态
③ 的使用
一、对象头
synchronized用的锁是存在Java对象头里的。如果对象是数组类型,则虚拟机用3个字宽 (Word)存储对象头,如果对象是非数组类型,则用2字宽存储对象头。在32位虚拟机中,1字宽 等于4字节,即32bit,在64位虚拟机中,1字宽就是8位.64bit
二、锁的升级和对比
竞争: 十个锁抢一个线程就是竞争
锁只能升级不能降级
1.偏向锁
当一个线程访问同步代码块并且获取锁时,会在对象头和栈帧中的锁记录里存储锁偏向的id
在对象上刻上自己的id,下次在访问的时候直接去比较就行了
等到竞争才会释放锁的机制
2.轻量级锁
CAS操作,比较并交换,比较成功交换,不成功不交换,…
轻量级解锁时,会使用原子的CAS操作将Displaced Mark Word替换回到对象头,如果成 功,则表示没有竞争发生。如果失败,表示当前锁存在竞争,锁就会膨胀成重量级锁。图2-2是 两个线程同时争夺锁,导致锁膨胀的流程图。
虽然在竞争,由于持有的时间不是很长,线程2有所谓的耐心,在耐心耗完之前获得了锁,便不会膨胀成轻量级锁