❶ 如何在学习java过程中实现线程之间的通信
在java中,每个对象都有两个池,锁池(monitor)和等待池(waitset),每个对象又都有wait、notify、notifyAll方法,使用它们可以实现线程之间的通信,只是平时用的较少.
wait(): 使当前线程处于等待状态,直到另外的线程调用notify或notifyAll将它唤醒
notify(): 唤醒该对象监听的其中一个线程(规则取决于JVM厂商,FILO,FIFO,随机…)
notifyAll(): 唤醒该对象监听的所有线程
锁池: 假设T1线程已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用该对象的synchronized方法(或者synchronized块),由于这些线程在进入对象的synchronized方法之前都需要先获得该对象的锁的拥有权,但是该对象的锁目前正被T1线程拥有,所以这些线程就进入了该对象的锁池中.
等待池: 假设T1线程调用了某个对象的wait()方法,T1线程就会释放该对象的锁(因为wait()方法必须出现在synchronized中,这样自然在执行wait()方法之前T1线程就已经拥有了该对象的锁),同时T1线程进入到了该对象的等待池中.如果有其它线程调用了相同对象的notifyAll()方法,那么处于该对象的等待池中的线程就会全部进入该对象的锁池中,从新争夺锁的拥有权.如果另外的一个线程调用了相同对象的notify()方法,那么仅仅有一个处于该对象的等待池中的线程(随机)会进入该对象的锁池.
java实现线程间通信的四种方式
1、synchronized同步:这种方式,本质上就是“共享内存”式的通信。多个线程需要访问同一个共享变量,谁拿到了锁(获得了访问权限),谁就可以执行。
2、while轮询:其实就是多线程同时执行,会牺牲部分CPU性能。
3、wait/notify机制
4、管道通信:管道流主要用来实现两个线程之间的二进制数据的传播
❷ java阻塞队列 线程同步合作
Queue接口与List Set同一级别 都是继承了Collection接口 LinkedList实现了Queue接口 Queue接口窄化了对LinkedList的方法的访问权限(即在方法中的参数类型如果是Queue时 就完全只能访问Queue接口所定义的方法了 而不能直接访问 LinkedList的非Queue的方法) 以使得只有恰当的方法才可以使用 BlockingQueue 继承了Queue接口
队列是一种数据结构.它有两个基本操作 在队列尾部加人一个元素 和从队列头部移除一个元素就是说 队列以一种先进先出的方式管理数据 如果你试图向一个已经满了的阻塞队列中添加一个元素或者是从一个空的阻塞队列中移除一个元索 将导致线程阻塞.在多线程进行合作时 阻塞队列是很有用的工具 工作者线程可以定期地把中间结果存到阻塞队列中而其他工作者线线程把中间结果取出并在将来修改它们 队列会自动平衡负载 如果第一个线程集运行得比第二个慢 则第二个线程集在等待结果时就会阻塞 如果第一个线程集运行得快 那么它将等待第二个线程集赶上来 下表显示了jdk 中的阻塞队列的操作
add 增加一个元索 如果队列已春慎桥满 则抛出一个IIIegaISlabEepeplian异常
remove 移除并返回队列头部的元素 如果队列为空 则抛出一个NoSuchElementException异常
element 返回队列头部的元素 如果队列为空 则抛出一个NoSuchElementException异常
offer 添加一个元素并返回true 如果队列已满 则返回false
poll 移除并返问队列头部的元素 如果队列扒猛为空 则返回null
peek 返回队列头部的元素 如果队列为空 则返回null
put 添加一个元素 如果队列满 则阻塞
take 移除并返回队列头部的元素 如果队列为空 则阻塞
remove element offer poll peek 其实是属于Queue接口
阻塞队列的操作可以根据它们的响应方式分为以下三类 aad removee和element操作在你试图为一个已满的队列增加元素或从空队列取得元素时抛出异常 当然 在多线程程序中 队列在任何时间都可能变成满的或空的 所以你可能想使用offer poll peek方法 这些方法在无法完成任务时只是给出一个出错示而不会抛出异常
注意 poll和peek方法出错进返回null 因此 向队列中插入null值是不合法的
还有带超时的offer和poll方法变种 例如 下面的调用
boolean success = q offer(x TimeUnit MILLISECONDS);
尝试在 毫秒内向队列尾部插入一个元素 如果成功 立即返回true 否则 当到达超时进 返回false 同样地 调用
Object head = q poll( TimeUnit MILLISECONDS);
如果在 毫秒内成功地移除了队列头元素 则立即返回头元素 否则在到达超时时 返回null
最后 我们有阻塞操作put和take put方法在队列满时阻塞 take方法在队列空时阻塞
ncurrent包提供了阻塞队列的 个变种 默认情况下 LinkedBlockingQueue的容量是没有上限的(说的不准确 在不指定时容量为Integer MAX_VALUE 不要然的话在put时怎么会受阻呢) 但是也可以选择指定其最大容量 它是基孝仔于链表的队列 此队列按 FIFO(先进先出)排序元素
ArrayBlockingQueue在构造时需要指定容量 并可以选择是否需要公平性 如果公平参数被设置true 等待时间最长的线程会优先得到处理(其实就是通过将ReentrantLock设置为true来达到这种公平性的 即等待时间最长的线程会先操作) 通常 公平性会使你在性能上付出代价 只有在的确非常需要的时候再使用它 它是基于数组的阻塞循环队列 此队列按 FIFO(先进先出)原则对元素进行排序
PriorityBlockingQueue是一个带优先级的队列 而不是先进先出队列 元素按优先级顺序被移除 该队列也没有上限(看了一下源码 PriorityBlockingQueue是对PriorityQueue的再次包装 是基于堆数据结构的 而PriorityQueue是没有容量限制的 与ArrayList一样 所以在优先阻塞队列上put时是不会受阻的 虽然此队列逻辑上是无界的 但是由于资源被耗尽 所以试图执行添加操作可能会导致 OutOfMemoryError) 但是如果队列为空 那么取元素的操作take就会阻塞 所以它的检索操作take是受阻的 另外 往入该队列中的元素要具有比较能力
最后 DelayQueue(基于PriorityQueue来实现的)是一个存放Delayed 元素的无界阻塞队列 只有在延迟期满时才能从中提取元素 该队列的头部是延迟期满后保存时间最长的 Delayed 元素 如果延迟都还没有期满 则队列没有头部 并且poll将返回null 当一个元素的 getDelay(TimeUnit NANOSECONDS) 方法返回一个小于或等于零的值时 则出现期满 poll就以移除这个元素了 此队列不允许使用 null 元素 下面是延迟接口
Java代码
public interface Delayed extends Comparable<Delayed> {
long getDelay(TimeUnit unit);
}
public interface Delayed extends Comparable<Delayed> {
long getDelay(TimeUnit unit);
}
放入DelayQueue的元素还将要实现pareTo方法 DelayQueue使用这个来为元素排序
下面的实例展示了如何使用阻塞队列来控制线程集 程序在一个目录及它的所有子目录下搜索所有文件 打印出包含指定关键字的文件列表 从下面实例可以看出 使用阻塞队列两个显着的好处就是 多线程操作共同的队列时不需要额外的同步 另外就是队列会自动平衡负载 即那边(生产与消费两边)处理快了就会被阻塞掉 从而减少两边的处理速度差距 下面是具体实现
Java代码
public class BlockingQueueTest {
public static void main(String[] args) {
Scanner in = new Scanner(System in);
System out print( Enter base directory (e g /usr/local/jdk /src): );
String directory = in nextLine();
System out print( Enter keyword (e g volatile): );
String keyword = in nextLine();
final int FILE_QUEUE_SIZE = ;// 阻塞队列大小
final int SEARCH_THREADS = ;// 关键字搜索线程个数
// 基于ArrayBlockingQueue的阻塞队列
BlockingQueue<File> queue = new ArrayBlockingQueue<File>(
FILE_QUEUE_SIZE);
//只启动一个线程来搜索目录
FileEnumerationTask enumerator = new FileEnumerationTask(queue
new File(directory));
new Thread(enumerator) start();
//启动 个线程用来在文件中搜索指定的关键字
for (int i = ; i <= SEARCH_THREADS; i++)
new Thread(new SearchTask(queue keyword)) start();
}
}
class FileEnumerationTask implements Runnable {
//哑元文件对象 放在阻塞队列最后 用来标示文件已被遍历完
public static File DUMMY = new File( );
private BlockingQueue<File> queue;
private File startingDirectory;
public FileEnumerationTask(BlockingQueue<File> queue File startingDirectory) {
this queue = queue;
this startingDirectory = startingDirectory;
}
public void run() {
try {
enumerate(startingDirectory);
queue put(DUMMY);//执行到这里说明指定的目录下文件已被遍历完
} catch (InterruptedException e) {
}
}
// 将指定目录下的所有文件以File对象的形式放入阻塞队列中
public void enumerate(File directory) throws InterruptedException {
File[] files = directory listFiles();
for (File file : files) {
if (file isDirectory())
enumerate(file);
else
//将元素放入队尾 如果队列满 则阻塞
queue put(file);
}
}
}
class SearchTask implements Runnable {
private BlockingQueue<File> queue;
private String keyword;
public SearchTask(BlockingQueue<File> queue String keyword) {
this queue = queue;
this keyword = keyword;
}
public void run() {
try {
boolean done = false;
while (!done) {
//取出队首元素 如果队列为空 则阻塞
File file = queue take();
if (file == FileEnumerationTask DUMMY) {
//取出来后重新放入 好让其他线程读到它时也很快的结束
queue put(file);
done = true;
} else
search(file);
}
} catch (IOException e) {
e printStackTrace();
} catch (InterruptedException e) {
}
}
public void search(File file) throws IOException {
Scanner in = new Scanner(new FileInputStream(file));
int lineNumber = ;
while (in hasNextLine()) {
lineNumber++;
String line = in nextLine();
if (ntains(keyword))
System out printf( %s:%d:%s%n file getPath() lineNumber
line);
}
in close();
}
lishixin/Article/program/Java/hx/201311/26657
❸ Java中线程间怎么通讯什么叫僵死线程
JAVA直接通信 一般有个servicer端 一个client端,servicer启动后,client与servicer连接,你可以试用UDP协议或者TCP/IP协议在多线程中,线程会sleep,当程序停止时,线程仍然处于sleep中,就出现了僵死线程
❹ java 进程间通讯的有几种方法
进程间通信的方法主要有以下几种:
(1)管道(Pipe):管道可用于具有亲缘关系进程间的通信,允许一个进程和另一个与它有共同祖先的进程之间进行通信。
(2)命名管道(named pipe):命名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关 系进程间的通信。命名管道在文件系统中有对应的文件名。命名管道通过命令mkfifo或系统调用mkfifo来创建。
(3)信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送 信号给进程本身;linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数)。
(4)消息(Message)队列:消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺
(5)共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。
(6)内存映射(mapped memory):内存映射允许任何多个进程间通信,每一个使用该机制的进程通过把一个共享的文件映射到自己的进程地址空间来实现它。
(7)信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。
(8)套接口(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字。
而在java中我们实现多线程间通信则主要采用"共享变量"和"管道流"这两种方法
方法一 通过访问共享变量的方式(注:需要处理同步问题)
方法二 通过管道流
其中方法一有两种实现方法,即
方法一a)通过内部类实现线程的共享变量
代码如下:
public class Innersharethread {
public static void main(String[] args) {
Mythread mythread = new Mythread();
mythread.getThread().start();
mythread.getThread().start();
mythread.getThread().start();
mythread.getThread().start();
}
}
class Mythread {
int index = 0;
private class InnerThread extends Thread {
public synchronized void run() {
while (true) {
System.out.println(Thread.currentThread().getName()
+ "is running and index is " + index++);
}
}
}
public Thread getThread() {
return new InnerThread();
}
}
/**
* 通过内部类实现线程的共享变量
*
*/
public class Innersharethread {
public static void main(String[] args) {
Mythread mythread = new Mythread();
mythread.getThread().start();
mythread.getThread().start();
mythread.getThread().start();
mythread.getThread().start();
}
}
class Mythread {
int index = 0;
private class InnerThread extends Thread {
public synchronized void run() {
while (true) {
System.out.println(Thread.currentThread().getName()
+ "is running and index is " + index++);
}
}
}
public Thread getThread() {
return new InnerThread();
}
}
b)通过实现Runnable接口实现线程的共享变量
代码如下:
public class Interfacaesharethread {
public static void main(String[] args) {
Mythread mythread = new Mythread();
new Thread(mythread).start();
new Thread(mythread).start();
new Thread(mythread).start();
new Thread(mythread).start();
}
}
/* 实现Runnable接口 */
class Mythread implements Runnable {
int index = 0;
public synchronized void run() {
while (true)
System.out.println(Thread.currentThread().getName() + "is running and
the index is " + index++);
}
}
/**
* 通过实现Runnable接口实现线程的共享变量
*/
public class Interfacaesharethread {
public static void main(String[] args) {
Mythread mythread = new Mythread();
new Thread(mythread).start();
new Thread(mythread).start();
new Thread(mythread).start();
new Thread(mythread).start();
}
}
/* 实现Runnable接口 */
class Mythread implements Runnable {
int index = 0;
public synchronized void run() {
while (true)
System.out.println(Thread.currentThread().getName() + "is running and
the index is " + index++);
}
}
方法二(通过管道流):
代码如下:
public class CommunicateWhitPiping {
public static void main(String[] args) {
/**
* 创建管道输出流
*/
PipedOutputStream pos = new PipedOutputStream();
/**
* 创建管道输入流
*/
PipedInputStream pis = new PipedInputStream();
try {
/**
* 将管道输入流与输出流连接 此过程也可通过重载的构造函数来实现
*/
pos.connect(pis);
} catch (IOException e) {
e.printStackTrace();
}
/**
* 创建生产者线程
*/
Procer p = new Procer(pos);
/**
* 创建消费者线程
*/
Consumer c = new Consumer(pis);
/**
* 启动线程
*/
p.start();
c.start();
}
}
/**
* 生产者线程(与一个管道输入流相关联)
*
*/
class Procer extends Thread {
private PipedOutputStream pos;
public Procer(PipedOutputStream pos) {
this.pos = pos;
}
public void run() {
int i = 8;
try {
pos.write(i);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 消费者线程(与一个管道输入流相关联)
*
*/
class Consumer extends Thread {
private PipedInputStream pis;
public Consumer(PipedInputStream pis) {
this.pis = pis;
}
public void run() {
try {
System.out.println(pis.read());
} catch (IOException e) {
e.printStackTrace();
}
}
}
❺ java中同步有几种方式啊
1。同步代码块:
synchronized(同一个数据){} 同一个数据:就是N条线程同时访问一个数据。
2。
同步方法:
public synchronized 数据返回类型 方法名(){}
就
是使用 synchronized 来修饰某个方法,则该方法称为同步方法。对于同步方法而言,无需显示指定同步监视器,同步方法的同步监视器是
this
也就是该对象的本身(这里指的对象本身有点含糊,其实就是调用该同步方法的对象)通过使用同步方法,可非常方便的将某类变成线程安全的类,具有如下特征:
1,该类的对象可以被多个线程安全的访问。
2,每个线程调用该对象的任意方法之后,都将得到正确的结果。
3,每个线程调用该对象的任意方法之后,该对象状态依然保持合理状态。
注:synchronized关键字可以修饰方法,也可以修饰代码块,但不能修饰构造器,属性等。
实现同步机制注意以下几点: 安全性高,性能低,在多线程用。性能高,安全性低,在单线程用。
1,不要对线程安全类的所有方法都进行同步,只对那些会改变共享资源方法的进行同步。
2,如果可变类有两种运行环境,当线程环境和多线程环境则应该为该可变类提供两种版本:线程安全版本和线程不安全版本(没有同步方法和同步块)。在单线程中环境中,使用线程不安全版本以保证性能,在多线程中使用线程安全版本.
线程通讯:
为什么要使用线程通讯?
当
使用synchronized
来修饰某个共享资源时(分同步代码块和同步方法两种情况),当某个线程获得共享资源的锁后就可以执行相应的代码段,直到该线程运行完该代码段后才释放对该
共享资源的锁,让其他线程有机会执行对该共享资源的修改。当某个线程占有某个共享资源的锁时,如果另外一个线程也想获得这把锁运行就需要使用wait()
和notify()/notifyAll()方法来进行线程通讯了。
Java.lang.object 里的三个方法wait() notify() notifyAll()
wait方法导致当前线程等待,直到其他线程调用同步监视器的notify方法或notifyAll方法来唤醒该线程。
wait(mills)方法
都是等待指定时间后自动苏醒,调用wait方法的当前线程会释放该同步监视器的锁定,可以不用notify或notifyAll方法把它唤醒。
notify()
唤醒在同步监视器上等待的单个线程,如果所有线程都在同步监视器上等待,则会选择唤醒其中一个线程,选择是任意性的,只有当前线程放弃对该同步监视器的锁定后,也就是使用wait方法后,才可以执行被唤醒的线程。
notifyAll()方法
唤醒在同步监视器上等待的所有的线程。只用当前线程放弃对该同步监视器的锁定后,才可以执行被唤醒的线程
❻ java 高并发 都有哪些技术
我用的JAVA NIO,一般常用的高并发IO框架,也是用的这个做扩展。
Java NIO是在jdk1.4开始使用的,它既可以说成“新I/O”,也可以说成非阻塞式I/O。下面是java NIO的工作原理:
1. 由一个专门的线程来处理所有的 IO 事件,并负责分发。
2. 事件驱动机制:事件到的时候触发,而不是同步的去监视事件。
3. 线程通讯:线程之间通过 wait,notify 等方式通讯。保证每次上下文切换都是有意义的。减少无谓的线程切换。
Java NIO的服务端只需启动一个专门的线程来处理所有的 IO 事件,这种通信模型是怎么实现的呢?呵呵,我们一起来探究它的奥秘吧。java NIO采用了双向通道(channel)进行数据传输,而不是单向的流(stream),在通道上可以注册我们感兴趣的事件。一共有以下四种事件:
事件名 对应值
服务端接收客户端连接事件 SelectionKey.OP_ACCEPT(16)
客户端连接服务端事件 SelectionKey.OP_CONNECT(8)
读事件 SelectionKey.OP_READ(1)
写事件 SelectionKey.OP_WRITE(4)
服务端和客户端各自维护一个管理通道的对象,我们称之为selector,该对象能检测一个或多个通道 (channel) 上的事件。我们以服务端为例,如果服务端的selector上注册了读事件,某时刻客户端给服务端发送了一些数据,阻塞I/O这时会调用read()方法阻塞地读取数据,而NIO的服务端会在selector中添加一个读事件。服务端的处理线程会轮询地访问selector,如果访问selector时发现有感兴趣的事件到达,则处理这些事件,如果没有感兴趣的事件到达,则处理线程会一直阻塞直到感兴趣的事件到达为止。下面是我理解的java NIO的通信模型示意图:
❼ 在用java开发程序中什么时候应用到线程
一般 需要独立运行某个东西的时候会用到线程
我用过的就是 端口监听的时候
比如说 你的程序在200端口上 设置另一个监听,当有数据通过这个端口传输的时候 就会被监听程序所获取 但是如何保证 实时监听?这个就需要有个线程独立的 来执行这个工作 保持监听的状态。
再比如说 你需要做一个任务处理的程序 当数据库中有新任务时候就进行处理/或者XX路径下出现新的文件 就进行处理,这个时候 也需要用到线程 来实时扫描数据库或者是文件路径,来保证任务能够得到及时的处理