精华内容
下载资源
问答
  • Java 多线程同步的五种方法
    Java 多线程同步的五种方法


    一、引言


    前几天面试,被大师虐残了,好多基础知识必须得重新拿起来啊。闲话不多说,进入正题。


    二、为什么要线程同步


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


    三、不同步时的代码


    Bank.java

    package threadTest;  
    
    
    /** 
     * @author ww 
     * 
     */  
    public class Bank {  
    
    
        private int count =0;//账户余额  
    
    
        //存钱  
        public  void addMoney(int money){  
            count +=money;  
            System.out.println(System.currentTimeMillis()+"存进:"+money);  
        }  
    
    
        //取钱  
        public  void subMoney(int money){  
            if(count-money < 0){  
                System.out.println("余额不足");  
                return;  
            }  
            count -=money;  
            System.out.println(+System.currentTimeMillis()+"取出:"+money);  
        }  
    
    
        //查询  
        public void lookMoney(){  
            System.out.println("账户余额:"+count);  
        }  
    }
    SyncThreadTest.java
    
    
    package threadTest;  
    
    
    public class SyncThreadTest {  
    
    
        public static void main(String args[]){  
            final Bank bank=new Bank();  
    
    
            Thread tadd=new Thread(new Runnable() {  
    
    
                @Override  
                public void run() {  
                    // TODO Auto-generated method stub  
                    while(true){  
                        try {  
                            Thread.sleep(1000);  
                        } catch (InterruptedException e) {  
                            // TODO Auto-generated catch block  
                            e.printStackTrace();  
                        }  
                        bank.addMoney(100);  
                        bank.lookMoney();  
                        System.out.println("\n");  
    
    
                    }  
                }  
            });  
    
    
            Thread tsub = new Thread(new Runnable() {  
    
    
                @Override  
                public void run() {  
                    // TODO Auto-generated method stub  
                    while(true){  
                        bank.subMoney(100);  
                        bank.lookMoney();  
                        System.out.println("\n");  
                        try {  
                            Thread.sleep(1000);  
                        } catch (InterruptedException e) {  
                            // TODO Auto-generated catch block  
                            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

    package threadTest;  
    
    
    /** 
     * @author ww 
     * 
     */  
    public class Bank {  
    
    
        private int count =0;//账户余额  
    
    
        //存钱  
        public  synchronized void addMoney(int money){  
            count +=money;  
            System.out.println(System.currentTimeMillis()+"存进:"+money);  
        }  
    
    
        //取钱  
        public  synchronized void subMoney(int money){  
            if(count-money < 0){  
                System.out.println("余额不足");  
                return;  
            }  
            count -=money;  
            System.out.println(+System.currentTimeMillis()+"取出:"+money);  
        }  
    
    
        //查询  
        public void lookMoney(){  
            System.out.println("账户余额:"+count);  
        }  
    }



    再看看运行结果:


    余额不足  
    账户余额:0  


    余额不足  
    账户余额:0  


    1441790837380存进:100  
    账户余额:100  


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


    1441790839381取出:100  
    账户余额:0
    瞬间感觉可以理解了吧。


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


    (2)同步代码块


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


    Bank.java代码如下:

    package threadTest;  
    
    
    /** 
     * @author ww 
     * 
     */  
    public class Bank {  
    
    
        private int count =0;//账户余额  
    
    
        //存钱  
        public   void addMoney(int money){  
    
    
            synchronized (this) {  
                count +=money;  
            }  
            System.out.println(System.currentTimeMillis()+"存进:"+money);  
        }  
    
    
        //取钱  
        public   void subMoney(int money){  
    
    
            synchronized (this) {  
                if(count-money < 0){  
                    System.out.println("余额不足");  
                    return;  
                }  
                count -=money;  
            }  
            System.out.println(+System.currentTimeMillis()+"取出:"+money);  
        }  
    
    
        //查询  
        public void lookMoney(){  
            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代码如下:
    package threadTest;  
    
    
    /** 
     * @author ww 
     * 
     */  
    public class Bank {  
    
    
        private volatile int count = 0;// 账户余额  
    
    
        // 存钱  
        public void addMoney(int money) {  
    
    
            count += money;  
            System.out.println(System.currentTimeMillis() + "存进:" + money);  
        }  
    
    
        // 取钱  
        public void subMoney(int money) {  
    
    
            if (count - money < 0) {  
                System.out.println("余额不足");  
                return;  
            }  
            count -= money;  
            System.out.println(+System.currentTimeMillis() + "取出:" + money);  
        }  
    
    
        // 查询  
        public void lookMoney() {  
            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代码修改如下:


    package threadTest;  
    
    
    import java.util.concurrent.locks.Lock;  
    import java.util.concurrent.locks.ReentrantLock;  
    
    
    /** 
     * @author ww 
     * 
     */  
    public class Bank {  
    
    
        private  int count = 0;// 账户余额  
    
    
        //需要声明这个锁  
        private Lock lock = new ReentrantLock();  
    
    
        // 存钱  
        public void addMoney(int money) {  
            lock.lock();//上锁  
            try{  
            count += money;  
            System.out.println(System.currentTimeMillis() + "存进:" + money);  
    
    
            }finally{  
                lock.unlock();//解锁  
            }  
        }  
    
    
        // 取钱  
        public void subMoney(int money) {  
            lock.lock();  
            try{  
    
    
            if (count - money < 0) {  
                System.out.println("余额不足");  
                return;  
            }  
            count -= money;  
            System.out.println(+System.currentTimeMillis() + "取出:" + money);  
            }finally{  
                lock.unlock();  
            }  
        }  
    
    
        // 查询  
        public void lookMoney() {  
            System.out.println("账户余额:" + count);  
        }  
    }


    运行效果怎么样呢?


    余额不足  
    账户余额:0  


    余额不足  
    账户余额:0  


    1441792891934存进:100  
    账户余额:100  


    1441792892935存进:100  
    账户余额:200  


    1441792892954取出:100  
    账户余额:100
    效果和前两种方法差不多。


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


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


    Bank.java代码如下:

    package threadTest;  
    
    
    /** 
     * @author ww 
     * 
     */  
    public class Bank {  
    
    
        private static ThreadLocal<Integer> count = new ThreadLocal<Integer>(){  
    
    
            @Override  
            protected Integer initialValue() {  
                // TODO Auto-generated method stub  
                return 0;  
            }  
    
    
        };  
    
    
        // 存钱  
        public void addMoney(int money) {  
            count.set(count.get()+money);  
            System.out.println(System.currentTimeMillis() + "存进:" + money);  
    
    
        }  
    
    
        // 取钱  
        public void subMoney(int money) {  
            if (count.get() - money < 0) {  
                System.out.println("余额不足");  
                return;  
            }  
            count.set(count.get()- money);  
            System.out.println(+System.currentTimeMillis() + "取出:" + money);  
        }  
    
    
        // 查询  
        public void lookMoney() {  
            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 多线程同步的五种方法

    展开全文
  • Java 多线程同步

    2012-05-31 12:17:38
    创建一个线程有两个方法:继承java.lang.Thread类或实现java.lang.Runnable接口。 main() 函数也是一个线程。   1.1继承Thread Thread 类是一个具体的类,即不是抽象类,该类封装了线程的行为。要创建一个...

    1.线程

    创建一个线程有两个方法:继承java.lang.Thread类或实现java.lang.Runnable接口。

    main() 函数也是一个线程。

     

    1.1继承Thread

    Thread 类是一个具体的类,即不是抽象类,该类封装了线程的行为。要创建一个线程,程序员必须创建一个从 Thread 类导出的新类。程序员必须覆盖 Thread 的 run() 函数来完成有用的工作。用户并不直接调用此函数;而是必须调用 Thread 的 start() 函数,该函数再调用 run()。下面的代码说明了它的用法:

    1. import java.util.*;  
    2. class TimePrinter extends Thread {  
    3.     int pauseTime;  
    4.     String name;  
    5.     public TimePrinter(int x, String n) {  
    6.         pauseTime = x;  
    7.         name = n;  
    8.     }  
    9.     public void run() {  
    10.         while(true) {  
    11.             try {  
    12.                 System.out.println(name + ":" + new   
    13.                     Date(System.currentTimeMillis()));  
    14.                 Thread.sleep(pauseTime);  
    15.             } catch(Exception e) {  
    16.                 System.out.println(e);  
    17.             }  
    18.         }  
    19.     }  
    20.     static public void main(String args[]) {  
    21.         TimePrinter tp1 = new TimePrinter(1000, "Fast Guy");  
    22.         tp1.start();  
    23.         TimePrinter tp2 = new TimePrinter(3000, "Slow Guy");  
    24.         tp2.start();  
    25.     }   
    26. }  

     

    1.2实现Runnable

    此接口只有一个函数,run(),此函数必须由实现了此接口的类实现。但是,就运行这个类而论,其语义与前一个示例稍有不同。我们可以用 runnable 接口改写前一个示例。

     

     

    1. import java.util.*;  
    2. class TimePrinter   
    3.         implements Runnable {  
    4.     int pauseTime;  
    5.     String name;  
    6.     public TimePrinter(int x, String n) {  
    7.         pauseTime = x;  
    8.         name = n;  
    9.     }  
    10.     public void run() {  
    11.         while(true) {  
    12.             try {  
    13.                 System.out.println(name + ":" + new   
    14.                     Date(System.currentTimeMillis()));  
    15.                 Thread.sleep(pauseTime);  
    16.             } catch(Exception e) {  
    17.                 System.out.println(e);  
    18.             }  
    19.         }  
    20.     }  
    21.     static public void main(String args[]) {  
    22.         Thread t1 = new Thread (new TimePrinter(1000, "Fast Guy"));  
    23.         t1.start();  
    24.         Thread t2 = new Thread (new TimePrinter(3000, "Slow Guy"));  
    25.         t2.start();  
    26.     }  
    27. }  

     

     

     

    1.3两种线程实现方式的比较

    比较内容

    继承Thread

    使用Runnable接口

    启动方式

    new一个实例,调用start()方法运行。

    例:

    MyThread extends Thread

    MyThread Test = new MyThread(); 
    Test.start(); 

    new一个实现Runnable的实例,之后用子类Thread调用。

    例:

    Test impelements Runnable 
    Test t = new Test(); 
    Thread thread= new Thread(t); 

     Thread.start();

    层次

    增加类层次

    不增加类层次

    继承关系

    单继承

    利用实现接口,可以达到多继承的效果。还可以再继承其他父类。

    *Runnable的实现方式更加灵活。在实际代码中使用的较多的是利用实现Runnable接口的方式来实现线程的。

     

     

     

    2.线程安全对象

    在Java中有些对象是线程安全的,线程安全的类其方法是同步的,每次只能一个访问。是重量级对象,效率较低。

    对于非线程安全的类和接口,在多线程中需要程序员自己处理线程安全问题。

     

    在集合框架中,线程安全的类有:Vector、HashTable、Stack、Enumeration。

    除了这些之外,其他的都是非线程安全的类和接口。

     

    3.线程状态

     

     线程的状态表示线程正在进行的活动以及在此时间段内所能完成的任务.线程有创建,可运行,运行中,阻塞,死亡五中状态.一个具有生命的线程,总是处于这五种状态之一:
    1.创建状态
    使用new运算符创建一个线程后,该线程仅仅是一个空对象,系统没有分配资源,称该线程处于创建状态(new thread)
    2.可运行状态
    使用start()方法启动一个线程后,系统为该线程分配了除CPU外的所需资源,使该线程处于可运行状态(Runnable)
    3.运行中状态
    Java运行系统通过调度选中一个Runnable的线程,使其占有CPU并转为运行中状态(Running).此时,系统真正执行线程的run()方法.
    4.阻塞状态
    一个正在运行的线程因某种原因不能继续运行时,进入阻塞状态(Blocked)
    5.死亡状态
    线程结束后是死亡状态(Dead)

    4.同步

    使用synchronized进行同步线程同步主要有以下几种方式:

     

    4.1通过synchronized块同步非静态方法

     

     

    1. class Sync {  
    2.     public void method(){  
    3.         synchronized(this){//对method方法使用synchronized进行同步  
    4.         ... ...  
    5.         }  
    6.     }   
    7. }  
     

    4.2使内类的非静态方法和外类的非静态方法同步

    1. class Sync {  
    2.     class InnerClass{  
    3.         public void method(){  
    4.             synchronized(Sync.this){   
    5.             ... ...  
    6.             }  
    7.         }  
    8.     }   
    9. }  
     

    4.3静态类方法的同步

    1. class Sync {  
    2.     public static void method(){  
    3.         synchronized(Sync.this){   
    4.         ... ...  
    5.         }  
    6.     }   
    7.     public static synchronized void method2(){}  
    8. }  
     

    4.4同步锁

    1. class MyThread_1 extends Thread{  
    2. Object lock;  
    3. public MyThread_1(Object o){  
    4.     lock=o;  
    5. }  
    6. public void run(){  
    7.     try{  
    8.         synchronized(lock){  
    9.             System.out.println("Enter Thread_1 and wait");  
    10.             lock.wait();  
    11.             System.out.println("be notified");  
    12.         }  
    13.     }catch(InterruptedException e){}  
    14. }  
    15. }  
    16.   
    17. class MyThread_2 extends Thread{  
    18. Object lock;  
    19. public MyThread_2(Object o){  
    20.     lock=o;  
    21. }  
    22. public void run(){  
    23.     synchronized(lock){  
    24.         System.out.println("Enter Thread_2 and notify");  
    25.         lock.notify();  
    26.     }  
    27. }  
    28. }  
    29.   
    30. public class MyThread{   
    31. public static void main(String[] args){  
    32.     int[] in=new int[0];//notice  
    33.     MyThread_1 t1=new MyThread_1(in);  
    34.     MyThread_2 t2=new MyThread_2(in);  
    35.     t1.start();  
    36.     t2.start();  
    37.     }  


    展开全文
  • java多线程同步

    2010-03-23 10:29:00
    初学java同步,从csdn.../* * 多线程同步依靠的是对象锁机制,synchronized关键字的背后就是利用了封锁来实现对共享资源的互斥访问。下面以一个简单的实例来进行对比分析。实例要完成的工作非常简单,就是创建10个线

     

    初学java同步,从csdn首页推荐上看了这个文章,自己实现了下,并加了点体会,作为自己学习的见证

    也方便日后查看。

     

     

     

     

     

     

    展开全文
  • java多线程同步和通信的方法有如下几种: 1、synchronized关键字修饰方法或代码段,实现数据的互斥访问 2、volatile修饰变量,实现多线程环境下数据的同步 3、ReentrantLock可重入锁,实现数据的互斥访问 3、...

    java多线程同步和通信的方法有如下几种:

        1、synchronized关键字修饰方法或代码段,实现数据的互斥访问
        2、volatile修饰变量,实现多线程环境下数据的同步
        3、ReentrantLock可重入锁,实现数据的互斥访问
        3、synchronized结合Object的wait和notify方法,实现线程间的等待通知机制
        4、ReentrantLock结合Condition接口的await()和signal()方法,实现线程间的等待通知机制   

    1、synchronized关键字修饰方法或代码段,只保证临界数据是互斥访问的

        java的每个对象都有一个内置锁,当用synchronized关键字修饰时,线程会获取该对象的内置锁,其他线程没有获取该对象的内置锁就会进入阻塞状态。
        对象锁:    synchronized关键字修饰代码段时,需要传入一个对象,通过该对象的内置锁来实现代码块的同步。
                synchronized关键字修饰方法时,会将实例化的java对象的内置锁传进去,通过该锁来实现代码块的同步。
        类锁:    synchronized关键字修饰静态方法,会锁住该类的Class对象,此时如果调用该静态方法,将会锁住整个类。
        注意: 同步是一种高开销的操作,因此应该尽量减少同步的内容。通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。

    	class MyThread extends Thread{
    		Bank bank;
    		public MyThread(Bank bank) {
    			this.bank = bank;
    		}
    		@Override
    		public void run() {
    			for(int i=0;i<10;i++)
    			{
    				bank.save(100); // 多线程调用该同步方法
    			}
    		}
    	}
    	class Bank{
    		private int account = 100;//临界数据
    		public int getAccount(){
    			return account;
    		}
    		//同步方法
    		public synchronized void save(int money){
    			account+=money;
    			System.out.println("当前线程:"+Thread.currentThread().getId()+" 当前余额:"+getAccount());
    		}
    		public void save1(int money){
    			//同步代码块
    			synchronized(this) { // 获取当前对象锁
    				account+=money;
    				System.out.println("当前线程:"+Thread.currentThread().getId()+" 当前余额:"+getAccount());
    			}
    		}
    	}
    	public static void main(String[] args)
    	{
    		Bank bank = new Bank();
    		MyThread th1 = new MyThread("线程1",bank);
    		th1.start();
    		MyThread th2 = new MyThread("线程2",bank);
    		th2.start();
    	}

    2、volatile修饰变量

        使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新,因此可以保证在多线程环境下保证数据的一致性

        class Bank {
            //需要同步的变量加上volatile
            private volatile int account = 100;
            public int getAccount() {
                return account;
            }
            //这里不再需要synchronized
            public void save(int money) {
                account += money;
            }
        }

       
    3、ReentrantLock可重入锁,实现数据的互斥访问

        class Bank{
            private ReentrantLock lock = new ReentrantLock();
            private int account = 100;//临界区数据
            public int getAccount(){
                return account;
            }
            
            public void put(int money)
            {
                lock.lock();
                try {
                    
                    account+=money;
                    System.out.println(Thread.currentThread().getId()+"存入"+money+" 当前余额:"+getAccount());
                    
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
            
            public void get(int money)
            {
                lock.lock();
                try {
                    
                    account-=money;
                    System.out.println(Thread.currentThread().getId()+"取出"+money+" 当前余额:"+getAccount());
                    
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally{
                    lock.unlock();
                }
            }
        }

    4、wait和notify,实现线程间的等待通知机制

        通常在synchronized修饰的代码块中使用wait、notify/notifyAll函数.
        当wait()被执行的时,会释放当前所持有的锁,然后让出CPU,进入等待阻塞状态.
        当notify/notifyAll()被执行时候,会唤醒一个或多个正处于等待状态的线程,然后继续往下执行,因此最好在同步代码块最后执行notify/notifyAll.

        //消费者线程类,每隔100ms消费一个产品
        class CustomerThread extends Thread{
            Bank bank;
            public CustomerThread(Bank bank) {
                this.bank = bank;
            }
            @Override
            public void run() {
                while(true){
                    bank.get(1);
                    try {
                        sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        //生产者线程类,每隔300ms生产一个产品
        class ProductorThread extends Thread{
            Bank bank;
            public ProductorThread(Bank bank) {
                this.bank = bank;
            }
            @Override
            public void run() {
                while(true){
                    bank.put(1);
                    try {
                        sleep(300);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        //数据缓冲区
        class Bank{
            private int account = 100;//临界区数据
            public int getAccount(){
                return account;
            }
            public void put(int money){
                synchronized(this) //获取当前对象锁
                {
                    if(getAccount() >= 120){ //若数量大于120则阻塞当前线程,释放对象锁
                        try {
                            this.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    account+=money;//生产一个产品
                    System.out.println(Thread.currentThread().getId()+"存入"+money+" 当前余额:"+getAccount());
                    
                    this.notifyAll();//唤醒其他线程
                }
            }
            
            public void get(int money){
                synchronized(this)
                {
                    if(getAccount() <= 0){ // 若数量小于等于0则阻塞当前线程,释放对象锁
                        try {
                            this.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    account-=money; // 消费一个产品
                    System.out.println(Thread.currentThread().getId()+"取出"+money+" 当前余额:"+getAccount());
                    
                    this.notifyAll();//唤醒其他线程
                }
            }
        }
        //主函数调用例子
        public static void main(String[] args)
        {
            Bank bank = new Bank();//创建一个缓冲区对象
            ProductorThread th1 = new ProductorThread(bank);//创建生产者线程1
            th1.start();
            ProductorThread th2 = new ProductorThread(bank);//创建生产者线程2
            th2.start();
            CustomerThread th3 = new CustomerThread(bank);//创建消费者线程1
            th3.start();
        }

    5、ReentrantLock结合Condition接口,实现线程间的等待通知机制

        class CustomerThread extends Thread{
            Bank bank;
            public CustomerThread(Bank bank) {
                this.bank = bank;
            }
            @Override
            public void run() {
                while(true){
                    bank.get(1);
                    try {
                        sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        class ProductorThread extends Thread{
            Bank bank;
            public ProductorThread(Bank bank) {
                this.bank = bank;
            }
    
            @Override
            public void run() {
                while(true){
                    bank.put(1);
                    try {
                        sleep(300);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        class Bank{
            private ReentrantLock lock = new ReentrantLock(); // 创建可重入锁
            private Condition notEmpty = lock.newCondition(); // 创建非空条件变量
            private Condition notFullCondition = lock.newCondition(); // 创建非满条件变量
            private int account = 100;//临界区数据
            public int getAccount(){
                return account;
            }
            public void put(int money){
                lock.lock();
                try {
                    if(getAccount() >= 120){ //当数量已满时,等待非满条件
                        notFullCondition.await();
                    }
                    
                    //进行生产
                    account+=money;
                    System.out.println(Thread.currentThread().getId()+"存入"+money+" 当前余额:"+getAccount());
                    
                    notEmpty.signal();// 非空条件释放信号
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally{
                    lock.unlock();
                }
            }
    
            public void get(int money){
                
                lock.lock();
                try {
                    if(getAccount() <= 0){ //当数量为空时,等待非空条件
                        notEmpty.await();
                    }
                    
                    // 进行消费
                    account-=money;
                    System.out.println(Thread.currentThread().getId()+"取出"+money+" 当前余额:"+getAccount());
                    
                    notFullCondition.signal();// 非满条件释放信号
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally{
                    lock.unlock();
                }
            }
        }
        public static void main(String[] args)
        {
            Bank bank = new Bank();
            ProductorThread th1 = new ProductorThread(bank);
            th1.start();
            CustomerThread th3 = new CustomerThread(bank);
            th3.start();
        }

     

    展开全文
  • 文章目录前言1、synchronized 关键字修饰方法或代码段,只保证临界数据是互斥访问的2、volatile修饰变量3、ReentrantLock可重入锁,实现数据的...java多线程同步和通信的方法有如下几种: synchronized关键字修饰...
  • Java多线程同步机制

    2017-09-02 11:48:07
    Java多线程同步机制一段sysnchronized的代码被一个线程执行之前,它要先拿到执行这段代码的权限,在java里面就是拿到某个同步对象的锁(一个对象一把锁),如果这个时候同步对象的锁被其他线程拿走了,这个线程就只能...
  • Java 多线程同步-模拟窗口售票

    千次阅读 2018-11-03 17:23:11
    Java 多线程同步-模拟窗口售票 java开发者 2018-11-02 20:52:45 Java 内容目录 实现Runnable接口 使用 同步代码块 1、实现的方式 2、继承的方式 使用 同步方法 1、实现的方式 本文例子:利用多线程模拟 ...
  • Java多线程同步的几种方式

    千次阅读 2015-04-02 20:53:30
    Java多线程同步的几种方式
  • 1. Java多线程同步主要解决的问题: 线程之间的干扰(通过线程同步解决) 内存一致性错误(通过对读写操作建立happens-before关系解决) 2. 概念 1) happens-before关系 写操作与读操作具有happens-...
  • )【更新重要补疑】Java 多线程同步问题的探究(四、协作,互斥下的协作——Java多线程协作(wait、notify、notifyAll))Java 多线程同步问题的探究(三、Lock来了,大家都让开【2. Fair or Unfair? It is a ...
  • java多线程同步

    2013-04-08 22:55:10
    java多线程同步锁的一点理解 高手请解读正确性? 同步锁就是把一段代码加锁 对锁的控制在括号中的内容里 谁得到了 这个锁 谁就可以调用该段代码 锁被加在括号中的对象上 比如下面这段代码:m1()方法中的对象 是谁...
  • java 多线程同步机制

    2017-02-27 22:38:45
    一、为什么需要多线程同步? 二、什么时候需要多线同步? 三、如何理解同步这个词? 四、同步什么? 五、Java如何实现多线程同步
  • java多线程同步编程

    千次阅读 2016-09-08 18:25:21
    学习java有一段时间了,一直对java多线程同步理解的不够深刻,今天将我学习的过程记录下来帮助大家一起来学习深刻理解java多线程同步策略 现实生活中多线程同步场景很多,比如说我的银行卡里面的money数是100,...
  • Java多线程同步解决线程安全的理解

    热门讨论 2021-03-02 19:03:32
    Java多线程使用同步机制处理线程安全的理解 ** ** Java中多线程的创建有三种,这里只说两种。 1.继承于Thread类 ①即创建一个Thread的子类 ②重写Thread类中的run()方法,方法体中写线程执行的操作 ③通过子类的对象...
  • Java 多线程同步的五种方法

    千次阅读 2016-05-23 21:06:07
    Java 多线程同步的五种方法 一、引言 前几天面试,被大师虐残了,好多基础知识必须得重新拿起来啊。闲话不多说,进入正题。 二、为什么要线程同步 因为当我们有多个线程要同时访问一个变量或对象时,...
  • 浅谈Java 多线程同步

    千次阅读 2019-02-24 20:35:34
    最近在研究多线程同步的一些问题,整理了网上很多文档,在这里给大家分享下 Java并发机制的底层实现原理 Java代码在编译后会变成Java字节码,字节码被类加载器加载到JVM里,JVM执行字节码,最终需要转化为汇编指令...
  • Java多线程同步和通信

    千次阅读 2019-12-07 10:15:47
    多线程同步 回顾 1 进程:正在运行的程序,操作系统通过进程Id区分不同进程。 2 线程:进程中的一条执行路径。一个进程中可以包含多个线程,至少有一个。 3 区别: a.一个程序运行后至少有一个进程 b.一个进程可以...
  • java 多线程 同步问题

    2016-11-19 18:53:17
    由于银行账号要求安全性和可靠性较高,需要对存款与取款操作进行同步。 要求:1.不能将存取款写成两个线程类。2.同一对象存款时不能取款。 3.一个对象存取款时不能阻碍其他对象存取款。 十分感谢!!!
  • java多线程同步的例子

    千次阅读 2018-05-30 23:03:28
    多线程访问共享的资源对象,为了避免错误,java提供三种解决机制同步代码块 synchronized code block同步方法 synchronized method同步锁 ReentrantLockpackage com.linchengshen._01.thread; //针对多线程并发访问...
  • java多线程同步5种方法

    万次阅读 多人点赞 2018-05-28 22:11:07
    二、为什么要线程同步因为当我们有个线程要同时访问一个变量或对象时,如果这些线程中既有读又有写操作时,就会导致变量值或对象的状态出现混乱,从而导致程序异常。举个例子,如果一个银行账户同时被两个线程操作...
  • Java 多线程同步和异步详解

    千次阅读 2018-05-31 10:00:32
    转载自 https://www.cnblogs.com/mengyuxin/p/5358364.htmljava线程 同步与异步 线程池1)多线程并发时,多个线程同时请求同一个资源,必然导致此资源的数据不安全,A线程修改了B线程的处理的数据,而B线程又修改了...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 33,803
精华内容 13,521
关键字:

java多线程同步

java 订阅