今天和大家一起学习下并发编程,先举一个简单的生活例子,我们去医院或者银行排队叫号,那每个工作人员之间如何保证不会叫重号呢?
public class TicketDemo extends Thread {
private int index = 1;
private static final int MAX = 10;
@Override
public void run() {
while (index <= MAX) {
System.out.println(Thread.currentThread().getName() + "呼叫的号码是:" + (index++));
}
}
public static void main(String[] args) {
TicketDemo t1 = new TicketDemo();
TicketDemo t2 = new TicketDemo();
TicketDemo t3 = new TicketDemo();
TicketDemo t4 = new TicketDemo();
t1.start();
t2.start();
t3.start();
t4.start();
}
}
运行结果
我们发现,每一个线程都拥有自己的index变量,我们需要将index改成共享变量,有static
public class TicketDemo extends Thread {
private static int index = 1;
private static final int MAX = 10;
@Override
public void run() {
while (index <= MAX) {
System.out.println(Thread.currentThread().getName() + "呼叫的号码是:" + (index++));
}
}
public static void main(String[] args) {
TicketDemo t1 = new TicketDemo();
TicketDemo t2 = new TicketDemo();
TicketDemo t3 = new TicketDemo();
TicketDemo t4 = new TicketDemo();
t1.start();
t2.start();
t3.start();
t4.start();
}
}
运行结果
但是我们如果将号码增到5000,50000万时,会出现跳号、重号、超过最大值的情况
我们可以通过synchronized将并发的代码加锁
public class TicketDemo extends Thread {
private static int index = 1;
private static final int MAX = 10;
@Override
public void run() {
while (index <= MAX) {
synchronized (this) {
System.out.println(Thread.currentThread().getName() + "呼叫的号码是:" + (index++));
}
}
}
public static void main(String[] args) {
TicketDemo t1 = new TicketDemo();
TicketDemo t2 = new TicketDemo();
TicketDemo t3 = new TicketDemo();
TicketDemo t4 = new TicketDemo();
t1.start();
t2.start();
t3.start();
t4.start();
}
}
结果输出
在Java中,每个对象都会有一个monitor对象,监视器。
-
某一线程占有这个对象的时候,先monitor的计数器是不是0,如果是0说明还没有线程占有,这个时候线程占有这个对象,并且这个对象的monitor+1,;如果不为0,表示这个线程已经被其他线程占有,这个线程等待。当线程释放占有权的时候,monitor-1
-
使用synchronized对代码块进行加锁,是使用monitorenter和monitorexit配合使用
-
使用synchronized对方法进行加锁,使用ACC_SYNCHRONIZED
锁:
jdk1.6以前,都是重量锁
Java虚拟机对synchronized的优化:
- 偏向锁:在对象第一次被某一线程占有的时候,是否偏向锁职位1,锁表01,写入线程号;当其他的线程访问的时候,竞争;竞争的结果有两种,成功或失败。很多次被第一次占有它的线程获取次数多
CAS算法:compare and set
竞争不激烈的时候适用 - 轻量级锁:线程有交替使用,互斥性不是很强,CAS失败,置为00
- 重量级锁(等待时间长):强互斥,置为10
- 自旋锁:竞争失败的时候,不是马上转化级别,而是执行几次空循环
- 锁消除:JIT进行编译的时候,把不必要的锁去掉