java多线程——临界资源问题及解决方案

1.产生原因

​ 临界资源:被多个线程同时访问的资源
​ 如果有多个线程同时访问同一份资源,这个资源对应的值有可能会出现值不准确的情况。临界资源产生的原因:在多个线程访问同一份资源的时候,如果一个线程在取值的过程中,时间片又被其他线程抢走了,临界资源问题就产生了

此处给出一个卖票的例子:

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
package Thread;

public class SourceConfilct implements Runnable {
// 演示临界资源问题
// 某个景点有4个售票员在卖票

public static void main(String[] args) {
SourceConfilct s = new SourceConfilct();
Thread t1 = new Thread(s);
t1.setName("售票员1");
Thread t2 = new Thread(s);
t2.setName("售票员2");
Thread t3 = new Thread(s);
t3.setName("售票员3");
Thread t4 = new Thread(s);
t4.setName("售票员4");
t1.start();
t2.start();
t3.start();
t4.start();

}

public void run() {
// TODO Auto-generated method stub
while (TicketCenter.restCount > 0) {
System.out.println(Thread.currentThread().getName() + "卖出一张票剩余:"
+ --TicketCenter.restCount + "张");
}

}
}

class TicketCenter {
// 描述剩余票的数量
public static int restCount = 10;
}

​ 运行结果:image-20191218172108257

此时的结果明显不是我们想要的, 因此,需要采用同步机制解决这种问题。

2.解决方法

1.加上synchronized关键字

synchronized关键字主要有两种用法(synchronized 方法和synchronized块),此外该关键字还可以作用于静态方法、类或某个实例,但这都对程序的效率有很大的影响。

1) synchronized 方法。

在方法的声明前加人synchronized关键字,示例如下:

1
public synchronized void mutiThreadAccess( );

只要把多个线程对类需要被同步的资源的操作放到mutiThreadAccess( )方法中,就能保这个方法在同一时刻只能被一个线程访问, 从而保证了多线程访问的安全性。然而,当一个法的方法体规模非常大时,把该方法声明为synchronized会大大影响程序的执行效率。为了高程序的效率,Java 提供了synchronized块。

代码:

只修改实现Runnable接口的run方法

1
2
3
4
5
6
7
8
9
10
11
12
13
	public void run() {
// TODO Auto-generated method stub
while (TicketCenter.restCount > 0) {
// 对象锁
// 类锁
// 需要保证一点,多个对象看到的锁,需要是同一把锁
synchronized(""){
System.out.println(Thread.currentThread().getName() + "卖出一张票剩余:"
+ --TicketCenter.restCount + "张");
}
}

}

运行结果:

image-20191218173324370

可以看到输出结果大致和我们所需相同但是多出了-1,-2,-3,这个是不满足我们的要求的

因为当你加了synchronized(“ “)后,线程看到锁后需要等待,所以当满足了TicketCenter.restCount > 0,还会有三个线程正在等待执行。只需再加一个判断条件即可。

全部代码:

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
package Thread;

public class SourceConfilct implements Runnable {
// 演示临界资源问题
// 某个景点有4个售票员在卖票

public static void main(String[] args) {
SourceConfilct s = new SourceConfilct();
Thread t1 = new Thread(s);
t1.setName("售票员1");
Thread t2 = new Thread(s);
t2.setName("售票员2");
Thread t3 = new Thread(s);
t3.setName("售票员3");
Thread t4 = new Thread(s);
t4.setName("售票员4");
t1.start();
t2.start();
t3.start();
t4.start();

}

public void run() {
// TODO Auto-generated method stub
while (TicketCenter.restCount > 0) {
// 对象锁
// 类锁
// 需要保证一点,多个对象看到的锁,需要是同一把锁
synchronized(""){
if(TicketCenter.restCount<=0)
return;
System.out.println(Thread.currentThread().getName() + "卖出一张票剩余:"
+ --TicketCenter.restCount + "张");
}
}

}
}

class TicketCenter {
// 描述剩余票的数量
public static int restCount = 10;
}
2) synchronized 块。

synchronized 块既可以把任意的代码段声明为synchronized, 也可以定上锁的对象,有非常高的灵活性。其用法如下:

1
2
3
4
5
synchronized( syncObject) {

//访问syncObjet的代码

}
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 Thread;

public class SourseConflict implements Runnable {
// 演示临界资源问题
// 某个景点有4个售票员在卖票

public static void main(String[] args) {
SourceConfilct s = new SourceConfilct();
Thread t1 = new Thread(s);
t1.setName("售票员1");
Thread t2 = new Thread(s);
t2.setName("售票员2");
Thread t3 = new Thread(s);
t3.setName("售票员3");
Thread t4 = new Thread(s);
t4.setName("售票员4");
t1.start();
t2.start();
t3.start();
t4.start();

}

public void run() {
// TODO Auto-generated method stub
while (TicketCenter.restCount > 0) {

sellticket();
}

}

public static synchronized void sellticket() {
if (TicketCenter.restCount <= 0)
return;
System.out.println(Thread.currentThread().getName() + "卖出一张票剩余:"
+ --TicketCenter.restCount + "张");

}
}

class TicketCenter {
// 描述剩余票的数量
public static int restCount = 10;
}
2. Lock

JDK5里新增一个Lock接口以及它的一个实现类ReentrantLock(重入锁),Lock也可以用来实现多线程的同步。

直接上代码:

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 Thread;

import java.util.concurrent.locks.ReentrantLock;

public class SourceConlictResolve implements Runnable {
// 演示临界资源问题
// 某个景点有4个售票员在卖票

public static void main(String[] args) {
SourceConlictResolve s = new SourceConlictResolve();
Thread t1 = new Thread(s);
t1.setName("售票员1");
Thread t2 = new Thread(s);
t2.setName("售票员2");
Thread t3 = new Thread(s);
t3.setName("售票员3");
Thread t4 = new Thread(s);
t4.setName("售票员4");
t1.start();
t2.start();
t3.start();
t4.start();

}

public void run() {
// TODO Auto-generated method stub
ReentrantLock lock=new ReentrantLock();
while (ticketsold.restCount > 0) {
lock.lock();
if(ticketsold.restCount <=0)
return ;

System.out.println(Thread.currentThread().getName() + "卖出一张票剩余:"
+ --ticketsold.restCount + "张");
lock.unlock();
}

}
}

class ticketsold {
// 描述剩余票的数量
public static int restCount = 10;
}
-------------本文结束感谢您的阅读-------------
0%