多线程的同步操作与死锁问题

程序利用线程可以进行更为高效的程序处理,若果在没有多线程的程序中,一个程序在处理某些资源时会有主方法(主线程),但这样处理速度一点会比较慢。如果采用多线程处理机制,利用主线程创建出许多子线程一起进行资源操作,那么执行效率一定比只使用主线程高。

虽然用多线程效率要比单线程高,但若果多线程同时操作一个资源就一定会出现一些问题,如资源操作的完整性问题。

同步与异步

同步和异步通常用来形容一次方法调用。

同步方法调用一旦开始,调用者必须等到方法调用返回后,才能继续后续的行为。
异步方法调用更像一个消息传递,一旦开始,方法调用就会立即返回,调用者就可以继续后续的操作。而,异步方法通常会在另外一个线程中,“真实”地执行着。整个过程,不会阻碍调用者的工作。

mark

如果正在写的数据以后可能被另一个线程读到或正在读的数据已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取。当程序在对象上调用了一个很耗时的方法而不希望让程序等待方法返回时,就该采用异步机制。

同步问题示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static class MyThread implements Runnable{
private int ticket = 5;
@Override
public void run() {
for(int i=0; i<10; i++) {
if(this.ticket>0) {
try {
Thread.sleep(100);
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"买票,tickets="+this.ticket--);
}
}
}
}
public static void main(String[] args) {
MyThread mt = new MyThread();
new Thread(mt, "张三").start();
new Thread(mt, "李四").start();
new Thread(mt, "王五").start();
new Thread(mt, "赵六").start();
}

可能的执行结果:

1
2
3
4
5
6
7
8
李四买票,tickets=5
张三买票,tickets=3
王五买票,tickets=4
赵六买票,tickets=2
张三买票,tickets=1
赵六买票,tickets=-2 // 不同步导致的错误结果
王五买票,tickets=-1 // 不同步导致的错误结果
李四买票,tickets=0

本程序模拟了一个卖票的过程,四个线程对象共同完成买票任务,增加判断条件if(this.ticket>0)是为了确保每次卖票都有剩余票,但最终结果却发现判断条件的作用并不明显。
分析卖票的操作步骤,有如下两步:
(1)判断票数是否大于0
(2)若大于0,则卖一张票出去
但示例中在第一步和第二步之间加入了延时操作,那么一个线程就可能在还没有对票数进行减操作之前,其他线程就已经将票数减少了,这样就会出现票数为负的情况。

mark

同步操作示例

Java里要实现同步操作可以使用synchronized关键字,有以下两种使用方法:
同步代码块:使用synchronized包装代码块,但要指定同步对象,一般为this
同步方法:使用synchronized定义的方法

markmark

同步代码块

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
public static class MyThread implements Runnable{
private int ticket = 40;
@Override
public void run() {
for(int i=0; i<10; i++) {
if(this.ticket>0) {
try {
Thread.sleep(100);
}catch(InterruptedException e) {
e.printStackTrace();
}
synchronized (this) { // 同步代码块
System.out.println(Thread.currentThread().getName()+"买票,tickets="+this.ticket--);
}
}
}
}
}
public static void main(String[] args) throws Exception{
MyThread mt = new MyThread();
new Thread(mt, "张三").start();
new Thread(mt, "李四").start();
new Thread(mt, "王五").start();
new Thread(mt, "赵六").start();
}

同步方法

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
public static class MyThread implements Runnable{
private int ticket = 40;
@Override
public void run() {
for(int i=0; i<10; i++) {
try {
Thread.sleep(100);
}catch(InterruptedException e) {
e.printStackTrace();
}
this.sale();
}
}
public synchronized void sale() { // 同步方法
if(this.ticket>0) {
System.out.println(Thread.currentThread().getName()+"买票,tickets="+this.ticket--);
}
}
}
public static void main(String[] args) throws Exception{
MyThread mt = new MyThread();
new Thread(mt, "张三").start();
new Thread(mt, "李四").start();
new Thread(mt, "王五").start();
new Thread(mt, "赵六").start();
}

死锁

同步就是指一个线程要等另一个线程执行完毕才能继续执行,的一种操作形式,虽然在一个操作中使用同步操作可以保证共享操作的正确性,但是过多的同步也会产生问题。
例如:张三想要李四的画,李四想要张三的书,那么张三对李四说:“把画给我,我就把书给你”,李四也说:“把书给我,我就把画给你”。此时,张三在等着李四的答复,而李四也在等着张三的答复,这样下去最终结果就是张三得不到李四的画,李四得不到张三的书,这实际上就是死锁的概念。

死锁示例

注意:以下代码没有任何意义,仅用来展示死锁问题

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
public static class A{
public synchronized void say(B b) {
System.out.println("A说:先给我本,就给你笔,否则不给");
b.get();
}
public synchronized void get() {
System.out.println("A说:得到了笔,付出了本,bye");
}
}
public static class B{
public synchronized void say(A a) {
System.out.println("B说:先给我笔,就给你本,否则不给");
a.get();
}
public synchronized void get() {
System.out.println("B说:得到了本,付出了笔,bye");
}
}
public static class Test implements Runnable{
private static A a = new A();
private static B b = new B();
public Test() {
new Thread(this).start();
b.say(a);
}
@Override
public void run() {
a.say(b);
}
}
public static void main(String[] args) throws Exception {
Test test = new Test();
System.out.println("没有发生死锁");
}

示例二
效果相同,可能更容易理解

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
public static class A{
public synchronized void say(B b) {
System.out.println("A说:先给我本,就给你笔,否则不给");
this.get(b);
this.give(b);
}
public synchronized void get(B b) {
b.give(this);
System.out.println("B说:我给你了昂");
}
public synchronized void give(B b) {
b.get(this);
System.out.println("B说:我收到了哦");
}
}
public static class B{
public synchronized void say(A a) {
System.out.println("B说:先给我笔,就给你本,否则不给");
this.get(a);
this.give(a);
}
public synchronized void get(A a) {
a.get(this);
System.out.println("A说:我给你了昂");
}
public synchronized void give(A a) {
a.give(this);
System.out.println("A说:我收到了哦");
}
}
public static class Test implements Runnable{
private static A a = new A();
private static B b = new B();
public Test() {
new Thread(this).start();
b.say(a);
}
@Override
public void run() {
a.say(b);
}
}
public static void main(String[] args) throws Exception {
Test test = new Test();
//new Thread(test).start();
System.out.println("没有发生死锁");
}

运行结果:
B说:先给我笔,就给你本,否则不给
A说:先给我本,就给你笔,否则不给

然后就没了下文。。。

×

纯属好玩

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

文章目录
  1. 1. 同步与异步
  2. 2. 同步问题示例
  3. 3. 同步操作示例
    1. 3.1. 同步代码块
    2. 3.2. 同步方法
  4. 4. 死锁
  5. 5. 死锁示例