- 浏览: 139858 次
- 性别:
- 来自: 成都
文章分类
最新评论
-
elephant_xiang:
condition例子,搞错了吧public void pro ...
jdk1.5的多线程总结一 -
yinlei126:
这么好的文章没有评论,谢谢
Mina框架剖析--动态篇 -
xds2008:
写的不错,值得表扬,测了一下,还不错,就是有点慢,但是最起码远 ...
java实现远程桌面监控 -
exp111:
这个确实很强 但是能不能连接一次不停的传呢 这个好像是必须连一 ...
java实现远程桌面监控 -
seeallsea:
不错!可以相互转换了。
keystore证书转换
线程同步
线程同步其实就是线程排队访问资源。只有访问共享变量的情况下才需要同步。其一,要是共享的资源,比如一个文件,一个内存变量。其二,要是变量,如果是常量就不需要同步了。
线程锁定
同步的实现方法,就是线程锁定。线程访问共享资源,此线程即持有锁,如果此线程不release这个锁,其他线程就无法访问。锁定通过synchronized或者volatile关键字来实现。如果是对代码段套上synchronized,则表示所有线程在执行到这段代码的时候,必须排队
如果给方法加上,就表示所有线程执行到此方法都得排队。
如果是对static方法加上synchronized, 就对这个类的所有实例对象起作用。
如果是对static方法中的代码块加上synchronized也是一样,其实对static方法的同步可以理解为对类的class对象的同步。
对于某些基本类型如char,int,它们是原子性的,即使被共享访问,不需要考虑同步的问题,但是在某些jvm上,long和double是非原子性的,这时候需要对它们的访问同步化,除了synchronized方法块外,还可以直接用volatile修饰。
wait notify notifyAll
这三个方法在多线程开发中使用频率非常高,它们的实质都跟wait set有关。wait Set是一个执行该实例的wait方法时,所有停止的线程的集合,就像一个休息室。一旦发生如下几种动作:
1.有其他线程notify该线程。
2.有其他线程notifyAll。
3.有其他线程interrupt唤醒该线程。
4.wait方法到期。
线程都会退出wait Set。
interrupt、InterruptedException与sleep,wait,join的关系
线程的interrupt方法只是改变线程的中断状态,而sleep,wait,join在执行的时候会去检查线程的中断状态,如果是中断状态就抛出InterruptedException,否则该怎么着还是怎么着。因此并不是调用了interrupt线程就马上抛InterruptedException。还有个interrupted方法是设置线程的中断状态的。
多线程开发模式
1.共享互斥
多个线程访问同1个资源,对调用这个资源的代码块加上synchronized。但是,如果这个共享资源是类中的某个字段,它的子类却不知道这个字段是需要同步的,就会引发继承异常,子类对这个字段的访问不同步就会有问题。synchronized是不能被子类继承的,子类若要同步需要显式的指定。同步比异步执行要消耗更多的性能,我自己的机器上测试是2-4倍甚至更高的时间消耗,因此同步的范围应该尽可能的小,也就是同步粒度应该尽可能的细化,一般的原则就是,synchronized代码块尽可能的小。
2.常量对象
如果变量是只读性质的,可以让它常量化,这样就不需要互斥访问synchronized了,可以避免互斥带来的额外性能消耗。在只读共享对象被大量的线程并发访问的场景下,省下来的synchronized可以节省大量的性能。它一般要满足下面一些原则:
1.没有setter方法,因为这些方法会修改字段以及由字段引用的对象。
2.将字段声明为final和private访问类型。
3.不允许子类覆盖方法,最简单的办法是将类定义为final。稍微复杂点的方法是将构造函数定义为私有,并使用factory方法生成实例。
4.如果实例字段中包括可变对象的引用,则不允许这些对象被改变,也就是不要提供修该可变对象的方法。
5.不要将可变对象的引用共享;不要将可变对象的引用外部序列化;传递给构造函数的可变对象应复制备份赋给字段;不要返回原始对象,而应返回对象的clone。
Swing中大量使用了Immutable Object,比如Point、Rectangle等。由于用户界面属于输入响应交互式模式,因此存在大量的并发问题,尤其界面、数据一致性问题。Swing通过使用这些Immutable Object较好的避免了并发问题。
更灵活一点,可设计成2种类,一种可变,一种不可变,两者之间可以相互构造,就像StringBuffer和String。比如如下对象
3.条件等待-唤醒/放弃
这个模式大致归纳成3种必须元素:有循环,有条件检查,有等待唤醒或放弃。线程执行一个操作,发现条件不允许的时候,等待,直到条件满足后被唤醒;或者放弃当前操作,下次再执行。根据不满足条件的场景,一般有几类做法:条件不满足,当前线程无限等待,直到被其他线程唤醒;条件不满足,yield当前线程并继续下一次条件检查;条件不满足,sleep当前线程,再循环检查等。
4.生产消费模式
如果需要线程A向线程B单向通信,而两者的处理速度差了很多,就可以使用这种方案。一般两者之间有一个缓冲队列或者缓冲区,生产者线程往队列里塞数据,消费者线程不断的从队列中拿数据执行。这样线程之间的共享访问冲突就集中在缓冲区上。下面是个简单的生产消费者例子。
用于缓冲数据的Box
生产者线程
5.读写锁
它将读取和写入分开处理,对于读取并发不做共享互斥,对于读写、写写并发做互斥,适用于读取频繁,写入较少的共享互斥情况,比如读远多于写的缓存。这种情况下,比直接的共享互斥有很大的性能提高。它的主要核心就是读写锁,做读操作就获取读的锁,写操作就获取写的锁.下面是个很简单的读写锁实现:
然后是使用这个读写锁的共享数据
读写线程就分别调用read和write就可。
在jdk1.5里有ReadWriteLock接口以及实现类ReentrantReadWriteLock。
线程同步其实就是线程排队访问资源。只有访问共享变量的情况下才需要同步。其一,要是共享的资源,比如一个文件,一个内存变量。其二,要是变量,如果是常量就不需要同步了。
线程锁定
同步的实现方法,就是线程锁定。线程访问共享资源,此线程即持有锁,如果此线程不release这个锁,其他线程就无法访问。锁定通过synchronized或者volatile关键字来实现。如果是对代码段套上synchronized,则表示所有线程在执行到这段代码的时候,必须排队
如果给方法加上,就表示所有线程执行到此方法都得排队。
如果是对static方法加上synchronized, 就对这个类的所有实例对象起作用。
如果是对static方法中的代码块加上synchronized也是一样,其实对static方法的同步可以理解为对类的class对象的同步。
对于某些基本类型如char,int,它们是原子性的,即使被共享访问,不需要考虑同步的问题,但是在某些jvm上,long和double是非原子性的,这时候需要对它们的访问同步化,除了synchronized方法块外,还可以直接用volatile修饰。
wait notify notifyAll
这三个方法在多线程开发中使用频率非常高,它们的实质都跟wait set有关。wait Set是一个执行该实例的wait方法时,所有停止的线程的集合,就像一个休息室。一旦发生如下几种动作:
1.有其他线程notify该线程。
2.有其他线程notifyAll。
3.有其他线程interrupt唤醒该线程。
4.wait方法到期。
线程都会退出wait Set。
interrupt、InterruptedException与sleep,wait,join的关系
线程的interrupt方法只是改变线程的中断状态,而sleep,wait,join在执行的时候会去检查线程的中断状态,如果是中断状态就抛出InterruptedException,否则该怎么着还是怎么着。因此并不是调用了interrupt线程就马上抛InterruptedException。还有个interrupted方法是设置线程的中断状态的。
多线程开发模式
1.共享互斥
多个线程访问同1个资源,对调用这个资源的代码块加上synchronized。但是,如果这个共享资源是类中的某个字段,它的子类却不知道这个字段是需要同步的,就会引发继承异常,子类对这个字段的访问不同步就会有问题。synchronized是不能被子类继承的,子类若要同步需要显式的指定。同步比异步执行要消耗更多的性能,我自己的机器上测试是2-4倍甚至更高的时间消耗,因此同步的范围应该尽可能的小,也就是同步粒度应该尽可能的细化,一般的原则就是,synchronized代码块尽可能的小。
2.常量对象
如果变量是只读性质的,可以让它常量化,这样就不需要互斥访问synchronized了,可以避免互斥带来的额外性能消耗。在只读共享对象被大量的线程并发访问的场景下,省下来的synchronized可以节省大量的性能。它一般要满足下面一些原则:
1.没有setter方法,因为这些方法会修改字段以及由字段引用的对象。
2.将字段声明为final和private访问类型。
3.不允许子类覆盖方法,最简单的办法是将类定义为final。稍微复杂点的方法是将构造函数定义为私有,并使用factory方法生成实例。
4.如果实例字段中包括可变对象的引用,则不允许这些对象被改变,也就是不要提供修该可变对象的方法。
5.不要将可变对象的引用共享;不要将可变对象的引用外部序列化;传递给构造函数的可变对象应复制备份赋给字段;不要返回原始对象,而应返回对象的clone。
Swing中大量使用了Immutable Object,比如Point、Rectangle等。由于用户界面属于输入响应交互式模式,因此存在大量的并发问题,尤其界面、数据一致性问题。Swing通过使用这些Immutable Object较好的避免了并发问题。
更灵活一点,可设计成2种类,一种可变,一种不可变,两者之间可以相互构造,就像StringBuffer和String。比如如下对象
public final class ImmutablePerson { private final String name; private final String address; public ImmutablePerson(String name, String address) { this.name = name; this.address = address; } public ImmutablePerson(MutablePerson person) { synchronized(person) {//去掉这个同步限制就会发现问题了 this.name = person.getName(); this.address = person.getAddress(); } } public MutablePerson getMutablePerson() { return new MutablePerson(this); } public String getName() { return name; } public String getAddress() { return address; } public String toString() { return "[ ImmutablePerson: " + name + ", " + address + " ]"; } }可以看到对象的字段全部是final的,而且没有set方法可更改值,跟它对比的有另外一个对象是可变的,提供了set方法但是有同步性能消耗。两者之间可相互构造。
public final class MutablePerson { private String name; private String address; public MutablePerson(String name, String address) { this.name = name; this.address = address; } public MutablePerson(ImmutablePerson person) { this.name = person.getName(); this.address = person.getAddress(); } public synchronized void setPerson(String newName, String newAddress) { name = newName; address = newAddress; } public synchronized ImmutablePerson getImmutablePerson() { return new ImmutablePerson(this); } String getName() { // Called only by ImmutablePerson return name; } String getAddress() { // Called only by ImmutablePerson return address; } public synchronized String toString() { return "[ MutablePerson: " + name + ", " + address + " ]"; } }然后通过main来测试
public class Main { public static void main(String[] args) { MutablePerson mutable = new MutablePerson("start", "start"); new CrackerThread(mutable).start(); new CrackerThread(mutable).start(); new CrackerThread(mutable).start(); for (int i = 0; true; i++) { mutable.setPerson("" + i, "" + i); } } } class CrackerThread extends Thread { private final MutablePerson mutable; public CrackerThread(MutablePerson mutable) { this.mutable = mutable; } public void run() { while (true) { ImmutablePerson immutable = new ImmutablePerson(mutable); if (!immutable.getName().equals(immutable.getAddress())) { System.out.println(currentThread().getName() + " ***** BROKEN ***** " + immutable); } } } }通过这个检测程序发现对象确实是不可变的,如果去掉注释,就会有问题了。
3.条件等待-唤醒/放弃
这个模式大致归纳成3种必须元素:有循环,有条件检查,有等待唤醒或放弃。线程执行一个操作,发现条件不允许的时候,等待,直到条件满足后被唤醒;或者放弃当前操作,下次再执行。根据不满足条件的场景,一般有几类做法:条件不满足,当前线程无限等待,直到被其他线程唤醒;条件不满足,yield当前线程并继续下一次条件检查;条件不满足,sleep当前线程,再循环检查等。
4.生产消费模式
如果需要线程A向线程B单向通信,而两者的处理速度差了很多,就可以使用这种方案。一般两者之间有一个缓冲队列或者缓冲区,生产者线程往队列里塞数据,消费者线程不断的从队列中拿数据执行。这样线程之间的共享访问冲突就集中在缓冲区上。下面是个简单的生产消费者例子。
用于缓冲数据的Box
public class Box { private final String[] buffer; private int tail; // 下一个put的地方 private int head; // 下一个take的地方 private int count; public Box(int count) { this.buffer = new String[count]; this.head = 0; this.tail = 0; this.count = 0; } public synchronized void put(String data) throws InterruptedException { System.out.println(Thread.currentThread().getName() + " puts " + data); while (count >= buffer.length) { System.out.println(Thread.currentThread().getName() + " wait BEGIN"); wait(); System.out.println(Thread.currentThread().getName() + " wait END"); } buffer[tail] = data; tail = (tail + 1) % buffer.length; count++; notifyAll(); } public synchronized String take() throws InterruptedException { while (count <= 0) { System.out.println(Thread.currentThread().getName() + " wait BEGIN"); wait(); System.out.println(Thread.currentThread().getName() + " wait END"); } String data = buffer[head]; head = (head + 1) % buffer.length; count--; notifyAll(); System.out.println(Thread.currentThread().getName() + " takes " + data); return data; } }
生产者线程
public class ProducerThread extends Thread { private final Random random; private final Box box; private static int id = 0; public ProducerThread(String name, Box box, long seed) { super(name); this.box = box; this.random = new Random(seed); } public void run() { try { while (true) { Thread.sleep(random.nextInt(1000)); String data = "[ Data No." + nextId() + " by " + getName() + " ]"; box.put(data); } } catch (InterruptedException e) { } } private static synchronized int nextId() { return id++; } }消费者线程
public class ConsumerThread extends Thread { private final Random random; private final Box box; public ConsumerThread(String name, Box box, long seed) { super(name); this.box = box; this.random = new Random(seed); } public void run() { try { while (true) { String data = box.take(); Thread.sleep(random.nextInt(1000)); } } catch (InterruptedException e) { } } }
5.读写锁
它将读取和写入分开处理,对于读取并发不做共享互斥,对于读写、写写并发做互斥,适用于读取频繁,写入较少的共享互斥情况,比如读远多于写的缓存。这种情况下,比直接的共享互斥有很大的性能提高。它的主要核心就是读写锁,做读操作就获取读的锁,写操作就获取写的锁.下面是个很简单的读写锁实现:
public final class ReadWriteLock { private int readingReaders = 0; // 实际正在读取的线程数量 private int waitingWriters = 0; // 正在等待写入的线程数量 private int writingWriters = 0; // 实际正在写入的线程数量 private boolean preferWriter = true; // 写入优先的话,值为true public synchronized void readLock() throws InterruptedException { while (writingWriters > 0 || (preferWriter && waitingWriters > 0)) { wait(); } readingReaders++; } public synchronized void readUnlock() { readingReaders--; preferWriter = true; notifyAll(); } public synchronized void writeLock() throws InterruptedException { waitingWriters++; try { while (readingReaders > 0 || writingWriters > 0) { wait(); } } finally { waitingWriters--; } writingWriters++; } public synchronized void writeUnlock() { writingWriters--; preferWriter = false; notifyAll(); } }
然后是使用这个读写锁的共享数据
public class Data { private final char[] buffer; private final ReadWriteLock lock = new ReadWriteLock(); public Data(int size) { this.buffer = new char[size]; for (int i = 0; i < buffer.length; i++) { buffer[i] = '*'; } } public char[] read() throws InterruptedException { lock.readLock(); try { return doRead(); } finally { lock.readUnlock(); } } public void write(char c) throws InterruptedException { lock.writeLock(); try { doWrite(c); } finally { lock.writeUnlock(); } } private char[] doRead() { char[] newbuf = new char[buffer.length]; for (int i = 0; i < buffer.length; i++) { newbuf[i] = buffer[i]; } wating(); return newbuf; } private void doWrite(char c) { for (int i = 0; i < buffer.length; i++) { buffer[i] = c; wating(); } } private void wating() { try { Thread.sleep(50); } catch (InterruptedException e) { } } }
读写线程就分别调用read和write就可。
public class ReaderThread extends Thread { private final Data data; public ReaderThread(Data data) { this.data = data; } public void run() { try { long begin = System.currentTimeMillis(); for (int i = 0; i < 20; i++) { char[] readbuf = data.read(); System.out.println(Thread.currentThread().getName() + " reads " + String.valueOf(readbuf)); } long time = System.currentTimeMillis() - begin; System.out.println(Thread.currentThread().getName() + ": time = " + time); } catch (InterruptedException e) { } } }
public class WriterThread extends Thread { private static final Random random = new Random(); private final Data data; private final String filler; private int index = 0; public WriterThread(Data data) { this.data = data; this.filler = "hjshkjsfysfahfafaflkafsu7yyg"; } public void run() { try { while (true) { char c = nextchar(); data.write(c); Thread.sleep(random.nextInt(3000)); } } catch (InterruptedException e) { } } private char nextchar() { char c = filler.charAt(index); index++; if (index >= filler.length()) { index = 0; } return c; } }
在jdk1.5里有ReadWriteLock接口以及实现类ReentrantReadWriteLock。
发表评论
-
Java上clear Squid缓存
2011-11-29 10:26 1282实现原理: 构造TCP请求,调用Squid自带 ... -
HttpURLConnection设置代理
2011-01-21 11:00 1466设置全局代理,JVM范围内有效: System ... -
JCE provider管理的问题
2010-08-23 13:24 3249现象 两个module A和B分别采用了infosec的不同版 ... -
Jar冲突解决二
2010-08-23 11:54 1745方案思想 自定义CustomClassLoader,彻底改变c ... -
Jar冲突解决一
2010-08-23 10:46 1226目的是classpath中线性的jar排列扩展成树型排列。方案 ... -
负载均衡备注二
2010-03-30 11:41 1091Lvs或者F5的负载体系中 ... -
SelectableChannel关闭注意事项
2010-03-29 11:49 997SocketChannel和ServerSocketChann ... -
java clone备忘
2009-12-09 14:39 8191.Object clone 就是复制一个对象的复本,在Fac ... -
远程调用之RMI
2009-05-22 00:03 1047RMI(Remote Method Invocation ... -
Java修饰符归纳
2009-05-20 17:26 924final 1.final类 final类不能被继承, ... -
classloader整理
2009-05-18 18:08 1115classloader它就是用来加载Class文件到JV ... -
Java IO归纳
2009-05-17 14:39 1652Java的IO基于装饰器模式设计。根接口是InputS ... -
java实现远程桌面监控
2009-05-10 19:28 3854java里面的Robot类可以完成截图的功能,借助于这 ... -
简单的轮子--IOC容器
2009-05-10 15:48 1136产品的需求越来越多,则开发了越来越多的功能,同时也带来了 ... -
Java也可以截图
2009-05-10 14:48 1116java.awt.Robot类真的很好玩。玩Robot会给你带 ... -
jdk1.5的多线程总结二
2009-05-10 13:07 1600新的Synchronizer: Java 5.0里新加了4个协 ... -
jdk1.5的多线程总结一
2009-05-10 01:17 6034Java 5.0里新加入了三个多线程包:java.util.c ... -
JavaGC知识整理
2009-05-08 00:55 963堆 JVM管理的内存叫堆。在32Bit操作系统上有1.5G-2 ... -
ConcurrentHashMap
2009-05-08 00:54 1023实现原理 锁分离 (Lock Stripping) C ... -
Java Socket异常整理
2009-05-08 00:41 3545最近跟进性能测试,碰 ...
相关推荐
总结起来,多线程环境下的单例模式实现需要注意线程安全问题,尤其是懒汉式单例,需要采取适当的同步措施来防止多线程环境下的实例化问题。此外,对于不同场景的需求,可以选择不同的实现方式来优化性能和资源使用。
多线程基础理论, 多线程中常用API,多线程的实现方式, 线程池以及创建线程池相关API, 常见的设计模式等内容
1. 单例模式:在多线程环境中保证单例对象的唯一性,常用双检锁/双重校验锁定(DCL)和静态内部类等方式实现。 2. 生产者-消费者模式:通过阻塞队列实现生产者线程和消费者线程之间的数据交换,利用BlockingQueue...
1. **单例模式**:单例模式确保一个类只有一个实例,并提供一个全局访问点。这种模式常用于配置管理、线程池或者数据库连接池等场景。实现方式有懒汉式(线程不安全)、饿汉式(静态常量)、双重检查锁定(DCL)以及...
以上只是Java多线程设计模式的一些基本概念和常用技术,实际开发中还需要根据具体需求灵活运用,结合设计模式来解决问题。《Java 多线程设计模式》一书的源代码应该涵盖了这些知识点的具体实现,通过阅读和实践,...
### Java多线程知识点总结及企业真题解析 #### 一、知识点总结 ##### (1)多线程相关概念 1. **程序、进程和线程的区分**: - **程序**:为了完成特定的任务而编写的指令集合。它是静态的概念。 - **进程**:...
设计模式则是解决软件开发中常见问题的经验总结,它为多线程环境下的编程提供了有效的解决方案。本资源包括了详细的“Java多线程设计模式详解”PDF文档以及配套的源码,帮助开发者深入理解和应用这些模式。 首先,...
### 多线程服务器的几种常用模型 #### 1. 进程与线程 在计算机科学中,**进程**和**线程**是两个重要的概念。进程是资源分配的基本单位,而线程则是调度的基本单位。每个进程都有自己的独立地址空间,这意味着不同...
总结来说,本文档通过介绍单例模式在多核多线程环境下的性能提升方法,探讨了Java设计模式在现代大型系统应用中的优化策略,并对JDK未来的设计模式实现提出期望,为Java开发人员提供了宝贵的技术指导和参考。
### Java多线程编程环境中单例模式的实现 #### 概述 单例模式是一种常用的软件设计模式,它确保一个类只有一个实例,并提供一个全局访问点。在Java中,单例模式的应用非常广泛,特别是在资源管理、日志记录、...
在“Java多线程学习总结6”这个主题中,我们可以深入探讨Java多线程的实现、管理及优化。下面将详细阐述相关知识点。 1. **线程的创建方式** - **继承Thread类**:自定义类继承Thread类,并重写run()方法,创建...
提供了一个包含多线程编程中常用术语的词汇表,方便读者查阅。 #### 结束语 本指南详细介绍了多线程编程在iOS开发中的应用,希望读者能够从中受益,并在实际项目中有效地运用多线程技术。 #### 推荐资源 最后,...
对于一些常用的框架,如Foundation Framework、ApplicationKit和CoreData,Apple提供了线程安全的总结,指导开发者如何在这些框架内安全地使用多线程。 以上就是iOS多线程编程指南的主要知识点,详细解读了多线程...
### Java多线程单例模式详解 #### 一、单例模式概述 单例模式(Singleton Pattern)是一种常用的软件设计模式,它确保一个类仅有一个实例,并提供一个全局访问点。这种模式通常用于那些需要频繁实例化然后销毁的...
1. **多线程支持**:LsComm类库的一大亮点在于其内置的多线程机制。在串口通信中,多线程可以同时处理多个接收任务,避免了单线程模型下的阻塞问题,提高了系统的并发性能。例如,一个线程负责读取数据,另一个线程...
单例模式是一种常用的创建型设计模式,它保证一个类仅有一个实例,并提供一个全局访问点。单例模式通常用于控制资源的共享访问,例如数据库连接池、日志管理器等。 #### 实现方式 - **构造函数私有化**:防止外部...
总结来说,支持多线程AC自动机的Snort改进版是一种高效的方法,它结合了AC自动机的快速搜索能力和多线程的并发处理能力,旨在提高网络入侵检测系统的性能和响应速度,对于应对现代网络安全挑战具有重要意义。
在多线程编程中,常用的设计模式有生产者-消费者模式、读写锁模式等。 10. 多线程工具类:Java提供了诸如Timer、ScheduledExecutorService等工具类,它们能够帮助开发者更方便地进行时间安排、任务调度等操作。 ...
在C++编程中,单例模式是一种常用的软件设计模式,它保证一个类只有一个实例,并提供一个全局访问点。线程安全的单例模式在多线程环境下尤其重要,因为不正确的实现可能导致多个线程创建多个实例,这违反了单例模式...