浅谈Java多线程的同步问题 多线程的同步依靠的是对象锁机制 synchronized关键字的背后就是利用了封锁来实现对共享资源的互斥访问
下面以一个简单的实例来进行对比分析 实例要完成的工作非常简单 就是创建 个线程 每个线程都打印从 到 这 个数字 我们希望线程之间不会出现交叉乱序打印 而是顺序地打印
先来看第一段代码 这里我们在run()方法中加入了synchronized关键字 希望能对run方法进行互斥访问 但结果并不如我们希望那样 这是因为这里synchronized锁住的是this对象 即当前运行线程对象本身 代码中创建了 个线程 而每个线程都持有this对象的对象锁 这不能实现线程的同步
代码package vista;class MyThread implements java lang Runnable {private int threadId;
public MyThread(int id) {this threadId = id;}
@Overridepublic synchronized void run() {for (int i = ; i;i) {System out println( Thread ID:this threadId:i);}}}
public class ThreadDemo {/*** @param args* @throws InterruptedException*/public static void main(String[] args) throws InterruptedException {for (int i = ; i;i) {new Thread(new MyThread(i)) start();Thread sleep( );}}}
从上述代码段可以得知 要想实现线程的同步 则这些线程必须去竞争一个唯一的共享的对象锁
基于这种思想 我们将第一段代码修改如下所示 在创建启动线程之前 先创建一个线程之间竞争使用的Object对象 然后将这个Object对象的引用传递给每一个线程对象的lock成员变量 这样一来 每个线程的lock成员都指向同一个Object对象 我们在run方法中 对lock对象使用synchronzied块进行局部封锁 这样就可以让线程去竞争这个唯一的共享的对象锁 从而实现同步
代码package vista;
class MyThread implements java lang Runnable {private int threadId;private Object lock;
public MyThread(int id Object obj) {this threadId = id;this lock = obj;}
@Overridepublic void run() {synchronized (lock) {for (int i = ; i;i) {System out println( Thread ID:this threadId:i);}}}}
public class ThreadDemo {/*** @param args* @throws InterruptedException*/public static void main(String[] args) throws InterruptedException {Object obj = new Object();for (int i = ; i;i) {new Thread(new MyThread(i obj)) start();Thread sleep( );}}}
从第二段代码可知 同步的关键是多个线程对象竞争同一个共享资源即可 上面的代码中是通过外部创建共享资源 然后传递到线程中来实现 我们也可以利用类成员变量被所有类的实例所共享这一特性 因此可以将lock用静态成员对象来实现 代码如下所示
代码package vista;
class MyThread implements java lang Runnable {private int threadId;private static Object lock = new Object();
public MyThread(int id) {this threadId = id;}
@Overridepublic void run() {synchronized (lock) {for (int i = ; i;i) {System out println( Thread ID:this threadId:i);}}}}
public class ThreadDemo {/*** @param args* @throws InterruptedException*/public static void main(String[] args) throws InterruptedException {for (int i = ; i;i) {new Thread(new MyThread(i)) start();Thread sleep( );}}}
再来看第一段代码 实例方法中加入sychronized关键字封锁的是this对象本身 而在静态方法中加入sychronized关键字封锁的就是类本身 静态方法是所有类实例对象所共享的 因此线程对象在访问此静态方法时是互斥访问的 从而可以实现线程的同步 代码如下所示
代码package vista;
class MyThread implements java lang Runnable {private int threadId;
public MyThread(int id) {this threadId = id;}
@Overridepublic void run() {taskHandler(this threadId);}
private static synchronized void taskHandler(int threadId) {for (int i = ; i;i) {System out println( Thread ID:threadId:i);}}}
lishixinzhi/Article/program/Java/gj/201311/27441
java多线程解决同步问题的几种方式,原理和代码在Java中一共有四种方法支持同步java线程同步代码,其中前三个是同步方法,一个是管道方法 。管道方法不建议使用 。
wait()/notify()方法
await()/signal()方法
BlockingQueue阻塞队列方法
PipedInputStream/PipedOutputStream
阻塞队列的一个简单实现java线程同步代码:
public class BlockingQueue {
private List queue = new LinkedList();
private intlimit = 10;
public BlockingQueue(int limit){
this.limit = limit;
}
public synchronized void enqueue(Object item)throws InterruptedException{
while(this.queue.size() == this.limit) {
wait();
}
if(this.queue.size() == 0) {
notifyAll();
}
this.queue.add(item);
}
public synchronized Object dequeue()throws InterruptedException{
while(this.queue.size() == 0){
wait();
}
if(this.queue.size() == this.limit){
notifyAll();
}
return this.queue.remove(0);
}}
在enqueue和dequeue方法内部,只有队列的大小等于上限(limit)或者下限(0)时,才调用notifyAll方法 。如果队列的大小既不等于上限,也不等于下限,任何线程调用enqueue或者dequeue方法时,都不会阻塞 , 都能够正常的往队列中添加或者移除元素 。
wait()/notify()方法
生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程 。与此同时,消费者也在缓冲区消耗这些数据 。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据 。
要解决该问题 , 就必须让生产者在缓冲区满时休眠(要么干脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能被唤醒,开始往缓冲区添加数据 。同样 , 也可以让消费者在缓冲区空时进入休眠 , 等到生产者往缓冲区添加数据之后,再唤醒消费者 。
Java线程同步的方法等待唤醒机制
wait():让线程等待 。将线程存储到一个线程池中 。
notify():唤醒被等待的线程 。通常都唤醒线程池中的第一个 。让被唤醒的线程处于临时阻塞状态 。
notifyAll(): 唤醒所有的等待线程 。将线程池中的所有线程都唤醒,让它们从冻结状体转到临时阻塞状态.
这三个方法用于操作线程,可是定义在了Object类中,为什么呢?
因为,这三个方法在使用时,都需要定义在同步中,要明确这些方法所操作的线程所属于锁 。
简单说 。在A锁被wait的线程,只能被A锁的notify方法唤醒 。
所以必须要表示wait notify方法所属的锁对象 , 而锁对象可以是任意的对象 。
可以被任意的对象调用的方法肯定定义在Object类中 。
注意:等待唤醒机制,通常都用在同步中,因为需要锁的支持 。
而且必须要明确wait notify 所作用的锁对象 。
JDK1.5后的锁
在jdk1.5版本之后,
出现了一些新的特性 , 将原理的线程进行了改良 。
在java.util.concurrent.locks包中提供了一个接口Lock 。替代了synchronized 。
synchronized 。使用的是锁操作是隐式的 。
Lock接口 , 使用的锁操作是显示的 。
由两个方法来完成:
lock():获取锁 。
unlock():释放锁 。
还有一个对象,Condition.
该对象的出现替代了Object中的wait notify notifyAll这些操作监视器的方法 。
替代后的方式:awaitsignalsignalAll.
Java 线程同步几种方式(1)同步方法:
即有synchronized关键字修饰的方法 。由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法 。在调用该方法前 , 需要获得内置锁,否则就处于阻塞状态 。
(2)同步代码块
即有synchronized关键字修饰的语句块 。被该关键字修饰的语句块会自动被加上内置锁,从而实现同步
(3)使用特殊域变量(Volatile)实现线程同步
a.volatile关键字为域变量的访问提供了一种免锁机制
b.使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新
c.因此每次使用该域就要重新计算,而不是使用寄存器中的值
d.volatile不会提供任何原子操作 , 它也不能用来修饰final类型的变量
(4)使用重入锁实现线程同步
在JavaSE5.0中新增了一个java.util.concurrent包来支持同步 。ReentrantLock类是可重入、互斥、实现了Lock接口的锁,它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力 。
(5)使用局部变量实现线程同步
JAVA中线程同步方法有哪些JAVA中线程同步方法一般有以下三种:
1wait方法:
该方法属于Object的方法,wait方法的作用是使得当前调用wait方法所在部分(代码块)的线程停止执行 , 并释放当前获得的调用wait所在的代码块的锁,并在其他线程调用notify或者notifyAll方法时恢复到竞争锁状态(一旦获得锁就恢复执行) 。
调用wait方法需要注意几点:
第一点:wait被调用的时候必须在拥有锁(即synchronized修饰的)的代码块中 。
第二点:恢复执行后,从wait的下一条语句开始执行,因而wait方法总是应当在while循环中调用,以免出现恢复执行后继续执行的条件不满足却继续执行的情况 。
第三点:若wait方法参数中带时间 , 则除了notify和notifyAll被调用能激活处于wait状态(等待状态)的线程进入锁竞争外,在其他线程中interrupt它或者参数时间到了之后,该线程也将被激活到竞争状态 。
第四点:wait方法被调用的线程必须获得之前执行到wait时释放掉的锁重新获得才能够恢复执行 。
2notify方法和notifyAll方法:
notify方法通知调用了wait方法 , 但是尚未激活的一个线程进入线程调度队列(即进入锁竞争),注意不是立即执行 。并且具体是哪一个线程不能保证 。另外一点就是被唤醒的这个线程一定是在等待wait所释放的锁 。
【java线程同步代码 java线程同步机制】notifyAll方法则唤醒所有调用了wait方法,尚未激活的进程进入竞争队列 。
3 synchronized关键字:
第一点:synchronized用来标识一个普通方法时 , 表示一个线程要执行该方法,必须取得该方法所在的对象的锁 。
第二点:synchronized用来标识一个静态方法时,表示一个线程要执行该方法,必须获得该方法所在的类的类锁 。
第三点:synchronized修饰一个代码块 。类似这样:synchronized(obj) { //code.... } 。表示一个线程要执行该代码块 , 必须获得obj的锁 。这样做的目的是减小锁的粒度,保证当不同块所需的锁不冲突时不用对整个对象加锁 。利用零长度的byte数组对象做obj非常经济 。
java线程同步代码的介绍就聊到这里吧 , 感谢你花时间阅读本站内容,更多关于java线程同步机制、java线程同步代码的信息别忘了在本站进行查找喔 。
推荐阅读
- linux差异备份的命令,linux备份命令bak
- 小程序哪种类目比较好做,小程序哪些平台
- 电信为啥服务器,中国电信服务器异常
- linux如何使用命令 linux如何使用命令行
- 视频收藏什么格式最好,视频收藏什么格式最好呢
- 网线怎么设置路由器,房东的网线怎么设置路由器
- java代码做圣诞树 圣诞树编程代码
- gis设置地图描述属性,arcgis地图文档属性
- 毕业设计做什么小程序赚钱,毕业设计做小程序要上线么