Java学习路线-15:多线程的同步与死锁

举报
彭世瑜 发表于 2021/08/14 00:06:52 2021/08/14
【摘要】 第3 章 : 线程的同步与死锁 14 同步问题引出 Thread描述每一个线程对象 Runnable描述多个线程操作的资源 多个线程访问同一资源的时候,如果处理不当会产生数据错误 3个线程卖票程序,会出现多张同号的票 class MyThread implements Runnable { private int ticket = 10; @Override p...

第3 章 : 线程的同步与死锁

14 同步问题引出

Thread描述每一个线程对象
Runnable描述多个线程操作的资源
多个线程访问同一资源的时候,如果处理不当会产生数据错误

3个线程卖票程序,会出现多张同号的票

class MyThread implements Runnable { private int ticket = 10; @Override public void run() { while (true) { if (this.ticket > 0) { System.out.println( Thread.currentThread().getName() + "卖第" + this.ticket + " 张票" ); this.ticket--; } else { System.out.println("票卖光了"); break; } } }
}

public class Demo { public static void main(String[] args) { MyThread thread = new MyThread(); new Thread(thread).start(); new Thread(thread).start(); new Thread(thread).start(); // 5 }
}


  
 
  • 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

15 线程同步处理

同步:多个操作在同一时间段内只能有一个线程进行,
其他线程要等待此线程完成之后才可以继续还行

解决同步问题的方式是锁
synchronized定义同步方法或同步代码块,里边的代码只允许一个线程执行

加入同步之后,程序整体性能下降了

1、同步代码块

synchronized(同步对象){}

  
 
  • 1

举例

synchronized (this) { if (this.ticket > 0) { System.out.println(Thread.currentThread().getName() + "卖第" + this.ticket + " 张票"); this.ticket--; } else { System.out.println("票卖光了"); break; }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

2、同步函数

public synchronized boolean method(){}

  
 
  • 1

举例

public synchronized boolean sale(){ if (this.ticket > 0) { System.out.println(Thread.currentThread().getName() + "卖第" + this.ticket + " 张票"); this.ticket--; return true; } else { System.out.println("票卖光了"); return false; }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

16 线程死锁

死锁是在进行多线程同步处理之中有可能产生的一种问题
是指若干个线程彼此互相等待的状态

若干线程访问同一资源时,一定要进行同步处理
而过多的同步会造成死锁

public class Demo { public static void main(String[] args) { //o1 o2 代表资源 Object o1 = new Object(); Object o2 = new Object(); System.out.println("go go go!"); Thread t1 = new Thread(new Runnable() { public void run() { synchronized (o1) {   //线程t1获取o1的锁才能继续执行 try { Thread.sleep(3000); //睡3秒,确保线程t2把o2锁拿走 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("t1获得了哦O1"); synchronized (o2) { //线程t1获取o2的锁才能继续执行 System.out.println("t1获得了哦O2"); } } } }); Thread t2 = new Thread(new Runnable() { public void run() { synchronized (o2) {  //线程t2获取o2的锁才能继续执行 try { Thread.sleep(3000); //睡3秒,确保线程t1把o1锁拿走 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("t2获得了哦O2"); synchronized (o1) { //线程t2获取o1的锁才能继续执行 System.out.println("t2获得了哦O1"); } } } }); t1.start(); t2.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
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

第4 章 : 综合实战:“生产者-消费者”模型

17 生产者与消费者基本程序模型

生产者负责信息内容生产
消费者取走信息

消费者要等待生产者生产完成再取走
生产者需要等待消费者消费完成再生产

不加锁示例

class Message { private String content; public void setContent(String content) { this.content = content; } public String getContent() { return content; }
}

class Producer implements Runnable { private Message message; private static int count; public Producer(Message message) { this.message = message; } @Override public void run() { for (int i = 0; i < 10; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } this.message.setContent("这是第" + count + " 个消息"); count++; } }
}


class Consumer implements Runnable { private Message message; public Consumer(Message message) { this.message = message; } @Override public void run() { for (int i = 0; i < 10; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(this.message.getContent()); } }
}

class Demo { public static void main(String[] args) { Message message = new Message(); new Thread(new Producer(message)).start(); new Thread(new Consumer(message)).start(); }
}
/**
这是第0 个消息
这是第0 个消息
这是第1 个消息
这是第2 个消息
这是第3 个消息
这是第4 个消息
这是第5 个消息
这是第6 个消息
这是第7 个消息
这是第8 个消息
*/

  
 
  • 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
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74

18 解决生产者-消费者同步问题

增加关键字 synchronized

19 利用Object类解决重复操作

等待机制
(1)一直等待

public final void wait()

  
 
  • 1

(2)等待一段时间

public final native void wait(long timeout)

  
 
  • 1

唤醒线程
(1)唤醒一个等待线程, 唤醒第一个等待的线程

 public final native void notify();

  
 
  • 1

(2)唤醒全部等待线程,谁优先级高谁先执行

 public final native void notifyAll();

  
 
  • 1

完整代码

 class Message { private String content; private boolean flag = false; // 生产完成就为true public synchronized void setContent(String content) { if (this.flag == true) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } this.content = content; this.flag = true; notify(); } public synchronized String getContent() { if (this.flag == false) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } try { return content; } finally { this.flag = false; notify(); } }
}

class Producer implements Runnable { private Message message; private static int count; public Producer(Message message) { this.message = message; } @Override public void run() { for (int i = 0; i < 10; i++) { this.message.setContent("这是第" + count + " 个消息"); count++; } }
}


class Consumer implements Runnable { private Message message; public Consumer(Message message) { this.message = message; } @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(this.message.getContent()); } }
}

class Demo { public static void main(String[] args) { Message message = new Message(); new Thread(new Producer(message)).start(); new Thread(new Consumer(message)).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
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88

第5 章 : 多线程深入话题

20 优雅的停止线程

已废除的方法,可能会导致线程死锁,不建议使用

// 停止线程 
public final void stop()

// 销毁线程 
public void destroy()

// 挂起线程 
public final void suspend()

// 恢复线程 
public final void resume()

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

使用flag 标志位不会立刻停止,而是当前线程自己判断

class Demo{ private static boolean flag = true; public static void main(String[] args) { new Thread(()->{ while (flag){ try { Thread.sleep(600); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在执行"); } }, "自定义线程").start(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("时间到"); flag = false; }
}

  
 
  • 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

21 后台守护线程

守护线程,如果主线程退出,守护线程就退出
GC就是守护线程

设置为守护线程

public final void setDaemon(boolean on)

  
 
  • 1

判断是否为守护线程

public final boolean isDaemon()

  
 
  • 1

示例
设置线程为守护线程后,主程序执行完毕就退出了,并不会打印任何内容

class MyThread implements Runnable{ @Override public void run() { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+" 正在执行"); }
}

class Demo{ public static void main(String[] args) { Thread t = new Thread(new MyThread()); t.setDaemon(true); t.start(); }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

22 volatile关键字

volatile 用于属性定义, 中文意思:易变的

变量处理的步骤:
(1)获取变量原有的数据内容副本
(2)利用副本为变量进行数学计算
(3)建计算后的变量,保存到原始空间中

读取read <- 数据副本
加载load
使用use
赋值asign
存储store
写入write  -> 原始空间

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

属性上加了volatile, 没有中间拷贝过程,直接使用原始数据

区别:volatile 和 synchronized
volatile: 主要在属性上使用,无法描述同步,直接内存处理,避免副本操作
synchronized: 代码块与方法上使用

class MyThread implements Runnable{ private volatile int count = 10; @Override public void run() { while (count>0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+" 正在执行"); count --; } }
}

class Demo{ public static void main(String[] args) { Thread t = new Thread(new MyThread()); t.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

文章来源: pengshiyu.blog.csdn.net,作者:彭世瑜,版权归原作者所有,如需转载,请联系作者。

原文链接:pengshiyu.blog.csdn.net/article/details/103059816

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。