多线程15:线程同步

同步方法: 

  由于我们可以通过 private 关键字来保证数据对象只能被方法访问,所以我们只需要针对方法提供一套机制,这套机制就是 synchronized 关键字,它包括两种用法:synchronized 方法 和 synchronized 块


同步方法: public synchronized void method(int args){}

synchronized方法控制对"对象"的访问,每个对象对应一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就独占该锁,直到该方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行


缺陷:若将一个大的方法申明为sychronized将会影响效率

同步方法弊端:

只读代码,不需要同步每个人都可以读,读的时候是不会有错的

修改代码的时候才需要同步。所以还有个 sychronized 块,

方法里面需要修改的内容的才需要锁,锁的太多,浪费资源。

所以说同步方法有时候也不是那么高效。

同步块:

  • synchronized(Obj){}
  • Obj 称之为同步监视器
    • Obj可以是任何对象,但是推荐使用共享资源作为同步监视器
    • 同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象的本身,或者是 class [反射中讲解]
  • 同步监视器的执行过程
    • 第一个线程访问,锁定同步监视器,执行其中代码
    • 第二个线程访问,发现同步监视器被锁定,无法访问
    • 第一个线程访问完毕,解锁同步监视器
    • 第二线程访问,发现同步监视器没有锁,然后锁定并访问

案例一:安全的火车站买票

synchronized 同步方法,锁的是this,也就是BuyTicket的对象

 1 package com.thread.syn;
 2
 3 public class SafeBuyTicket {
 4
 5     public static void main(String[] args) {
 6         BuyTicket station = new BuyTicket();
 7
 8         new Thread(station, "苦逼的我").start();
 9         new Thread(station, "牛逼的你们").start();
10         new Thread(station, "可恶的黄牛党").start();
11
12     }
13 }
14
15
16 class BuyTicket implements Runnable {
17
18     //票
19     private int ticketNums = 10;
20     boolean flag = true;//外部停止方式
21
22     @Override
23     public void run() {
24
25         //买票
26         while (flag) {
27             try {
28                 buy();
29             } catch (InterruptedException e) {
30                 e.printStackTrace();
31             }
32         }
33     }
34
35     //synchronized 同步方法,锁的是this
36     private synchronized void buy() throws InterruptedException {
37         //判断是否有票
38         if (ticketNums <= 0) {
39             flag = false;
40             return;
41         }
42
43         //模拟延时
44         Thread.sleep(100);
45         //买票
46         System.out.println(Thread.currentThread().getName() + "拿到" + ticketNums--);
47     }
48
49 }
50
51 结果:
52 苦逼的我拿到10
53 苦逼的我拿到9
54 苦逼的我拿到8
55 苦逼的我拿到7
56 苦逼的我拿到6
57 苦逼的我拿到5
58 苦逼的我拿到4
59 牛逼的你们拿到3
60 牛逼的你们拿到2
61 牛逼的你们拿到1

案例二:银行取钱

synchronized默认是锁this,而this锁不住,因为它操作的是账户,不是银行。就需要用到同步块。

