当前位置:首页 » 编程语言 » java多线程同步

java多线程同步

发布时间: 2022-08-30 22:39:45

java 实现线程同步的方式有哪些

实现同步机制有两个方法:
1、同步代码块:
synchronized(同一个数据){} 同一个数据:就是N条线程同时访问一个数据。
2、同步方法:
public synchronized 数据返回类型 方法名(){}
就是使用 synchronized 来修饰某个方法,则该方法称为同步方法。对于同步方法而言,无需显示指定同步监视器,同步方法的同步监视器是 this 也就是该对象的本身(这里指的对象本身有点含糊,其实就是调用该同步方法的对象)通过使用同步方法,可非常方便的将某类变成线程安全的类,具有如下特征:
1,该类的对象可以被多个线程安全的访问。
2,每个线程调用该对象的任意方法之后,都将得到正确的结果。
3,每个线程调用该对象的任意方法之后,该对象状态依然保持合理状态。
注:synchronized关键字可以修饰方法,也可以修饰代码块,但不能修饰构造器,属性等。
实现同步机制注意以下几点: 安全性高,性能低,在多线程用。性能高,安全性低,在单线程用。
1,不要对线程安全类的所有方法都进行同步,只对那些会改变共享资源方法的进行同步。
2,如果可变类有两种运行环境,当线程环境和多线程环境则应该为该可变类提供两种版本:线程安全版本和线程不安全版本(没有同步方法和同步块)。在单线程中环境中,使用线程不安全版本以保证性能,在多线程中使用线程安全版本.

㈡ java多线程开发的同步机制有哪些

一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在 java里边就是拿到某个同步对象的锁(一个对象只有一把锁); 如果这个时候同步对象的锁被其他线程拿走了,他(这个线程)就只能等了(线程阻塞在锁池 等待队列中)。 取到锁后,他就开始执行同步代码(被synchronized修饰的代码);线程执行完同步代码后马上就把锁还给同步对象,其他在锁池中 等待的某个线程就可以拿到锁执行同步代码了。这样就保证了同步代码在统一时刻只有一个线程在执行。
众所周知,在Java多线程编程中,一个非常重要的方面就是线程的同步问题。
关于线程的同步,一般有以下解决方法:

1. 在需要同步的方法的方法签名中加入synchronized关键字。

2. 使用synchronized块对需要进行同步的代码段进行同步。

3. 使用JDK 5中提供的java.util.concurrent.lock包中的Lock对象。

另外,为了解决多个线程对同一变量进行访问时可能发生的安全性问题,我们不仅可以采用同步机制,更可以通过JDK 1.2中加入的ThreadLocal来保证更好的并发性。

㈢ java多线程有几种实现方法线程之间如何同步

一、为什么要线程同步

因为当我们有多个线程要同时访问一个变量或对象时,如果这些线程中既有读又有写操作时,就会导致变量值或对象的状态出现混乱,从而导致程序异常。举个例子,如果一个银行账户同时被两个线程操作,一个取100块,一个存钱100块。假设账户原本有0块,如果取钱线程和存钱线程同时发生,会出现什么结果呢?取钱不成功,账户余额是100.取钱成功了,账户余额是0.那到底是哪个呢?很难说清楚。因此多线程同步就是要解决这个问题。

二、不同步时的代码

Bank.Java

packagethreadTest;

/**
*@authorww
*
*/
publicclassBank{

privateintcount=0;//账户余额

//存钱
publicvoidaddMoney(intmoney){
count+=money;
System.out.println(System.currentTimeMillis()+"存进:"+money);
}

//取钱
publicvoidsubMoney(intmoney){
if(count-money<0){
System.out.println("余额不足");
return;
}
count-=money;
System.out.println(+System.currentTimeMillis()+"取出:"+money);
}

//查询
publicvoidlookMoney(){
System.out.println("账户余额:"+count);
}
}

SyncThreadTest.java


packagethreadTest;


