关于多线程与并发,多线程是指系统在轮流执行不同的方法,由于CPU速度太快,所以我们看起来是同步执行的,这就是所谓的线程并发;
线程并发时访问共享变量会引起数据不一致的问题,解决数据的不一致问题就需要用到线程同步。
此篇讲线程同步技术。
同步技术引出:
先来个程序测试不同步的情况:
class MyRunable implements Runnable{
private int ticket = 10;
@Override
public void run() {
while (true){
if (ticket > 0 ) {
System.out.println(Thread.currentThread().getName()+":ticket="+ticket--);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
System.out.println("卖光了");
break;
}
}
}
}
public class A {
public static void main(String[] args) throws InterruptedException {
MyRunable r = new MyRunable();
Thread t1 = new Thread(r,"线程1");
Thread t2 = new Thread(r,"线程2");
Thread t3 = new Thread(r,"线程3");
Thread t4 = new Thread(r,"线程4");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
结果:可以看出卖票没有顺序的,而且把一张票卖给多个人,这肯定是不符合要求的。
线程2:ticket=10
线程3:ticket=9
线程1:ticket=8
线程4:ticket=7
线程2:ticket=6
线程4:ticket=6
线程3:ticket=4
线程1:ticket=5
线程1:ticket=3
线程2:ticket=2
线程3:ticket=3
线程4:ticket=1
卖光了
卖光了
卖光了
卖光了
为什么呢,1、run方法内的代码并不是同步执行完毕的,而是分时间段来执行的;
判断票数是否大于0,如果线程a判断大于0, 现在a挂起了,它之后会继续在其他时间片执行,现在线程b将票已经卖光了(ticket = 0),但是线程a获取了时间片,他会继续将0减1,结果可能就会出现-1的结果;
2、还有run方法中这句
System.out.println(Thread.currentThread().getName()+":ticket="+ticket--);
println是线程安全的,但是传入的ticket变量是在进行自减,自减其实是分为三步来完成的:
1.ticket = this.ticket | 先取原值 |
2.ticket+1 | 再加法运算 |
3.ticket = ticket | 再赋值 |
所以,以上程序在非并发情况下,可能产生一张票卖多个人,卖空票,这就不是我们需要的结果,因为需要线程的同步来实现。
修改run方法
public void run() {
while (true) {
//同步代码块
synchronized (this) {
if (ticket > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":ticket=" + ticket--);
} else {
System.out.println("卖光了");
break;
}
}
}
}