sychronized(Obj){},锁的对象是变化的量,需要增删改的对象

 1 package com.thread.syn;
 2
 3 public class SafeBank {
 4     public static void main(String[] args) {
 5         //账户
 6         Account account = new Account(100, "结婚基金");
 7
 8         Drawing you = new Drawing(account, 50, "你");
 9         Drawing girlFriend = new Drawing(account, 100, "girlFriend");
10
11         you.start();
12         girlFriend.start();
13
14     }
15 }
16
17
18 //账户
19 class Account {
20     int money;//余额
21     String name;//卡名
22
23     public Account(int money, String name) {
24         this.money = money;
25         this.name = name;
26     }
27 }
28
29 //银行:模拟取款
30 class Drawing extends Thread {
31
32     //账户
33     Account account;
34     //取了多少钱
35     int drawingMoney;
36     //现在手里有多少钱
37     int nowMoney;
38
39     public Drawing(Account account, int drawingMoney, String name) {
40         super(name);
41         this.account = account;
42         this.drawingMoney = drawingMoney;
43     }
44
45     //取钱
46     //synchronized 默认锁的是this.
47     @Override
48     public void run() {
49         //锁的对象就是变化的量,需要增删改的对象
50         synchronized (account) {
51             //判断有没有钱
52             if (account.money - drawingMoney < 0) {
53                 System.out.println(Thread.currentThread().getName() + "钱不够,取不了");
54                 return;
55             }
56
57             try {
58                 Thread.sleep(100);
59             } catch (InterruptedException e) {
60                 e.printStackTrace();
61             }
62
63             //卡内余额 = 余额 - 你取得钱
64             account.money = account.money - drawingMoney;
65             //你手里的钱
66             nowMoney = nowMoney + drawingMoney;
67
68             System.out.println(account.name + "余额为:" + account.money);
69             //Thread.currentThread().getName() = this.getName()
70             System.out.println(this.getName() + "手里的钱:" + nowMoney);
71
72         }
73     }
74
75 }
76 结果:
77 结婚基金余额为:50
78 你手里的钱:50
79 girlFriend钱不够,取不了

案例三:线程安全的集合

把list对象放进同步代码块,锁住即可

 1 package com.thread.syn;
 2
 3 import java.util.ArrayList;
 4 import java.util.List;
 5
 6 public class SafeList {
 7
 8     public static void main(String[] args) {
 9
10         List<String> list = new ArrayList<>();
11         for (int i = 0; i < 10000; i++) {
12             new Thread(() -> {
13                 synchronized (list){
14                     list.add(Thread.currentThread().getName());
15                 }
16             }).start();
17         }
18         try {
19             Thread.sleep(3000);
20         } catch (InterruptedException e) {
21             e.printStackTrace();
22         }
23         System.out.println(list.size());
24     }
25 }
26
27 结果:
28 10000

原文地址:https://www.cnblogs.com/duanfu/p/12260763.html

时间: 2025-01-17 03:36:24

多线程15:线程同步的相关文章

Linux程序设计学习笔记----多线程编程线程同步机制之互斥量(锁)与读写锁

互斥锁通信机制 基本原理 互斥锁以排他方式防止共享数据被并发访问,互斥锁是一个二元变量,状态为开(0)和关(1),将某个共享资源与某个互斥锁逻辑上绑定之后,对该资源的访问操作如下: (1)在访问该资源之前需要首先申请互斥锁,如果锁处于开状态,则申请得到锁并立即上锁(关),防止其他进程访问资源,如果锁处于关,则默认阻塞等待. (2)只有锁定该互斥锁的进程才能释放该互斥锁. 互斥量类型声明为pthread_mutex_t数据类型,在<bits/pthreadtypes.h>中有具体的定义. 互斥量

C#多线程之线程同步3

在上一篇C#多线程之线程同步2中,我们主要学习了AutoResetEvent构造.ManualResetEventSlim构造和CountdownEvent构造,在这一篇中,我们将学习Barrier构造.ReaderWriterLockSlim构造和SpinWait构造. 七.使用Barrier构造 在这一小节中,我们将学习一个比较有意思的同步构造:Barrier.Barrier构造可以帮助我们控制多个等待线程达到指定数量后,才发送通知信号,然后所有等待线程才能继续执行,并且在每次等待线程达到指

关于Java多线程的线程同步和线程通信的一些小问题(顺便分享几篇质量高的博文)

Java多线程的线程同步和线程通信的一些小问题(顺便分享几篇质量高的博文) 前言:在学习多线程时,遇到了一些问题,这里我将这些问题都分享出来,同时也分享了几篇其他博客主的博客,并且将我个人的理解也分享给大家. 一.对于线程同步和同步锁的理解(注:分享了三篇高质量的博客) 以下我精心的挑选了几篇博文,分别是关于对线程同步的理解和如何选择线程锁以及了解线程锁的作用范围. <一>线程同步锁的选择 1. 这里我推荐下Java代码质量改进之:同步对象的选择这篇博文. 2. 以上推荐的博文是以卖火车票为例

