多线程售票问题以及线程同步机制

售票问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package windowSale;

/**
* @author zss
*/
public class WindowSale1 {
public static int countTicket=10;

public static void main(String[] args) {
Window1 window1 = new Window1();
Window1 window2 = new Window1();
Window1 window3 = new Window1();
window1.start();
window2.start();
window3.start();
}
}
class Window1 extends Thread{
boolean judge=true;
@Override
public void run(){
while (judge){
sell();
}
}
public void sell(){
if (WindowSale1.countTicket<=0){
judge=false;
System.out.println("售票结束");

}
else {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
WindowSale1.countTicket = WindowSale1.countTicket - 1;
System.out.println(Thread.currentThread().getName() + "售卖一张票" + "还剩" + WindowSale1.countTicket);
}

// System.out.println();

}
}


三个窗口售卖,会发现我们的票数剩余0张还可以继续售卖。这是因为在我们的票数还有两张时,我们的1号窗口进来还没有进行减去操作,2号与3号都进来,导致2张票被买了三次。

线程同步机制

在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任意同一时刻,最多由一个线程访问,保证数据的完整性。

同步的具体方法

  • synchronized(对象){//得到对象的锁,才能操作同步带代码

​ //需要被同步的代码},操作的必须是同一个对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package windowSale;

/**
* @author zss
*/
public class WindowSale1 {
public static void main(String[] args) {
SellTicket sellTicket=new SellTicket();
Thread thread1=new Thread(sellTicket);
Thread thread2=new Thread(sellTicket);
Thread thread3=new Thread(sellTicket);
thread1.start();
thread2.start();
thread3.start();
}
}
class SellTicket extends Thread{
int ticketNum=100;
boolean judge=true;
Object obj=new Object();
@Override
public void run() {
while (judge){
sell();
}
}
public void sell(){

synchronized (this.obj){
if (ticketNum<=0){
System.out.println("售票结束");
judge=false;
}
else {
ticketNum--;
System.out.println(Thread.currentThread().getName()+"售卖一张票,号数为"+(ticketNum+1)+"还剩"+ticketNum);
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
  • synchronized还可以放在方法声明中,表示整个方法为同步方法

public synchronized void m(String name){

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package windowSale;

/**
* @author zss
*/
public class WindowSale1 {
public static void main(String[] args) {
SellTicket sellTicket=new SellTicket();
Thread thread1=new Thread(sellTicket);
Thread thread2=new Thread(sellTicket);
Thread thread3=new Thread(sellTicket);
thread1.start();
thread2.start();
thread3.start();
}
}
class SellTicket extends Thread{
int ticketNum=100;
boolean judge=true;
@Override
public void run() {
while (judge){
sell();
}
}
public void sell(){
if (ticketNum<=0){
System.out.println("售票结束");
judge=false;
}
else {
ticketNum--;
System.out.println(Thread.currentThread().getName()+"售卖一张票,号数为"+(ticketNum+1)+"还剩"+ticketNum);
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

image-20220318091456954

此时发现没有超票售出,但是售票的顺序却不正确,而且其实内部已经发生了超卖现象,但是我们只不过用代码块掩盖 了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package windowSale;

/**
* @author zss
*/
public class WindowSale1 {
public static void main(String[] args) {
SellTicket sellTicket=new SellTicket();
Thread thread1=new Thread(sellTicket);
Thread thread2=new Thread(sellTicket);
Thread thread3=new Thread(sellTicket);
thread1.start();
thread2.start();
thread3.start();
}
}
class SellTicket extends Thread{
int ticketNum=100;
boolean judge=true;
@Override
public void run() {
while (judge){
sell();
}
}
public synchronized void sell(){
if (ticketNum<=0){
System.out.println("售票结束");
judge=false;
}
else {
ticketNum--;
System.out.println(Thread.currentThread().getName()+"售卖一张票,号数为"+(ticketNum+1)+"还剩"+ticketNum);
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

image-20220318092230291