Java线程同步的实现
来源:互联网 发布:国家顶级域名证书 编辑:程序博客网 时间:2024/05/08 05:38
线程共享受限资源
1、基本上所有的并发模式在解决线程冲突的时候,都是采用序列化访问共享资源的方案,这一位着在给定时刻只允许一个任务访问共享资源;
这通常是在代码前加上一条锁语句实现的,这种机制常常被称为互斥量(mutex);
2、隐式加锁同步:使用synchronized关键字
1)共享资源一般是以对象形式存在的内存片段,但也可以是文件、输入/输出端口、打印机;
2)要控制共享资源的访问,首先要将其包装进一个对象,然后要将所有访问该资源的方法(或代码块)标记为synchronized;
同时要将该共享域标记为private;
3)示例代码
1、对整个方法进行同步class demo{ private int shareResource; public synchronized void method1(){ shareResource ++; } public synchronized void method2(){ shareResource --; }}2、对方法中需要同步的地方进行同步class demo{ private int shareResource; public void method1(){ System.out.println("method1 start"); synchronized(this){ shareResource ++; } System.out.println("method1 end"); } public void method2(){ System.out.println("method2 start"); synchronized(this){ shareResource --; } System.out.println("method2 end"); }}
4)对于某个特定的对象来说,其范围内所有的synchronized方法共享一个锁;
5)每个访问临界共享资源的方法都必须被同步,否则其他方法会随意忽视该锁;
3、显式加锁同步:利用Lock进行加锁
1)java SE5中的java.util.concurrent.Locks 可以显式地定义互斥机制;
2)示例代码
class Demo{ private int shareResource; private Lock lock = new ReentrantLock(); public int method{ lock.lock(); try{ shareResource ++; return shareResource; }finallly{ lock.unlock(); } }//将同步块放置在try-finally块中,return必须在try块中,以确保unlock不会过早发生,从而将数据暴露给第二个任务;}
3)使用synchronized隐式加锁的代码量更少,通常在一些特殊情况下才会使用Lock对象,如:
①使用synchronized不能尝试获取锁,且最终获取锁失败时只会抛出一个异常,无法进行任何的清除工作;
②尝试获取锁一段时间,然后释放它;
class Demo{ private Reentrantock lock = new ReentrantLock(); public void method1(){ boolean result = lock.tryLock(); //仅在调用时,lock处于空闲状态才获取锁; try{ System.out.println(result); }finally{ if(result) lock.unlock(); } } public void method2(){ boolean result = false; try{ result = lock.tryLock(2,TimeUnit.SECONDS); //在调用时,lock在2seconds内处于空闲状态,且线程在这个时间段类没有被打断,才获取锁; }catch(InterruptedException e){ System.out.println(result); }finally{ if(result) lock.unlock(); } }}
4、通过限制1个许可信号量来模拟一个互斥的锁(信号量时用来限制访问共享资源的线程数)
class Task{ Semaphore semaphore = new Semaphore(1); public void xMethod(){ try{ semaphore.acquire(); statement; }catch(InterruptedException ex){ }finally{ semaphore.release(); } } }
5※、利用原子类取代synchronized互斥同步
1)原子性:原子操作不需要进行同步控制,原子操作时不能被线程调度中断的操作,一旦操作开始,那么它一定可以在可能发生的“上下文切换”之前执行完毕,可以利用这一点的特定来编写无锁的代码;
2)原子性可以应用在除了long和double之外的所有基本类型之上的“简单操作”,对于读写除了long和double之外的基本变量这样的操作,可以保证他们会被当做不可分割的操作来操作内存;
3)原子类:Java SE5引入了如AtomicInteger,AtomincLong,AtomicReference等特殊的原子性变量,它们提供以下形式的原子性条件更新操作:
这些类被调整为使用在某些现代处理器上可获得的,并且在机器级别上的原子性,一般应用在性能调优上;
boolean compareAndSet(expectedValue,updateValue); 当前值==预期值,则以原子方式将该值设置为给定的值
int getAndAdd(int delta); 以原子的形式将给定值与当前值相加
int getAndSet(int delta); 以原子形式将当前值设置为给定值
int get();
void set(int newValue);
如:AtomicInteger i = new AtomicInteger(1024);
i.compareAndSet(1024,2048); //该操作是线程安全的
4)示例代码
import java.util.concurrent.atomic.*;class Demo{ private AtomicInteger count = new AtomicInteger(0); public int getValue(){ return count.get(); } public void method1(){ count.getAndAdd(20);} public void method2(){ count.getAndDerement(); //count++} public void method3(){ count.getAndIncrement(); //count--} //Test public static void main(){ Demo demo = new Deme(); ExecutorService exec = Executors.newCachedThreadPool(); exec.execute(new Runnable(){ public void run(){ while(true){ demo.method1(); System.out.println(demo.getValue()); } } }); exec.execute(new Runnable(){ public void run(){ while(true){ demo.method2(); System.out.println(demo.getValue()); } } }); exec.execute(new Runnable(){ public void run(){ while(true){ demo.method3(); System.out..println(demo.getValue()); } } }); }}//对数据进行原子性操作,不用使用synchronized就可以对操作进行同步;
6、临界区
1)临界区(critical section)/同步控制块:有时候只是希望多个线程同时访问方法内部的部分代码,而不是整个方法,这种方式分离出来的代码块就是临界区;
synchronized(syncObject){
statements;
}
2)在进入该同步控制块之前,必须获取syncObject对象的锁,如果其他线程已经获取该锁,那么要等到该锁释放后,才能进入该临界区;
3)使用同步控制块取代对整个方法进行同步控制,可以使多个任务访问对象的时间性能得到显著地提升;
4)synchronized块必须给定一个在其上进行同步的对象(一般比较合理的使用时synchronized(this)即本对象);
有时必须在另一个对象上同步,此时必须保证所有相关的任务都是在同一个对象上同步的,此时两个同步控制块是相互独立的,他们不会因为对方而阻塞,示例:
//解决限制:一个对象只能获取一个同步锁————使得两个任务可以同时进入同一个对象class Demo{ private Object syncObject = new Object(); public synchronized void method1(){ while(true){ println("method1()"); Thread.yield(); } } public void method2(){ synchronized(syncObject){ while(true){ println("method2()"); Thread.yield(); } } } //Test public static void main(){ final Demo demo = new Demo(); new Thread(){ public void run(){ demo.method1(); } } demo.method2(); }}
7、线程本地储存ThreadLocal
1)除了以上使用互斥量同步的方法外,放置任务子在共享资源上产生冲突的第二种反方式:根除对变量的共享;
2)在java中的线程本地储存是一种自动化机制,可以为使用相同变量的每一个线程创建不同的储存,可以使用 java.lang.ThreadLocal 来实现;
3)实例代码
class Demo{ private static ThreadLocal<Integer> value = new ThreadLocal<Integer>(); value.set(0); public static void mian(String[] args){ ExecutorService executor = Executors.newCachedThreadPool(); for(int i=0;i<5;i++){ executor.execute(new Runnable(){ public void run(){ value.set(value.get()+i); System.out.print(value.get()+","); } }); } TimeUnit.SECONDS.sleep(3); executor.shutdown(); }}/*output: 0,1,2,3,4*///只能使用set(),get()来修改和访问TreadLocal的数据;
0 0
- Java线程同步的实现
- Java线程同步-牛逼的线程同步实现
- JAVA实现线程同步
- Java的多线程-实现线程同步
- java怎样实现线程的同步
- java实现线程同步的方法
- java如何实现线程的安全:线程的同步机制
- Java线程:线程的同步
- java的线程同步
- java 线程的同步
- Java线程的同步
- Java线程的同步
- java线程的同步
- JAVA的线程同步
- java的线程同步
- java线程的同步
- java 线程的同步
- java的线程同步
- yii框架 简单的增、删、改、查(分页)
- 简单工厂模式构建数据访问层以实现多种数据库之间的切换
- Bootstrap 表格
- java map练习之统计单词出现的次数
- java中json包的使用以及字符串,map,list,自定义对象之间的相互转换
- Java线程同步的实现
- Apache Commons工具集简介
- Fragment
- SQL Server自定义函数(一)
- arguments
- AI范围检测(指定范围内攻击)
- Pycharm代码提示更新问题
- 带压堵漏技术在检修中的应用
- mosquitto源码学习笔记(1)