mfc小工具开发之定时闹钟之---多线程急线程同步

一.MFC对多线程编程的支持 MFC中有两类线程,分别称之为工作者线程和用户界面线程.二者的主要区别在于工作者线程没有消息循环,而用户界面线程有自己的消息队列和消息循环. 工作者线程没有消息机制,通常用来执行后台计算和维护任务,如冗长的计算过程,打印机的后台打印等.用户界面线程一般用于处理独立于其他线程执行之外的用户输入,响应用户及系统所产生的事件和消息等.但对于Win32的API编程而言,这两种线程是没有区别的,它们都只需线程的启动地址即可启动线程来执行任务. 在MFC中,一般用全局函数Afx

MFC——9.多线程与线程同步

Lesson9:多线程与线程同步 程序.进程和线程是操作系统的重点,在计算机编程中,多线程技术是提高程序性能的重要手段.本文主要讲解操作系统中程序.进程和线程之间的关系,并通过互斥对象和事件对象实例说明多线程和线程同步技术. 1.      程序.进程和线程 1.1  程序和进程 程序是计算机指令的集合,它以文件的形式存储在磁盘上.进程通常被定义为一个正在运行的程序的实例,是一个程序在其自身的地址空间中的一次执行活动.进程是资源申请.调度和独立运行的单位,因此,它使用系统中的运行资源:而程序不能

java多线程之 ---- 线程同步

java多线程之线程同步 线程同步 定义:同步是指在同一时间段内只能运行一个线程. 分类:同步方法.同步块. 作用:安全解决共享问题. 同步块: 语法: synchronized (同步对象) { 需要同步的代码; } 例子: public class ThreadDemo implements Runnable{ private int ticket = 5; public void run(){ for(int i=1;i<=5;i++){ synchronized (this){ if(t

多线程(6)线程同步

使用多线程很容易,但是如果多个线程同时访问一个共享资源时而不加以控制,就会导致数据损坏.所以多线程并发时,必须要考虑线程同步(或称线程安全)的问题. 什么是线程同步 多个线程同时访问共享资源时,使多个线程顺序(串行)访问共享资源的机制. 注意: 1,共享资源,比如全局变量和静态变量. 2,访问,一般指写操作,读操作无需考虑线程同步. 3,串行,指当一个线程正在访问共享资源时,其它线程等待,直到该线程释放锁. 线程同步带来哪些问题 如果能保证多个线程不会同时访问共享资源,那么就不需要考虑线程同步.

多线程以及线程同步

cocos2d-x引擎在内部实现了一个庞大的主循环,每帧之间更新界面,如果耗时的操作放到了主线程中,游戏的界面就会卡,这是不能容忍的,游戏最基本的条件就是流畅性,这就是为什么游戏开发选择C++的原因.另外现在双核手机和四核手机越来越普遍了,是时候使用多线程来挖掘硬件的潜力了. 1.环境搭建 cocos2d-x中的多线程使用pthread就可以实现跨平台,而且也不是很难理解.使用pthread需要先配置一下工程.右击工程----->属性----->配置属性---->链接器----->

Java多线程之线程同步

一.多线程出现的安全问题: 1.问题的原因: 多个线程执行的不确定性引起执行结果的不稳定.当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误.2.解决的办法: 对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行. 二.Synchronized的使用方法: 1.操作共享数据的代码,即为需要被同步的代码.不能包含代码多或者少.2.共享数据:多个线程共同操作的变量.比如:ticket就是共

系统API函数实现多线程及线程同步

1.线程的创建 须包含头文件:#include <windows.h> HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId ); lpThreadAttributes:指向SECURI