publicclassSyncThreadTest{

publicstaticvoidmain(Stringargs[]){
finalBankbank=newBank();

Threadtadd=newThread(newRunnable(){

@Override
publicvoidrun(){
//TODOAuto-generatedmethodstub
while(true){
try{
Thread.sleep(1000);
}catch(InterruptedExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
bank.addMoney(100);
bank.lookMoney();
System.out.println(" ");

}
}
});

Threadtsub=newThread(newRunnable(){

@Override
publicvoidrun(){
//TODOAuto-generatedmethodstub
while(true){
bank.subMoney(100);
bank.lookMoney();
System.out.println(" ");
try{
Thread.sleep(1000);
}catch(InterruptedExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
}
}
});
tsub.start();

tadd.start();
}



}

余额不足
账户余额:0


余额不足
账户余额:100


1441790503354存进:100
账户余额:100


1441790504354存进:100
账户余额:100


1441790504354取出:100
账户余额:100


1441790505355存进:100
账户余额:100


1441790505355取出:100
账户余额:100

三、使用同步时的代码

(1)同步方法:


即有synchronized关键字修饰的方法。由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。

修改后的Bank.java


packagethreadTest;

/**
*@authorww
*
*/
publicclassBank{

privateintcount=0;//账户余额

//存钱
(intmoney){
count+=money;
System.out.println(System.currentTimeMillis()+"存进:"+money);
}

//取钱
(intmoney){
if(count-money<0){
System.out.println("余额不足");
return;
}
count-=money;
System.out.println(+System.currentTimeMillis()+"取出:"+money);
}

//查询
publicvoidlookMoney(){
System.out.println("账户余额:"+count);
}
}

再看看运行结果:


余额不足
账户余额:0


余额不足
账户余额:0


1441790837380存进:100
账户余额:100


1441790838380取出:100
账户余额:0
1441790838380存进:100
账户余额:100
1441790839381取出:100
账户余额:0

瞬间感觉可以理解了吧。


注: synchronized关键字也可以修饰静态方法,此时如果调用该静态方法,将会锁住整个类

(2)同步代码块

即有synchronized关键字修饰的语句块。被该关键字修饰的语句块会自动被加上内置锁,从而实现同步

Bank.java代码如下:


packagethreadTest;

/**
*@authorww
*
*/
publicclassBank{

privateintcount=0;//账户余额

//存钱
publicvoidaddMoney(intmoney){

synchronized(this){
count+=money;
}
System.out.println(System.currentTimeMillis()+"存进:"+money);
}

//取钱
publicvoidsubMoney(intmoney){

synchronized(this){
if(count-money<0){
System.out.println("余额不足");
return;
}
count-=money;
}
System.out.println(+System.currentTimeMillis()+"取出:"+money);
}

//查询
publicvoidlookMoney(){
System.out.println("账户余额:"+count);
}
}

运行结果如下:

余额不足
账户余额:0


1441791806699存进:100
账户余额:100


1441791806700取出:100
账户余额:0


1441791807699存进:100
账户余额:100

效果和方法一差不多。

注:同步是一种高开销的操作,因此应该尽量减少同步的内容。通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。

(3)使用特殊域变量(volatile)实现线程同步


a.volatile关键字为域变量的访问提供了一种免锁机制
b.使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新
c.因此每次使用该域就要重新计算,而不是使用寄存器中的值
d.volatile不会提供任何原子操作,它也不能用来修饰final类型的变量

Bank.java代码如下:


packagethreadTest;

/**
*@authorww
*
*/
publicclassBank{

privatevolatileintcount=0;//账户余额

//存钱
publicvoidaddMoney(intmoney){

count+=money;
System.out.println(System.currentTimeMillis()+"存进:"+money);
}

//取钱
publicvoidsubMoney(intmoney){

if(count-money<0){
System.out.println("余额不足");
return;
}
count-=money;
System.out.println(+System.currentTimeMillis()+"取出:"+money);
}

//查询
publicvoidlookMoney(){
System.out.println("账户余额:"+count);
}
}

运行效果怎样呢?


余额不足
账户余额:0


余额不足
账户余额:100


1441792010959存进:100
账户余额:100


1441792011960取出:100
账户余额:0


1441792011961存进:100
账户余额:100

是不是又看不懂了,又乱了。这是为什么呢?就是因为volatile不能保证原子操作导致的,因此volatile不能代替synchronized。此外volatile会组织编译器对代码优化,因此能不使用它就不适用它吧。它的原理是每次要线程要访问volatile修饰的变量时都是从内存中读取,而不是存缓存当中读取,因此每个线程访问到的变量值都是一样的。这样就保证了同步。


(4)使用重入锁实现线程同步

在JavaSE5.0中新增了一个java.util.concurrent包来支持同步。ReentrantLock类是可重入、互斥、实现了Lock接口的锁,它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力。
ReenreantLock类的常用方法有:
ReentrantLock() : 创建一个ReentrantLock实例
lock() : 获得锁
unlock() : 释放锁
注:ReentrantLock()还有一个可以创建公平锁的构造方法,但由于能大幅度降低程序运行效率,不推荐使用
Bank.java代码修改如下:


packagethreadTest;

importjava.util.concurrent.locks.Lock;
importjava.util.concurrent.locks.ReentrantLock;

/**
*@authorww
*
*/
publicclassBank{

privateintcount=0;//账户余额

//需要声明这个锁
privateLocklock=newReentrantLock();

//存钱
publicvoidaddMoney(intmoney){
lock.lock();//上锁
try{
count+=money;
System.out.println(System.currentTimeMillis()+"存进:"+money);

}finally{
lock.unlock();//解锁
}
}

//取钱
publicvoidsubMoney(intmoney){
lock.lock();
try{

if(count-money<0){
System.out.println("余额不足");
return;
}
count-=money;
System.out.println(+System.currentTimeMillis()+"取出:"+money);
}finally{
lock.unlock();
}
}

//查询
publicvoidlookMoney(){
System.out.println("账户余额:"+count);
}
}

运行效果怎么样呢?


余额不足
账户余额:0


余额不足
账户余额:0


1441792891934存进:100
账户余额:100


1441792892935存进:100
账户余额:200


1441792892954取出:100
账户余额:100

效果和前两种方法差不多。


如果synchronized关键字能满足用户的需求,就用synchronized,因为它能简化代码 。如果需要更高级的功能,就用ReentrantLock类,此时要注意及时释放锁,否则会出现死锁,通常在finally代码释放锁

(5)使用局部变量实现线程同步


Bank.java代码如下:


packagethreadTest;


/**
*@authorww
*
*/
publicclassBank{

privatestaticThreadLocal<Integer>count=newThreadLocal<Integer>(){

@Override
protectedIntegerinitialValue(){
//TODOAuto-generatedmethodstub
return0;
}

};


//存钱
publicvoidaddMoney(intmoney){
count.set(count.get()+money);
System.out.println(System.currentTimeMillis()+"存进:"+money);

}

//取钱
publicvoidsubMoney(intmoney){
if(count.get()-money<0){
System.out.println("余额不足");
return;
}
count.set(count.get()-money);
System.out.println(+System.currentTimeMillis()+"取出:"+money);
}

//查询
publicvoidlookMoney(){
System.out.println("账户余额:"+count.get());
}
}

运行效果:


余额不足
账户余额:0


余额不足
账户余额:0


1441794247939存进:100
账户余额:100


余额不足
1441794248940存进:100
账户余额:0


账户余额:200


余额不足
账户余额:0


1441794249941存进:100
账户余额:300

看了运行效果,一开始一头雾水,怎么只让存,不让取啊?看看ThreadLocal的原理:


如果使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。现在明白了吧,原来每个线程运行的都是一个副本,也就是说存钱和取钱是两个账户,知识名字相同而已。所以就会发生上面的效果。


ThreadLocal与同步机制
a.ThreadLocal与同步机制都是为了解决多线程中相同变量的访问冲突问题

b.前者采用以"空间换时间"的方法,后者采用以"时间换空间"的方式

㈣ JAVA 多线程同步概念

1
多线程的优势在于并发
如果有多线程需要共享某个资源
在运行到这里的时候和单线程的效率就相同了
但在运行到这之前都是处于并发的状态
瓶颈就在于锁了
但没有访问同一个资源时就是并发的效率就高了
2
多线程同时工作就算多线程了吧
伪多线程...你是想说超线程么

㈤ java.关于线程同步的几个知识点

  1. 扩展:

    为何要使用同步?

因为java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查),所以将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用,从而保证了该变量的唯一性和准确性。

2.同步方法
有synchronized关键字修饰的方法。

由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。

列:

public synchronized void save(){}

3.同步代码块

有synchronized关键字修饰的语句块。

被该关键字修饰的语句块会自动被加上内置锁,从而实现同步



列: synchronized(object){ }

4.用特殊域变量(volatile)实现线程同步

5.用重入锁实现线程同步

6.用局部变量实现线程同步

㈥ 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这些操作监视器的方法。
替代后的方式:await signal signalAll.

㈦ JAVA中线程同步方法有哪些

JAVA中线程同步方法一般有以下三种:
1 wait方法:
该方法属于Object的方法,wait方法的作用是使得当前调用wait方法所在部分(代码块)的线程停止执行,并释放当前获得的调用wait所在的代码块的锁,并在其他线程调用notify或者notifyAll方法时恢复到竞争锁状态(一旦获得锁就恢复执行)。
调用wait方法需要注意几点:
第一点:wait被调用的时候必须在拥有锁(即synchronized修饰的)的代码块中。
第二点:恢复执行后,从wait的下一条语句开始执行,因而wait方法总是应当在while循环中调用,以免出现恢复执行后继续执行的条件不满足却继续执行的情况。
第三点:若wait方法参数中带时间,则除了notify和notifyAll被调用能激活处于wait状态(等待状态)的线程进入锁竞争外,在其他线程中interrupt它或者参数时间到了之后,该线程也将被激活到竞争状态。
第四点:wait方法被调用的线程必须获得之前执行到wait时释放掉的锁重新获得才能够恢复执行。
2 notify方法和notifyAll方法:
notify方法通知调用了wait方法,但是尚未激活的一个线程进入线程调度队列(即进入锁竞争),注意不是立即执行。并且具体是哪一个线程不能保证。另外一点就是被唤醒的这个线程一定是在等待wait所释放的锁。
notifyAll方法则唤醒所有调用了wait方法,尚未激活的进程进入竞争队列。
3 synchronized关键字:
第一点:synchronized用来标识一个普通方法时,表示一个线程要执行该方法,必须取得该方法所在的对象的锁。
第二点:synchronized用来标识一个静态方法时,表示一个线程要执行该方法,必须获得该方法所在的类的类锁。
第三点:synchronized修饰一个代码块。类似这样:synchronized(obj) { //code.... }。表示一个线程要执行该代码块,必须获得obj的锁。这样做的目的是减小锁的粒度,保证当不同块所需的锁不冲突时不用对整个对象加锁。利用零长度的byte数组对象做obj非常经济。

㈧ java多线程解决同步问题的几种方式,原理和代码

在Java中一共有四种方法支持同步,其中前三个是同步方法,一个是管道方法。管道方法不建议使用。

  • wait()/notify()方法

  • await()/signal()方法

  • BlockingQueue阻塞队列方法

  • PipedInputStream/PipedOutputStream

  • 阻塞队列的一个简单实现:

  • public class BlockingQueue {

  • private List queue = new LinkedList();

  • private int limit = 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()方法

  • 生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。

    要解决该问题,就必须让生产者在缓冲区满时休眠(要么干脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能被唤醒,开始往缓冲区添加数据。同样,也可以让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据之后,再唤醒消费者。

热点内容
php批量查询 发布:2025-01-16 10:43:38 浏览:917
适合搭建代理服务器的云 发布:2025-01-16 10:42:49 浏览:428
我的世界手机版服务器怎么注册 发布:2025-01-16 10:41:30 浏览:614
小米云电视服务器 发布:2025-01-16 10:37:03 浏览:350
php开源wiki 发布:2025-01-16 10:27:19 浏览:189
sql加字段备注 发布:2025-01-16 10:21:49 浏览:565
线割编程教程 发布:2025-01-16 10:21:03 浏览:18
谷歌浏览器缓存删除 发布:2025-01-16 10:19:36 浏览:414
数据库txt 发布:2025-01-16 10:16:41 浏览:457
小米账号王者传奇脚本挂机 发布:2025-01-16 10:07:25 浏览:917