异步机制: 在多线程并发,对数据进行操作的时候不会进行“排队”有可能发生多个线程同时对一个数据进行操作,这个时候会存在线程安全问题(也就是平常线程执行任务)。 同步机制: 在多线程并发,对数据进行操作的时候会先等待之前的线程操作完之后才会轮到下一个线程进行操作,形成了类似于排队进行操作的机制,这就是同步机制。 同步与异步的对比 概述: 多线程并发的时候共享同一个资源,在对数据进行修改的时候有可能会造成数据的值发生错误。 下面这个例子为了方便我将其写在了一起 既然在多线程并发的时候有线程安全问题的存在,那么我们应该如何解决线程的安全问题? 步骤: synochronized 关键字: 作用:锁住synchronized代码块中的内容,使得有共享对象的线程执行的时候会排队执行,进入synchronized代码块的线程会占有这把锁,知道这个线程执行完之后才会释放锁,下一个线程才能进入。 1.修饰要操作的语句 2.直接修饰其方法(代表将整个方法锁住) 共享对象: 像上面的例子(取款的例子)中,账户对象user就是共享对象,两个线程同时对一个对象操作,这个对象就是共 概述: 将synchronized代码块中的内容锁起来,只有有共同对象的线程才能进入,并且一个线程进入之后会占有这把对象锁其它线程就不得再进入,必须等进入的线程完成操作释放这把对象锁之后才能进入,就使得线程的执行方式变成了同步机制,即就是排队执行。 锁的分类:对象锁和类锁 将上个例子改为线程安全的(也就是同步机制),其它代码不变,只需将取款方法变化一下即可,请看代码。 概述: 死锁就是锁使用不当,导致程序僵持住了,不在进行下一步的执行,而且也不会报错,也不会出现异常。 请看一个死锁现象的例子,请看以下代码。 解释:当两个线程,线程一和线程二运行的时候,线程一先占住了t1锁,然后在占住了t2锁,然后再释放t2锁,再释放t1锁,线程二刚好与它相反,是先占t2锁后占t1锁,但是,这个时候容易发生一个问题,如果当线程一占住了t1锁的时候,线程二占住了t2锁,那么线程一就不能进入t2锁中,而线程一占住了t1锁,那么线程二就不能进入t1锁中,因为要结束锁中的内容才能释放锁,而这个时候两个线程的执行都没有结束,所以释放不了锁,这个时候程序就会僵持住,不会进行下一步,不会报错,也不会出现异常,这就是死锁。 因为死锁不会报错,也不会出现异常,所以在程序代码较多的情况下不容易找出这个错误,因此,我们就要在写程序的时候避免死锁现象的发生,也就是不用synchronized进行嵌套使用。 概述: 这个模式就是生产者进行生产,消费者进行消费,当生产的产品在仓库中满了的话,让生产者停止生产,让消费者消费,同样的,当仓库中的消费产品完了的时候,让消费者停止消费,让生产者进行生产。 1. 生产者与消费者模式也属于多线程并发问题,也需要考虑线程安全问题。 2. 需要用到的方法:wait()方法和notify()方法(这两个方法的介绍可以看我上一篇博客“多线程的总结(上)”) 3. 在这个例子中也有对wait()方法和notify()方法也有比较详细的介绍,就在代码最上面的注释中,也可以看我上一篇博客“多线程的总结(上)”中,也对这两个方法有所介绍。 关于消费者与生产者的例子,请看以下代码。 生产者和消费者线程不一定要设置为守护线程,视具体情况而定。 码字不易,不要白嫖,觉得有用的,可以给我点个赞。感谢!
一、线程安全问题
先看一下同步机制与异步机制
异步机制的缺点: 存在线程安全问题。
1.什么是线程安全问题?
请看以下代码。/** * 线程安全问题的举例: * 背景:两个人(可看做是两个线程)同时对一个账户进行取钱操作 * 在某个人有可能的网络延迟下,取款后的余额将会发生错误 * 会出现,多取了钱,但余额没减的情况。 * 这就是线程安全问题,在多线程并发的时候 */ public class ThreadSafe { public static void main(String[] args) { //创建一个账户,初始化余额为1000,让两个线程对此账户进行取款500的操作。 Account user=new Account(1000); //创建取款线程一 Thread t1=new Thread(new TestThread1(user)); //创建取款线程二 Thread t2=new Thread(new TestThread2(user)); //修改线程名字 t1.setName("取款线程一"); t2.setName("取款线程二"); //启动两个取款线程 t1.start(); t2.start(); } } class Account{ public static final String name="Jack"; private double balance; public Account(double balance) { this.balance = balance; } public static String getName() { return name; } public double getBalance() { return balance; } //取款的方法 public void drawMoney(double money) throws InterruptedException { if(money<=getBalance()) { double after = getBalance() - money; //模拟取款线程一网络延迟0.05秒 if("取款线程一".equals(Thread.currentThread().getName())) { Thread.sleep(50); } //刷新取款后的余额 this.balance=after; System.out.println(Thread.currentThread().getName()+",取款:"+money+"元"+",取款后剩余金额:"+getBalance()); }else { System.out.println("余额不足!"); } } } //线程一 class TestThread1 implements Runnable{ private Account user; public TestThread1(Account user){ this.user=user; } public void run(){ try { //调用取款方法 user.drawMoney(500); } catch (InterruptedException e) { e.printStackTrace(); } } } //线程二 class TestThread2 implements Runnable{ private Account user; public TestThread2(Account user){ this.user=user; } public void run(){ try { //调用取款方法 user.drawMoney(500); } catch (InterruptedException e) { e.printStackTrace(); } } }
2. 线程安全问题应该如何解决?
答:采用同步机制处理。2.1 同步机制的实现
语法:
synchronized(共享对象){
java语句;
}
修饰符 synchronized 返回值类型 方法名(形参列表){
java语句;
}.
享的2.2 锁的概念
//取款的方法 public void drawMoney(double money) throws InterruptedException { //this代表的是当前对象,也就是账户对象。 synchronized(this) { if (money <= getBalance()) { double after = getBalance() - money; //模拟取款线程一网络延迟0.05秒 if ("取款线程一".equals(Thread.currentThread().getName())) { Thread.sleep(50); } //刷新取款后的余额 this.balance = after; System.out.println(Thread.currentThread().getName() + ",取款:" + money + "元" + ",取款后剩余金额:" + getBalance()); } else { System.out.println("余额不足!"); } } }
2.3 死锁情况的产生
public class TestThreadSafe2 { public static void main(String[] args) { Test t1=new Test(); Test t2=new Test(); Thread thread1=new Thread(new Thread2(t1,t2)); Thread thread2=new Thread(new Thread021(t1,t2)); thread1.setName("线程一"); thread2.setName("线程二"); thread1.start(); thread2.start(); } } class Thread2 implements Runnable{ Test t1; Test t2; public Thread2(Test t1,Test t2){ this.t1=t1; this.t2=t2; } public void run() { /* //让其中一个线程先睡眠一会可以解除死锁 if(Thread.currentThread().getName().equals("线程一")){ try { System.out.println(Thread.currentThread().getName()+"正在睡眠三秒"); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } }*/ synchronized(t1){ System.out.println(Thread.currentThread().getName()+"正在执行"+t1); synchronized(t2){ System.out.println(Thread.currentThread().getName()+"正在执行"+t2); } } } } class Thread021 implements Runnable{ Test t1; Test t2; public Thread021(Test t1,Test t2){ this.t1=t1; this.t2=t2; } public void run() { synchronized(t2){ System.out.println(Thread.currentThread().getName()+"正在执行"+t2); synchronized(t1){ System.out.println(Thread.currentThread().getName()+"正在执行"+t1); } } } } class Test{ //@Override public String toString() { return super.toString(); } }
二、 生产者与消费者模式
/** * 使用wait()方法和notify()方法实现“生产者和消费者模式” * 生成线程负责生产,消费线程负责消费。 * *wait()方法和notify()方法共享数据,因此要建立在 synchronized 基础之上(这两个方法是java对象的普通方法,不是线程方法) * * wait()方法:o.wait() 让正在o对象上获取的线程进入等待状态,并且释放掉之前占有的o对象的锁 * notify()方法:o.notify() 唤醒在o对象上等待的线程(只是通知,不会释放o对象之前占有的锁) */ public class TestWaitAndNotify { public static void main(String[] args) { List<StudentX> list=new ArrayList<>(); Thread t1=new Thread(new Producer(list)); Thread t2=new Thread(new Consumer(list)); t1.setName("生产者线程"); t2.setName("消费者线程"); //将t1和t2线程设置为守护线程 t1.setDaemon(true); t2.setDaemon(true); t1.start(); t2.start(); try { //主线程睡眠10秒 Thread.sleep(1000*10); } catch (InterruptedException e) { e.printStackTrace(); } } } //生产线程 class Producer implements Runnable{ private List<StudentX> list; public Producer(List list){ this.list=list; } @Override //一直生产 public void run() { while(true) { //保证线程安全(给list加锁) synchronized(list) { if(list.size()>10){ try { list.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //程序能够执行到这,说明集合未满 //继续生产 StudentX s=new StudentX(18, "贺子旗"); list.add(s); System.out.println(Thread.currentThread().getName()+"生产了--->"+s.name); //唤醒消费者 list.notify(); } } } } //消费线程 class Consumer implements Runnable{ private List<StudentX> list; private int i=0; public Consumer(List list){ this.list=list; } @Override //一直消费 public void run() { while(true){ synchronized(list) { if(list.size()==0){ //集合空了,停止消费 try { list.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //程序能够执行到这说明集合未空 //创建一个0-10之间的随机整数 //int i = new Random().nextInt(10); System.out.println(Thread.currentThread().getName()+"消费了--->"+list.get(i).name); list.remove(i); //唤醒生产者 list.notify(); } } } } class StudentX{ int age; String name; public StudentX(int age, String name) { this.age = age; this.name = name; } }
因技术能力有限,如文中有不合理的地方,希望各位大佬指出,在下方评论区留言,谢谢,希望大家一起进步,一起成长。
如需转载请注明来源,谢谢!
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算