线程同步:锁的初步认识(三)
来源:互联网 发布:java病毒代码 编辑:程序博客网 时间:2024/05/19 07:07
- 同步机制介绍
- 使用synchronized实现同步
- 修饰一个代码块
- 修饰对象里的代码
- 修饰对象
- 修饰一个方法
- 修饰一个静态方法
- 修饰一个类
- 总结
同步机制介绍
- 一个资源被多个执行线程共享,为了防止这个资源可能出现的错误或者数据不一致,人们引入了临界区(critical section)概念。临界区是一个用于访问共享资源的代码块,在统一时间内该代码块只能有一个线程执行
- Java语言提供了两种基本同步机制
1.synchronized关键字机制
2.Lock接口及其实现机制
使用synchronized实现同步
- 修饰一个代码块,作用范围是synchronized(this){…}扩起来的代码;
- 修饰一个方法,作用范围是整个方法,作用对象是调用这个方法的对象
- 修饰静态方法,其作用范围是整个静态方法,作用对象是这个类的所有对象;
- 修饰一个类,其作用范围是synchronized(className.Class){…}扩起来的部分,作用的对象是这个类的所有对象
1.修饰一个代码块
1.修饰对象里的代码
public class SyncThread implements Runnable { private int number = 0; @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i < 3; i++) { synchronized (this) { this.number++; System.out.printf("Thread name: %s number=%d\n", Thread.currentThread().getName(), this.number); } } }}
执行:
SyncThread syncThread = new SyncThread();
Thread thread = new Thread(syncThread, “syncThread1”);
Thread thread2 = new Thread(syncThread, “syncThread2”);
thread.start();
thread2.start();
结果如下:
Thread name: syncThread1 number=1
Thread name: syncThread1 number=2
Thread name: syncThread1 number=3
Thread name: syncThread2 number=4
Thread name: syncThread2 number=5
Thread name: syncThread2 number=6
当两个并发线程访问同一个对象的同步代码块时,只有上一个线程执行完之后释放该对象锁下一个线程才能执行该代码块并获得该对象的锁。
对以上执行做如下调整:
执行:
Thread thread = new Thread(new SyncThread(), “syncThread1”);
Thread thread2 = new Thread(new SyncThread(), “syncThread2”);
thread.start();
thread2.start();
结果如下:
Thread name: syncThread2 number=1
Thread name: syncThread1 number=1
Thread name: syncThread1 number=2
Thread name: syncThread2 number=2
Thread name: syncThread2 number=3
Thread name: syncThread1 number=3
发现执行结果并未与上一个执行一致。究其原因,两个线程中的对象并不一致,而synchronized (this) {…}锁定的是对象,thread与thread2拿到的锁并不相同,两个对象的锁是互不干扰,所以线程同时执行。
2.修饰对象
1.创建一个账户信息类
/** * @Desciption 创建一个账号类 * @Author Baozi */public class Account { private float balance; public Account(float balance) { this.balance = balance; } public double getBalance() { return balance; } public void addAmount(float amount) { try { Thread.sleep(10); } catch (Exception e) { e.printStackTrace(); } this.balance += amount; } public void subtractAmount(float amount) { try { Thread.sleep(10); } catch (Exception e) { e.printStackTrace(); } this.balance -= amount; }}
2.创建一个消费者
public class Bank implements Runnable { private Account account; public Bank(Account account) { super(); this.account = account; } @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i < 10; i++) { synchronized (account) { this.account.subtractAmount(1000); } } }}
3.创建一个存款人
public class Company implements Runnable { private Account account; public Company(Account account) { this.account = account; } @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i < 10; i++) { synchronized (account) { this.account.addAmount(1000); } } }}
执行
Account account = new Account(1000); Bank bank = new Bank(account); Thread t = new Thread(bank); Company company = new Company(account); Thread t1 = new Thread(company); System.out.printf("Account : Initial Balance: %f\n", account.getBalance()); t.start(); t1.start(); try { t.join(); t1.join(); System.out.printf("Account : Final Balance: %f\n", account.getBalance()); } catch (InterruptedException e) { e.printStackTrace(); }
结果:
Account : Initial Balance: 1000.000000
Account : Final Balance: 1000.000000
以上结果可以看出修饰对象相当于将同步代码块的大小扩大到整个实例化对象。
修饰一个方法
public class SyncMethod implements Runnable { private int number = 0; @Override public void run() { for (int i = 0; i < 3; i++) { String name = Thread.currentThread().getName(); switch (name) { case "1": this.addNumber(name); break; default: this.printfNumber(name); break; } } } private void printfNumber(String name) { System.out.printf("Thread name: %s number=%d\n", name, this.number); } private synchronized void addNumber(String name) { this.number++; System.out.printf("Thread name: %s number++=%d\n", name, this.number); }}
执行:
SyncMethod method = new SyncMethod();
Thread thread = new Thread(method, “syncThread1”);
Thread thread2 = new Thread(method, “syncThread2”);
thread.start();
thread2.start();
结果如下:
Thread name: 2 number=1
Thread name: 1 number++=1
Thread name: 1 number++=2
Thread name: 2 number=1
Thread name: 2 number=3
Thread name: 1 number++=3
以上结果可以看出一个线程访问同步方法的情况下,另一个线程是可以访问非同步方法而不受阻塞,但是存在获取共享变量时可能存在线程安全问题。
修饰一个静态方法
public class SyncStatic implements Runnable { private static int number = 0; @Override public void run() { addNumber(); } public synchronized static void addNumber() { for (int i = 0; i < 3; i++) { SyncStatic.number++; System.out.printf("Thread name: %s number=%d\n", Thread.currentThread().getName(), number); } }}
执行:
Thread thread = new Thread(new SyncStatic(), “syncStatic1”);
Thread thread2 = new Thread(new SyncStatic(), “syncStatic2”);
thread.start();
thread2.start();
结果如下:
Thread name: syncStatic1 number=1
Thread name: syncStatic1 number=2
Thread name: syncStatic1 number=3
Thread name: syncStatic2 number=4
Thread name: syncStatic2 number=5
Thread name: syncStatic2 number=6
以上结果结合静态修饰符的特性可以看出,两个线程虽然两个线程的对象不一致,但是引用的方法都是静态方法,而静态修饰符使该方法拥有唯一属性,所以两个线程使用的是同一个锁
修饰一个类
SyncClass implements Runnable { private int number; @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i < 3; i++) { synchronized (SyncThread.class) { this.number++; System.out.printf("Thread name: %s number=%d\n", Thread.currentThread().getName(), this.number); } } }}
执行:
Thread thread = new Thread(new SyncThread(), “syncThread1”);
Thread thread2 = new Thread(new SyncThread(), “syncThread2”);
thread.start();
thread2.start();
结果如下:
Thread name: syncThread1 number=1
Thread name: syncThread1 number=2
Thread name: syncThread1 number=3
Thread name: syncThread2 number=1
Thread name: syncThread2 number=2
Thread name: syncThread2 number=3
以上结果与修饰对象类似,不同在于synchronized作用于一个类C时,所有C对象是哟同一个锁。
总结
- synchronized修饰符作用与非静态方法或者类上时,锁的基本单位是对象,不同对象获取到的锁是不一致的;
- synchronized修饰的代码块越小阻塞的时间越短,造成死锁的可能性越小,效率越高;
- 线程同步:锁的初步认识(三)
- java线程同步锁synchronized的认识
- 线程初步(三)
- android中Handler的初步认识(三)
- Jenkins学习笔记(三) master/slave的初步认识
- android多线程之AsyncTask的初步认识(三)
- Java的四种线程初步认识
- JAVA初步认识线程
- 线程池初步认识
- 多线程学习(三)线程的同步与锁
- 对Java线程同步的认识
- Java线程(三):同步与锁
- 线程实用解析--------(三)线程的同步
- (四十三)、线程的同步和线程池
- 线程的初步认识——2011-7-7
- 线程同步三:Slim锁
- 线程三、锁与同步
- OpenMP(三) 线程同步
- Android下的OOM异常、内存抖动和界面卡顿
- 解析 nginx启动期做了哪些事
- javascript中的window事件
- SDUT 2117数据结构实验之链表二:逆序建立链表
- 2012-2013 ACM-ICPC, Asia Tokyo Regional Contest Beautiful Spacing 二分 + dp + 双指针扫描
- 线程同步:锁的初步认识(三)
- 【codevs 1026】逃跑的拉尔夫
- Slam编程一 旋转向量、旋转矩阵,欧拉角、变换矩阵和四元数
- matlab中的uigetfile函数和setappdata函数
- L1,L2 正则化与过拟合问题
- Win10 配置Docker -- 运行MySQL实例篇
- Windows10上再安装个Linux系统
- 213. House Robber II
- Linux命令之head,tail