精华内容
下载资源
问答
  • 本文全文以售票系统为例,简诉了java多线程间共享数据的两种方式、线程同步。文章可能还有很多不足,请大家谅解,欢迎大佬提意见。 本文使用到的东西 java eclipse 2019-11 文章目录写在前面本文使用到的东西1.多...

    写在前面

    本文全文以售票系统为例,简诉了java多线程间共享数据的两种方式、线程同步。文章可能还有很多不足,请大家谅解,欢迎大佬提意见。

    本文使用到的东西

    1. java
    2. eclipse 2019-11

    1.多线程共享数据

    1.1 共享Runnable

      当多个线程执行的内容相同,可以采用共享Runnable接口实现类对象的方式共享数据,将共享的数据作为Runnable对象的成员变量。
      这里我们以包含多个售票处的售票系统为例,每一个售票处一个线程,多个线程共享余票数变量。

    class TicketRunnable implements Runnable{
    	private int num;
    	
    	public TicketRunnable(int num) {
    		this.num=num;
    	}
    	@Override
    	public void run() {
    		for(int i=0;i<5;i++) {	//出售5张票
    			if(num>0) {
    				num--;
    				System.out.println(Thread.currentThread().getName()+":售票1张,余票"+num);
    			}else {
    				System.out.println(Thread.currentThread().getName()+":暂时无余票");
    			}
    		}
    	}
    }
    public class 多线程共享数据 {
    	public static void main(String[] args) {
    		Runnable runnable = new TicketRunnable(8);	//初始化8张余票
    		new Thread(runnable,"武汉售票点").start();
    		new Thread(runnable,"北京售票点").start();
    	}
    }
    

    1.2 封装数据为对象

      当多个线程执行的是相同的操作时可以采用共享Runnable对象的方法来共享变量,执行不同操作时这个方法就不适用,可以将共享的数据封装成对象,多个线程共享该对象。
      以售票系统为例,一个线程执行退票,一个线程执行售票。

    class Ticket{
    	private int num;
    	public Ticket(int num) {
    		this.num=num;
    	}
    	public void sell() {
    		if(num>0) {
    			num--;
    			System.out.println(Thread.currentThread().getName()+":售票1张,余票"+num);
    		}else {
    			System.out.println(Thread.currentThread().getName()+":暂时无余票");
    		}
    	}
    	public void returned() {
    		num++;
    		System.out.println(Thread.currentThread().getName()+":退票1张,余票"+num);
    	}
    }
    public class 多线程共享数据 {
    	public static void main(String[] args) {
    		Ticket ticket = new Ticket(8);	//初始化为8张票
    		new Thread(new Runnable() {
    			@Override
    			public void run() {
    				for(int i=0;i<8;i++) {
    					ticket.sell();//售票
    				}
    			}
    		},"售票处").start();
    		new Thread(new Runnable() {
    			@Override
    			public void run() {
    				for(int i=0;i<8;i++) {
    					ticket.returned();//退票
    				}
    			}
    		},"退票处").start();
    	}
    }
    

    2.线程同步与互斥

    2.1 上述代码存在的问题

    以共享Runnable对象实现同步的方式为例,运行该程序,运行结果如下:

    武汉售票点:售票1张,余票6
    武汉售票点:售票1张,余票5
    北京售票点:售票1张,余票6
    武汉售票点:售票1张,余票4
    武汉售票点:售票1张,余票2
    北京售票点:售票1张,余票3
    北京售票点:售票1张,余票0
    北京售票点:暂时无余票
    北京售票点:暂时无余票
    武汉售票点:售票1张,余票1
    

      我们设置的初始票数为8,查看运行结果,余票数量并不是从7开始递减,而是从6开始,而且并不是递减。出现该问题是因为武汉售票点将票数减1还未输出的时候,北京售票点也将票数减1,这时候输出结果就是6了。不是按递减输出也同样是因为读取了数据还未输出,另一个线程执行了卖票输出。对TicketRunnable的run()方法稍加修改,修改为

    	@Override
    	public void run() {
    		for(int i=0;i<5;i++) {	//出售5张票
    			synchronized (this) {
    				if(num>0) {
    					num--;
    					System.out.println(Thread.currentThread().getName()+":售票1张,余票"+num);
    				}else {
    					System.out.println(Thread.currentThread().getName()+":暂时无余票");
    				}
    			}
    		}
    	}
    

    此时,又是按递减顺序输出程序内容,因为synchronized给代码块添加了同步锁,将修改值和取值的操作进行了同步,所以不会在出现乱序、输出余票不正确的情况。

    2.2 同步与互斥

    1. 什么是互斥?
    在计算机中很多资源都是有限的,这种有限资源叫做临界资源。多个进程争抢同一个临界资源,抢到了可以运行,没抢到就无法运行。互斥就是争夺临界资源进程的间接制约关系。 例如多个打印线程争夺一台打印机资源,进程间就形成了互斥。

    2. 什么是同步?
    同步是协调多个相互关联线程合作完成任务,彼此之间存在一定约束,执行顺序往往是有序的。 同步是进程间的直接制约关系,例如供销系统,当仓库满了需要停止供货,仓库空了无法出货,此时供货进程和销货进程就形成了同步关系。

    2.3 synchronized实现同步

    synchronized可用于修饰方法、静态方法和代码块

    //对象锁,修饰方法
    synchronized void a() {
    
    }
    //类锁,修饰静态方法
    synchronized static void b() {
    
    }
    void c() {
    	//对象锁,修饰代码块
    	synchronized (this) {
    			
    	}
    	//类锁,修饰代码块
    	synchronized (Ticket.class) {
    
    	}	
    }
    

    2.4 ReentrantLock实现同步

    1.使用

    ReentrantLock lock = new ReentrantLock();
    lock.lock();	//加锁
    lock.unlock();	//解锁
    

    2.实现上述的售票同步

    class TicketRunnable implements Runnable{
    	private int num;
    	ReentrantLock lock = new ReentrantLock();
    	
    	public TicketRunnable(int num) {
    		this.num=num;
    	}
    	@Override
    	public void run() {
    		for(int i=0;i<5;i++) {	//出售5张票
    			lock.lock();
    			if(num>0) {
    				num--;
    				System.out.println(Thread.currentThread().getName()+":售票1张,余票"+num);
    			}else {
    				System.out.println(Thread.currentThread().getName()+":暂时无余票");
    			}
    			lock.unlock();
    		}
    	}
    }
    public class 多线程共享数据 {
    	public static void main(String[] args) {
    		Runnable runnable = new TicketRunnable(8);	//初始化8张余票
    		new Thread(runnable,"武汉售票点").start();
    		new Thread(runnable,"北京售票点").start();
    	}
    }
    

    3.总结

    synchronized实现的是同步还是互斥这一点有些难理解,网上也有说synchronized是互斥锁的,synchronized实现的是修饰的内容同步。有不清楚的地方欢迎评论留言,看到的我都会回复的。本文到此结束,有什么不足的地方请大家不吝指正。

    展开全文
  • 操作系统实验 多线程同步与互斥 java编写 可动态创建
  • 通过JAVA多线程同步互斥的技术实现CSMA/CD协议的模拟
  • java线程同步与互斥

    千次阅读 2017-09-13 00:55:06
    同步:用关键字synchronized给针对共享资源的操作(方法或代码块)加锁,这把锁就叫作互斥锁。所以有两种加锁方式,一种是同步方法,一种是同步代码块。如图: 特别提示:锁住的不是方法,也不是代码块,而是对象,...

    同步:用关键字synchronized给针对共享资源的操作(方法或代码块)加锁,这把锁就叫作互斥锁。所以有两种加锁方式,一种是同步方法,一种是同步代码块。如图:


    特别提示:锁住的不是方法,也不是代码块,而是对象,并且是同一个对象。也就是说一个线程正在调用该对象的同步方法,如果还没有执行完成,那么另一个线程就无法调用该对象的所有同步方法或代码块(会阻塞),当然没有加synchronized关键字的方法或代码块不会被锁住。

    示例代码1:

    class ShareObj{
    	String[] colors1={"red","orange","yellow", "green",
    			"blue","indigo","purple","black","white"};
    	String[] colors2={"红色","橙色","黄色", "绿色",
    			"蓝色","靛色","紫色","黑色","白色"};
    	synchronized void printColor(String thName){
    		if(thName.equals("color")){
    			for(int i=0;i<colors1.length;i++){
    				try {
    					System.out.println(colors1[i]);
    					Thread.sleep((long)(Math.random()*2000));
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    			}
    		}else if(thName.equals("颜色")){
    			for(int i=0;i<colors2.length;i++){
    				try {
    					System.out.println(colors2[i]);
    					Thread.sleep((long)(Math.random()*2000));
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    			}
    		}	
    	}
    	
    }
    class CreateByThread extends Thread{
    	ShareObj obj=null;
    	public CreateByThread(String name,ShareObj obj){
    		super(name);
    		this.obj=obj;
    	}
    	public void run() {
    		obj.printColor(this.getName());
    	}
    }
    public class ThreadSynExc {
    	public static void main(String[] args) {
    		ShareObj obj=new ShareObj();
    		CreateByThread cbt1=new CreateByThread("color",obj);
    		CreateByThread cbt2=new CreateByThread("颜色",obj);
    		cbt1.start();
    		cbt2.start();
    	}
    }
    如果printColor方法没有加synchronized关键字,运行结果如下:

    红色

    red
    orange
    yellow
    橙色
    green
    黄色
    绿色
    blue
    蓝色
    indigo
    purple
    靛色
    紫色
    black
    white
    黑色
    白色

    这就是两个线程都在调用printColor方法的结果,相互竞争资源,没有规律。

    如果我们要求一个线程在调用printColor的时候,其它线程不能调用,我们就可以在printColor前面加上synchronized关键字
    运行结果如下所示:

    红色
    橙色
    黄色
    绿色
    蓝色
    靛色
    紫色
    黑色
    白色
    red
    orange
    yellow
    green
    blue
    indigo
    purple
    black
    white

    示例代码2:

    public class Bank {
    	private int balance =0;//账户余额  
        //存钱  
        public void addMoney(int money){  
        	System.out.println("======存钱start=======");
        	balance +=money;  
            System.out.println(System.currentTimeMillis()+"存进:"+money);  
            System.out.println("账户余额:"+balance);  
            System.out.println("======存钱end=======");
        }  
        //取钱  
        public void subMoney(int money){ 
        	System.out.println("======取钱start=======");
            if(balance-money < 0){  
                System.out.println("余额不足"); 
                System.out.println("======取钱end=======");
                return;  
            }  
            balance -=money;  
            System.out.println(+System.currentTimeMillis()+"取出:"+money);  
            System.out.println("账户余额:"+balance); 
            System.out.println("======取钱end=======");
        }  
    }
    
    public class SyncBankTest {
    	public static void main(String[] args) {
    		final Bank bank=new Bank();  
            Thread tadd=new Thread(new Runnable() {  
                public void run() {  
                    while(true){  
                        try {  
                        	Thread.sleep(1000);
                        } catch (InterruptedException e) {  
                            e.printStackTrace();  
                        }  
                        bank.addMoney(100);  
                          
                    }  
                }  
            });  
            Thread tsub = new Thread(new Runnable() {  
                public void run() {  
                    while(true){  
                        bank.subMoney(100);  
                        try {  
                        	Thread.sleep(1000);
                        } catch (InterruptedException e) {  
                            e.printStackTrace();  
                        }     
                    }  
                }  
            });  
            tsub.start();  
            tadd.start();  
    	}
    }

    如果不加synchronized关键字,运行结果是这样的:

    ======存钱start=======
    1505238241890存进:100
    账户余额:100
    ======存钱end=======
    ======取钱start=======
    1505238242890取出:100
    账户余额:0
    ======存钱start=======
    ======取钱end=======
    1505238242890存进:100
    账户余额:100
    ======存钱end=======
    ======取钱start=======
    1505238243890取出:100
    账户余额:0
    ======取钱end=======
    ======存钱start=======
    1505238243890存进:100
    账户余额:100
    ======存钱end=======

    是不是看起来很混乱,看不明白存钱取钱是怎么回事?

    如果在存钱和取钱的方法上加上synchronized关键字,运行结果如下所示:

    ======取钱start=======
    余额不足
    ======取钱end=======
    ======存钱start=======
    1505238048821存进:100
    账户余额:100
    ======存钱end=======
    ======取钱start=======
    1505238048834取出:100
    账户余额:0
    ======取钱end=======
    ======存钱start=======
    1505238049821存进:100
    账户余额:100
    ======存钱end=======
    ======取钱start=======
    1505238049834取出:100
    账户余额:0
    ======取钱end=======
    ======存钱start=======
    1505238050821存进:100
    账户余额:100
    ======存钱end=======
    ======取钱start=======
    1505238050834取出:100
    账户余额:0
    ======取钱end=======

    怎么样,是不是感觉可以理解了!!

    示例代码3:

    class SyncCla {  
      
        public synchronized void test() {  
            System.out.println("test开始..");  
            try {  
                Thread.sleep(1000);  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            }  
            System.out.println("test结束..");  
        }  
    }  
      
    class MyThread extends Thread {  
      
        public void run() {  
            SyncCla sync = new SyncCla();  
            sync.test();  
        }  
    }  
      
    public class Main {  
      
        public static void main(String[] args) {  
            for (int i = 0; i < 3; i++) {  
                Thread thread = new MyThread();  
                thread.start();  
            }  
        }  
    } 

    运行结果:

    test开始..
    test开始..
    test开始..
    test结束..
    test结束..
    test结束..

    可以看出来,上面的程序起了三个线程,同时运行SyncCla类中的test()方法,虽然test()方法加上了synchronized,但是还是同时运行起来,貌似synchronized没起作用。因为SyncCla被new了三次,每个线程分别是一个不同对象,所以synchronized不起作用了

    说明synchronized关键字是对同一对象加锁!!!!切记切记!



    展开全文
  • Thread安全线程安全不是指数据本身或者数据传输中的安全,而主要是指在高并发多线程的访问过程中,多线程对数据本身的读写所造成的数据不一致、脏数据等情况的避免...在多线程运行过程中,需要保持各个线程之间的同步

    Thread安全

    线程安全不是指数据本身或者数据传输中的安全,而主要是指在高并发多线程的访问过程中,多线程对数据本身的读写所造成的数据不一致、脏数据等情况的避免。在多线程运行过程中,需要保持各个线程之间的同步。同步指在多个线程之间需要某种机制来保证线程之间的执行顺序,以确保如as-of-Serilse,顺序一致性的要求

    1. Java内存模型JMM与多线程

    在Java里,JLS定义了Java的统一的内存管理模型JMM(Java Memory Model)。JMM规定JVM中有主内存(Main Memory)以及工作内存(Work Memory)主内存就是我们所说的堆内存,存放类的实例、静态数据等,由多线程共享。工作内存是各个线程自己的“小金库”,存放自己从主内存拷贝过来的变量以及局部变量。既然是小金库,那么就只能自己访问,其他线程无法访问。如果线程之间要相互通信,就只能用主内存中的共享变量进行通信。

    JMM

    既然存在这样的内存模型,那么在具体的多线程读写中,会发生什么问题呢?

    • 可见性问题

      因为在线程对变量进行读写时,首先从主内存中将数据复制到自己的工作内存中,在自己的工作内存中进行写操作后,在刷新回主内存。由于主内存是共享的,工作内存是私有的,因此在这二者之间就存在着隔离,就会导致其他线程的读写不一致。同时,由于编译器以及处理器会对程序进行重排序,也会对程序的读写顺序,保证一致性提出挑战。

      为了解决可见性问题,保证happen-before(JSR-113内存模型:happen-before指一个操作的结果相对于另一个操作是生效可见的)语义,有很多种方法,比如用volatile,可保证可见性以及阻止局部重排序。以及通过锁机制保证可见性、原子操作等等。

    • 时序问题

      我们知道当线程需要一个变量时,先从主内存中获取一个变量,复制到工作内存中进行读写。当这个线程再次需要用到这个变量时,可以从主内存中复制新的过来,或者直接使用工作内存中的变量。假如多个线程同时需要读写这个变量,那这个过程就会存在时序问题。

    在多线程的编写中,我们需要保证线程安全,即每次多线程执行结束后操作的结果都是一样的。因此需要用到Java中的同步互斥机制来确保线程安全。

    2. synchronized

    synchronized是保证线程同步的关键字。通过使用这个关键字,可以使得被标记的代码在任何时候最多允许一个线程执行这段代码。

    一般有两种用法:

    法一:
    
    public synchronized void method(){
        …………
    }
    
    法二:
    
    public int method(){
        synchronized(this){
            …………
        }
    }

    这两种方法都能进行控制。当然各有优缺点。由于法一是直接对整个方法进行同步,在进入方法时需要再分配资源,性能会低于法二。

    对于法二,语法为synchronized (object){……}。其中object可以是任意其他对象、this。可以用

    private byte[] lock = new byte [1];
    public void method() {
        synchronized (lock){
        ……
        }
    }

    这种方式比较常用,能减少对锁的新建、释放所需要的资源,提高性能。但是需要注意的是,在一个class中,所有需要同步的方法都用这个lock对象,这样才能保证线程安全。

    当一个线程访问一个object的一个synchronized同步块时,它将获得整个对象的所有加了同步的方法锁,也就是说即使它没有访问这个对象里其他的同步块,但是其他线程也无法访问那些没被访问的同步块。换句话说,就是对这个对象“包场”了。

    3. Lock and ReentrantLock

    synchronized关键字提供了隐式锁机制来保证线程同步。当然,java中提供显示锁Lock,可手动进行锁的请求与释放。与synchronized相比,显示锁可操作性更强,功能更强。

    Lock相对于synchronized,区别在于Lock是对代码块进行加锁,而synchronized是对对象进行操作,保证对象的互斥。

    这里对几种常用锁进行对比

    ReentrantLock

    这个锁是在工作中用量很大的。主要的好处是更具有伸缩性,当很多线程竞争相同锁时,能够提供相对synchronized更高的吞吐量。主要的缺点就是需要手动释放锁。当然这个是无法避免的。

    private final ReentrantLock lock = new ReentrantLock();
    public void method() {
        lock.lock();
        try{
        ……
        }finally{
            lock.unlock();
        }
    }

    ReadWriteLock and ReentrantReadWriteLock

    ReadWriteLock接口,主要用在对于读操作比较多,写操作比较少的情况。相对于ReentrantLock对读写都需要进行互斥保证,ReadWriteLock对读写进行了优化。

    • 多线程可同时读
    • 单线程写
    • 有读是不能写,有写时不能读

      ReentrantReadWriteLock是其实现类,实现读锁、写锁的分离使用,能适用更复杂的应用场景

     private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();    //创建锁对象
     private Lock readLock = rwLock.readLock();
     private Lock writeLock = rwLock.writeLock();
    
     public void write(){
            writeLock.lock();
            ……
            writeLock.unlock();
        }
      public int read(){
            readLock.lock();
            ……
            readLock.unlock();
        }

    StampedLock

    StampedLock主要是为了实现悲观锁和乐观锁机制。一般利用StampedLock是为了用作开发线程安全组件的内部工具,吞吐量相对于Lock有很大的改进。但是这个锁有点小复杂,我还对这个还不是很熟,就不细讲了。在一般的应用场景中更主要是运用ReentrantLock系列就足够了。

    4. volatile修饰变量

    volatile关键字的作用是告诉编译器,这个被修饰的变量是容易变化的,因此不对这个变量使用缓存等优化机制。每次使用时直接从主存中进行读取。volatile是轻量级的synchronized,不会引起上下文切换和调度。

    对于volatile的具体实现细节,我们不做深入讨论。大体是根据在多处理器环境中,处理器本身的指令来对volatile进行支持。在编译为汇编语言后,会插入一个LOCK关键字。整个过程分为两步,首先Lock指令会引起处理器的缓存写回到内存。第二步是一个处理器将其缓存写会到共享内存中后,其余处理器内关于该数据的缓存会无效,只能从内存中读取新的。

    LinkedTransferQueue类对volatile进行了优化,基于处理器每个缓存行64个字节的特点进行了优化。

    可以看出volatile提供内存可见性,没有提供原子性。主要可以用在一个变量会有多个线程读,一个线程写的场景下。

    5.原子操作atomic

    在高并发的环境下非常适合用原子操作。原子操作即是一个操作要么完全执行,要么完全不执行。在Java中提高了多种原子类可供使用,如AtomicInteger\AtomicLong\AtomicBoolean\AtomicReference等等,基本各类的操作大同小异,提供读、写、赋值、自增、自减等等操作。

    原子操作的实现原理为CPU的CAS(比较交换),实现机制同样是根据处理器本身进行的实现,可通过总线锁以及缓存锁定来实现原子性。CAS在实现的过程中会遇到几种问题,如ABA问题、循环时间长开销长、以及只能保证一个共享变量的原子操作。

    由于不是我们的关注重点,也就不说了,只需要知道怎么用就行。

    以上的volatile、synchronized、atomic为java对于多线程支持的基本操作元素。其他的各类实现都是对这三种操作的运用。

    point: 在并发多线程编程中,运用同步锁机制的主要是用于单例模式(Singleton)。在编写时,不仅要考虑线程安全,同时也要尽量提高并发性能。

    展开全文
  • Java线程同步互斥

    2021-03-17 17:53:21
    本例将模仿经典的线程同步互斥例子——生产者和消费者问题,来演示  java 强大的多线程机制。生产者和消费者共享一个数据,当数据为0 时,消费者不可访问,生产者可访问数据,每次访问数据加1;当数据到达100 时,...
  • 线程的同步与互斥(同步线程异步线程,线程同步和异步问题) Java 虚拟机中的同步(Synchronization)基于进入和退出管程(Monitor)对象实现, 无论是显式同步(有明确的 monitorenter 和 monitorexit 指令,即同...

    多线程三个特征:原子性、可见性以及有序性.
    同步锁 /并发锁/ 读写锁,显示锁, ReentrantLock与Condition.
    > 线程的同步与互斥 (同步线程与异步线程,线程同步和异步问题)
     Java 虚拟机中的同步(Synchronization)基于进入和退出管程(Monitor)对象实现, 无论是显式同步(有明确的 monitorenter 和 monitorexit 指令,即同步代码块)还是隐式同步都是如此。在 Java 语言中,同步用的最多的地方可能是被 synchronized 修饰的同步方法。同步方法 并不是由 monitorenter 和 monitorexit 指令来实现同步的,而是由方法调用指令读取运行时常量池中方法的 ACC_SYNCHRONIZED 标志来隐式实现的.

     1.同步:假设现有线程A和线程B,线程A需要往缓冲区写数据,线程B需要从缓冲区读数据,但他们之间存在一种制约关系,即当线程A写的时候,B不能来拿数据;B在拿数据的时候A不能往缓冲区写,也就是说,只有当A写完数据(或B取走数据),B才能来读数据(或A才能往里写数据)。这种关系就是一种线程的同步关系。
      同步关系则是多个线程彼此合作,通过一定的逻辑关系来共同完成一个任务。一般来说,同步关系中往往包含互斥,同时对临界区的资源会按照某种逻辑顺序进行访问。如先生产后使用。
      同步并没有通过指令monitorenter和monitorexit来完成(理论上其实也可以通过这两条指令来实现),不过相对于普通方法,其常量池中多了ACC_SYNCHRONIZED标示符。JVM就是根据该标示符来实现方法的同步的:当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他任何线程都无法再获得同一个monitor对象。 其实本质上没有区别,只是方法的同步是一种隐式的方式来实现,无需通过字节码来完成。

     2.互斥:对于线程A和线程B来讲,在同一时刻,只允许一个线程对临界资源进行操作,即当A进入临界区对资源操作时,B就必须等待;当A执行完,退出临界区后,B才能对临界资源进行操作。
      所谓互斥,就是不同线程通过竞争进入临界区(共享的数据和硬件资源),为了防止访问冲突,在有限的时间内只允许其中之一独占性的使用共享资源。如不允许同时写;

     3.总的来说,两者的区别就是:
      互斥是通过竞争对资源的独占使用,彼此之间不需要知道对方的存在,执行顺序是一个乱序。
      同步是协调多个相互关联线程合作完成任务,彼此之间知道对方存在,执行顺序往往是有序的。

    > synchronized在静态方法上表示调用前要获得类的锁,而在非静态方法上表示调用此方法前要获得对象的锁。
    public class StaticSynDemo {
     private static String a="test";
     public void print2(String b){
       synchronized (this) {//取得StaticSynDemo实例化后对象的锁
        System.out.println(b+a);
       }
     }
      
     public static void print4(String b){
       synchronized (StaticSynDemo.class) { //取得StaticSynDemo.class类的锁
        System.out.println(b+a);
       }
     }

    }
      同步代码块或同步对象:synchronized 方法和 synchronized 块。synchronized(this)、synchronized(*.class)与synchronized(任意对象)这几种类型同步方法:
      同步synchronized(*.class)代码块的作用其实和synchronized static方法作用一样。Class锁对类的所有对象实例起作用。
      this关键字代表类的一个对象,所以其内存锁是针对相同对象的互斥操作,而static成员属于类专有,其内存空间为该类所有成员共有,这就导致synchronized()对static成员加锁,相当于对类加锁,也就是在该类的所有成员间实现互斥,在同一时间只有一个线程可访问该类的实例。

    Synchronized和Static Synchronized区别:
      一个是实例锁(锁在某一个实例对象上,如果该类是单例,那么该锁也具有全局锁的概念),一个是全局锁(该锁针对的是类,无论实例多少个对象,那么线程都共享该锁)。
      实例锁对应的就是synchronized关键字,而类锁(全局锁)对应的就是static synchronized(或者是锁在该类的class或者classloader对象上)。
      synchronized(this)与synchronized(static XXX)的区别了,synchronized就是针对内存区块申请内存锁,this关键字代表类的一个对象,所以其内存锁是针对相同对象的互斥操作,而static成员属于类专有,其内存空间为该类所有成员共有,这就导致synchronized()对static成员加锁,相当于对类加锁,也就是在该类的所有成员间实现互斥,在同一时间只有一个线程可访问该类的实例。

     synchronized是通过同一时刻只有一个线程执行共享代码来保证多线程三个特征的;
      volatile 变量具有 synchronized 的可见性特性,禁止指令重排,但是不具备原子特性。使用volatile变量,必须同时满足下面两个条件:
    1.对变量的写操作不依赖于当前值。 
    2.该变量没有包含在具有其他变量的不变式中。

    > 线程安全
     造成线程安全问题的主要诱因有两点:
    一是存在共享数据(也称临界资源),
    二是存在多条线程共同操作共享数据。

      在单线程中不会出现线程安全问题,而在多线程编程中,有可能会出现同时访问同一个资源的情况,这种资源可以是各种类型的的资源:一个变量、一个对象、一个文件、一个数据库表等,而当多个线程同时访问同一个资源的时候,就会存在一个问题:
      由于每个线程执行的过程是不可控的,所以很可能导致最终的结果与实际上的愿望相违背或者直接导致程序出错。线程安全问题,即多个线程同时访问一个资源时,会导致程序运行结果并不是想看到的结果。这里面,这个资源被称为:临界资源(也有称为共享资源)。
      也就是说,当多个线程同时访问临界资源(一个对象,对象中的属性,一个文件,一个数据库等)时,就可能会产生线程安全问题。不过,当多个线程执行一个方法,方法内部的局部变量并不是临界资源,因为方法是在栈上执行的,而Java栈是线程私有的,因此不会产生线程安全问题。

    -- 如何解决线程安全问题的呢?
      基本上所有的并发模式在解决线程安全问题时,都采用“序列化访问临界资源”的方案,即在同一时刻,只能有一个线程访问临界资源,也称作同步互斥访问。通常来说,是在访问临界资源的代码前面加上一个锁,当访问完临界资源后释放锁,让其他线程继续访问。在Java中,提供了两种方式来实现同步互斥访问:synchronized和Lock。
      在了解synchronized关键字的使用方法之前,我们先来看一个概念:互斥锁,顾名思义:能到达到互斥访问目的的锁。举个简单的例子:如果对临界资源加上互斥锁,当一个线程在访问该临界资源时,其他线程便只能等待。
      在Java中,每一个对象都拥有一个锁标记(monitor),也称为监视器,多线程同时访问某个对象时,线程只有获取了该对象的锁才能访问。
      在Java中,可以使用synchronized关键字来标记一个方法或者代码块,当某个线程调用该对象的synchronized方法或者访问synchronized代码块时,这个线程便获得了该对象的锁,其他线程暂时无法访问这个方法,只有等待这个方法执行完毕或者代码块执行完毕,这个线程才会释放该对象的锁,其他线程才能执行这个方法或者代码块。

    > 同步锁 /并发锁/ 读写锁
      互斥锁的本质: 首先需要明确一点,互斥锁实际上是一种变量,在使用互斥锁时,实际上是对这个变量进行置0置1操作并进行判断使得线程能够获得锁或释放锁。 
    -- 死锁产生的条件: 
    1、互斥属性:即每次只能有一个线程占用资源。 
    2、请求与保持:即已经申请到锁资源的线程可以继续申请。在这种情况下,一个线程也可以产生死锁情况,即抱着锁找锁。 
    3、不可剥夺:线程已经得到所资源,在没有自己主动释放之前,不能被强行剥夺。 
    4、循环等待:多个线程形成环路等待,每个线程都在等待相邻线程的锁资源。
    -- 死锁的避免: 
    1、既然死锁的产生是由于使用了锁,那么在能不使用锁的情况下就尽量不使用,如果有多种方案都能实现,那么尽量不选用带锁的这种方案 
    2、尽量避免同时获得多把锁,如果有必要,就要保证获得锁的顺序相同。

    > Java锁:无锁状态、偏向锁、轻量级锁和重量级锁

    一网打尽Java中锁的分类- https://www.jianshu.com/p/49f4a8730833
    Java中锁的分类只是将锁的特性进行了归纳,可以分为:
     1.可重入锁/不可重入锁
     2.可中断锁
     3.公平锁/非公平锁
     4.独享锁(互斥锁)/共享锁(读写锁)
     5.乐观锁/悲观锁
     6.分段锁
     7.偏向锁/轻量级锁/重量级锁
     8.自旋锁

        注意:ReentrantLock和ReentrantReadWriteLock虽然一些性质相同,但前者实现的是Lock接口,后者实现的是ReadWriteLock接口。
    在Java中,synchronized是不可中断锁,而ReentrantLock是可中断锁。

      可重入锁是指在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。ReentrantLock和synchronized都是可重入锁。可重入锁的一个好处是可一定程度避免死锁。
      不可重入锁是指若当前线程执行某个方法已经获取了该锁,那么在方法中尝试再次获取锁时,就会获取不到且被阻塞。

      公平锁:以请求锁的顺序来获取锁。比如同时有多个线程在等待一个锁,当这个锁被释放时,等待时间最久的线程(最先请求的线程)会获得该锁,这种就是公平锁。
      非公平锁:无法保证锁的获取是按照请求锁的顺序进行的。这样就可能导致某个或者一些线程永远获取不到锁。
      在Java中,synchronized就是非公平锁,它无法保证等待的线程获取锁的顺序;而对于ReentrantLock和ReentrantReadWriteLock,默认情况下是非公平锁,但是可以在构造函数中设置为公平锁。

     独享锁:该锁一次只能被一个线程所持有。
     共享锁:该锁可被多个线程所持有。
     对于ReentrantLock/synchronized而言,其是独享锁。但是对于ReadWriteLock,其读锁是共享锁,其写锁是独享锁。
     读锁的共享锁可保证并发读是非常高效的,但读写、写读 、写写的过程是互斥的。

     乐观锁与悲观锁是从看待并发同步的角度来划分的。
     悲观锁认为对于同一个数据的并发操作,一定是会发生修改的,哪怕没有修改,也会认为修改。因此对于同一个数据的并发操作,悲观锁采取加锁的形式。悲观的认为,不加锁的并发操作一定会出问题。
     乐观锁则认为对于同一个数据的并发操作,是不会发生修改的。在更新数据的时候,会采用尝试更新,不断重新的方式更新数据。乐观的认为,不加锁的并发操作是没有事情的。
     从上面的描述我们可以看出,悲观锁适合写操作非常多的场景,乐观锁适合读操作非常多的场景,不加锁会带来大量的性能提升。
     悲观锁在Java中的使用,就是J.U.C下的locks包和synchronized关键字。
     乐观锁在Java中的使用,(又称为无锁编程)常常采用的是CAS算法,J.U.C下Atomic包的各种实现。

     分段锁其实是一种锁的设计,对于ConcurrentHashMap而言,其并发的实现就是通过分段锁的形式来实现高效的并发操作。
     我们以ConcurrentHashMap来说一下分段锁的含义以及设计思想,ConcurrentHashMap中的分段锁称为Segment,它是类似于HashMap(JDK7版本及以上的HashMap实现)的结构,即内部拥有一个Entry数组,数组中的每个元素又是一个链表;同时又是一个ReentrantLock(Segment继承了ReentrantLock)。
     当需要put元素的时候,并不是对整个hashmap进行加锁,而是先通过hashcode来知道他要放在那一个分段中,然后对这个分段进行加锁,所以当多线程put的时候,只要不是放在一个分段中,就实现了真正的并行的插入。
     在统计size的时候,可就是获取hashmap全局信息的时候,就需要获取所有的分段锁才能统计。
     分段锁的设计目的是细化锁的粒度,当操作不需要更新整个数组的时候,就仅仅针对数组中的一项进行加锁操作。

      偏向锁/轻量级锁/重量级锁, 这三种锁对应synchronized锁的三种状态。JDK6通过引入锁升级的机制来实现高效synchronized。这三种锁的状态是通过对象监视器在对象头中MarkWord的值来表明的。
      偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。
      轻量级锁是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。
      重量级锁是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让其他申请的线程进入阻塞,性能降低。

      自旋锁:在Java中,自旋锁是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。

    -- 锁的使用场景 
      由于偏向锁在线程存在竞争的时候会带来额外的性能开销,所以偏向锁适用于只有一个线程方法同步快的情况;轻量级锁在线程竞争锁的情况下不会导致线程阻塞,但是会通过自旋消耗CPU,所以轻量级锁适用于追求响应时间的情况。重量级锁线程竞争不会使用自旋,但是线程竞争会导致阻塞,所以响应时间比较慢,重量级锁一般使用在追求吞吐量的情况。

    -- 当前常用的多线程同步机制可以分为下面三种类型: 
    1.volatile 变量:轻量级多线程同步机制,不会引起上下文切换和线程调度。仅提供内存可见性保证,不提供原子性。 
    2.CAS 原子指令:轻量级多线程同步机制,不会引起上下文切换和线程调度。它同时提供内存可见性和原子化更新保证。 
    3.内部锁和显式锁:重量级多线程同步机制,可能会引起上下文切换和线程调度,它同时提供内存可见性和原子性。

      -- java同步机制: volatile、synchronized和final.
      Java™ 语言包含两种内在的同步机制:同步块(或方法)和 volatile 变量。这两种机制的提出都是为了实现代码线程的安全性。其中 Volatile 变量的同步性较差(但有时它更简单并且开销更低),而且其使用也更容易出错。
      Java 语言中的 volatile 变量可以被看作是一种 “程度较轻的 synchronized”;与 synchronized 块相比,volatile 变量所需的编码较少,并且运行时开销也较少,但是它所能实现的功能也仅是 synchronized 的一部分。
      锁提供了两种主要特性:互斥(mutual exclusion) 和可见性(visibility)。互斥即一次只允许一个线程持有某个特定的锁,因此可使用该特性实现对共享数据的协调访问协议,这样,一次就只有一个线程能够使用该共享数据。可见性要更加复杂一些,它必须确保释放锁之前对共享数据做出的更改对于随后获得该锁的另一个线程是可见的 —— 如果没有同步机制提供的这种可见性保证,线程看到的共享变量可能是修改前的值或不一致的值,这将引发许多严重问题。

     synchronized属于重量级锁,效率低下,因为监视器锁(monitor)是依赖于底层的操作系统的Mutex Lock来实现的,而操作系统实现线程之间的切换时需要从用户态转换到核心态,这个状态之间的转换需要相对比较长的时间,时间成本相对较高,这也是为什么早期的synchronized效率低的原因。庆幸的是在Java 6之后Java官方对从JVM层面对synchronized较大优化,所以现在的synchronized锁效率也优化得很不错了,Java 6之后,为了减少获得锁和释放锁所带来的性能消耗,引入了轻量级锁和偏向锁,接下来我们将简单了解一下Java官方在JVM层面对synchronized锁的优化。
     锁的状态总共有四种,无锁状态、偏向锁、轻量级锁和重量级锁。随着锁的竞争,锁可以从偏向锁升级到轻量级锁,再升级的重量级锁,但是锁的升级是单向的,也就是说只能从低到高升级,不会出现锁的降级.读写锁特点:
    1)多个读者可以同时进行读
    2)写者必须互斥(只允许一个写者写,也不能读者写者同时进行)
    3)写者优先于读者(一旦有写者,则后续读者必须等待,唤醒时优先考虑写者)

    互斥锁特点:一次只能一个线程拥有互斥锁,其他线程只有等待
     
    -- 使用同步机制获取互斥锁的情况,进行几点说明:
     1、如果同一个方法内同时有两个或更多线程,则每个线程有自己的局部变量拷贝。
     2、类的每个实例都有自己的对象级别锁。当一个线程访问实例对象中的synchronized同步代码块或同步方法时,该线程便获取了该实例的对象级别锁,其他线程这时如果要访问synchronized同步代码块或同步方法,便需要阻塞等待,直到前面的线程从同步代码块或方法中退出,释放掉了该对象级别锁。
     3、访问同一个类的不同实例对象中的同步代码块,不存在阻塞等待获取对象锁的问题,因为它们获取的是各自实例的对象级别锁,相互之间没有影响。
     4、持有一个对象级别锁不会阻止该线程被交换出来,也不会阻塞其他线程访问同一示例对象中的非synchronized代码。当一个线程A持有一个对象级别锁(即进入了synchronized修饰的代码块或方法中)时,线程也有可能被交换出去,此时线程B有可能获取执行该对象中代码的时间,但它只能执行非同步代码(没有用synchronized修饰),当执行到同步代码时,便会被阻塞,此时可能线程规划器又让A线程运行,A线程继续持有对象级别锁,当A线程退出同步代码时(即释放了对象级别锁),如果B线程此时再运行,便会获得该对象级别锁,从而执行synchronized中的代码。
     5、持有对象级别锁的线程会让其他线程阻塞在所有的synchronized代码外。例如,在一个类中有三个synchronized方法a,b,c,当线程A正在执行一个实例对象M中的方法a时,它便获得了该对象级别锁,那么其他的线程在执行同一实例对象(即对象M)中的代码时,便会在所有的synchronized方法处阻塞,即在方法a,b,c处都要被阻塞,等线程A释放掉对象级别锁时,其他的线程才可以去执行方法a,b或者c中的代码,从而获得该对象级别锁。
     6、使用synchronized(obj)同步语句块,可以获取指定对象上的对象级别锁。obj为对象的引用,如果获取了obj对象上的对象级别锁,在并发访问obj对象时时,便会在其synchronized代码处阻塞等待,直到获取到该obj对象的对象级别锁。当obj为this时,便是获取当前对象的对象级别锁。
     7、类级别锁被特定类的所有示例共享,它用于控制对static成员变量以及static方法的并发访问。具体用法与对象级别锁相似。
     8、互斥是实现同步的一种手段,临界区、互斥量和信号量都是主要的互斥实现方式。synchronized关键字经过编译后,会在同步块的前后分别形成monitorenter和monitorexit这两个字节码指令。根据虚拟机规范的要求,在执行monitorenter指令时,首先要尝试获取对象的锁,如果获得了锁,把锁的计数器加1,相应地,在执行monitorexit指令时会将锁计数器减1,当计数器为0时,锁便被释放了。由于synchronized同步块对同一个线程是可重入的,因此一个线程可以多次获得同一个对象的互斥锁,同样,要释放相应次数的该互斥锁,才能最终释放掉该锁。

    -- 无锁、偏向锁、轻量级锁和重量级锁:
      在JDK 1.6中引入了“偏向锁”和“轻量级锁“。锁一共有四种状态:无锁、偏向锁、轻量级锁和重量级锁。锁只能升级,不能降级。当对锁的竞争加剧的时候,锁会发生升级。
      1.偏向锁
     之所以引入偏向锁,是为了让线程获得锁的代价更低。当一个线程访问同步块并获取锁的时候,会在对象的对象头(对象头包括两部分的信息:一部分是”Mark Word“,主要存放的是哈希码、对象的分代年龄、锁的标记等信息;另一部分是对象的类型指针)和栈帧中的锁记录中存储锁偏向的ID,以后该线程在进入方法的同步块的时候,就检查这个ID(可以理解为一种标记,是一种身份的标识),如果测试成功,表明对象已经获得了锁;如果测试失败,继续测试偏向锁的标识是否设置为1(1的话就是偏向锁),如果没有则使用CAS(Compare And Swap)锁。
      2.轻量级锁
      分为加锁和解锁。当线程执行到同步块之前,JVM会首先检查当前线程的栈帧中创建用于存储记录锁记录的空间,并将对象头中Mark Word复制到锁记录中,也称为Displaced Mark Word,然后线程尝试使用CAS将对象头中的Mark Word替换为指向锁记录的指针。如果成功,则线程获得锁,否则当前线程尝试使用自旋来获取锁。这就是加锁的过程。
      这里多次提到CAS,那么CAS是个什么鬼?CAS是Compare and swap(比较和替换)的简写,具体而言就是:当进行CAS操作的时候,需要输入两个数值,一个是旧值,该旧值是原来的值,另一个是新值,也就是发生改变的值,得到这两个值后,在CAS操作期间会去比较旧值是否发生变化,如果没有发生变化就用新值进行替换,如果发生了变化就不进行替换。
      那么解锁的过程又是怎样的呢?就是使用CAS操作将Displaced Mark Word替换回对象头,如果成功,则表示没有竞争发生。如果失败,表示当前锁存在竞争,锁就会膨胀,膨胀的结果是导致锁的升级,并进入阻塞状态。直到需要释放锁的线程释放锁并唤醒其他等待的线程。

    Java 理论与实践: JDK 5.0 中更灵活、更具可伸缩性的锁定机制-- http://www.ibm.com/developerworks/cn/java/j-jtp10264/index.html
      Java 语言包括了跨线程传达并发性约束的构造 —— synchronized 和 volatile 。把代码块声明为 synchronized,有两个重要后果,通常是指该代码具有 原子性(atomicity)和 可见性(visibility)。
      Condition是配合Lock使用的,而wait/notify是配合synchronized使用的。比较两种实现方式,其实就是比较Lock和synchronized两种同步机制的区别。 ReentrantLock 和 synchronized 的可伸缩性.

    -- 可重入代码, 重入锁ReetrantLock
      可重入代码又称为“纯代码”,是一种允许多个进程访问的代码,因此,可重入代码是一种不允许任何进程对它进行修改的代码 。为了能修改,访问纯代码的
    进程,把执行中可能改变的部分拷贝到该数据区,只需对该数据区中的内容进行修改,并不去改变共享的代码,这时的可共享代码即成为可重入码。
      synchronized一般都是配合wait()和notify()一起使用的;通过重入锁 的newCondition()创建一个Condition, Condition 的await()和signal()
    并发编程都是离不开 同步的,而同步最简单的莫过于synchronized关键字了;ReentrantLock重入锁
    Condition condition = lock.newCondition(); condition.await(); condition.signal();
    ReentrantLock lock = new ReentrantLock();lock.lock();lock.unlock();lock.tryLock() lock.tryLock(10, TimeUnit.SECONDS);

    若一个程序或子程序可以安全的被并行执行,则称其为可重入(reentrant或re-entrant)的;即,当该子程序正在运行时,可以再次进入并执行它。若一个函数是可重入的,则该函数:
     不能含有静态(全局)非常量数据。
     不能返回静态(全局)非常量数据的地址。
     只能处理由调用者提供的数据。
     不能依赖于单实例模式资源的锁。
     不能调用不可重入的函数。

      多用户/对象/进程优先级'以及多进程一般会使得对可重入代码的控制变得复杂。同时,IO代码通常不是可重入的,因为他们依赖于像磁盘这样共享的、单独的资源。
      要提供可重入性代码方法是:编写的函数都只会影响到局部变量,而不能改变全局的数据结构。这样的函数都被称作可重入函数。但是一个可重入内核不仅有这些可重入函数。Linux内核都是可重入的。由于在访问I/O等共享资源,内核还要有不可重入函数。此时,Linux内核就要使用锁机制,来保证在某一时间内只有一个进程可执行该不可重入代码,也就是分时共享的办法,来实现可重入的内核。

      重入锁ReetrantLock,JDK 1.5新增的类,实现了Lock接口,作用与synchronized关键字相当,但比synchronized更加灵活。synchronized在等待获取锁时是不可中的。
       AQS,AbstractQueuedSynchronizer又称为队列同步器(后面简称AQS),它是用来构建锁或其他同步组件的基础框架,内部通过一个int类型的成员变量state来控制同步状态,当state=0时,则说明没有任何线程占有共享资源的锁,当state=1时,则说明有线程目前正在使用共享变量,其他线程必须加入同步队列进行等待,AQS内部通过内部类Node构成FIFO的同步队列来完成线程获取锁的排队工作,同时利用内部类ConditionObject构建等待队列,当Condition调用wait()方法后,线程将会加入等待队列中,而当Condition调用signal()方法后,线程将从等待队列转移动同步队列中进行锁竞争。注意这里涉及到两种队列,一种的同步队列,当线程请求锁而等待的后将加入同步队列等待,而另一种则是等待队列(可有多个),通过Condition调用await()方法释放锁后,将加入等待队列。
      AQS作为基础组件,对于锁的实现存在两种不同的模式,即共享模式(如Semaphore)和独占模式(如ReetrantLock),无论是共享模式还是独占模式的实现类,其内部都是基于AQS实现的,也都维持着一个虚拟的同步队列,当请求锁的线程超过现有模式的限制时,会将线程包装成Node结点并将线程当前必要的信息存储到node结点中,然后加入同步队列等会获取锁,而这系列操作都有AQS协助我们完成,这也是作为基础组件的原因,无论是Semaphore还是ReetrantLock,其内部绝大多数方法都是间接调用AQS完成的。从设计模式角度来看,AQS采用的模板模式的方式构建的,其内部除了提供并发操作核心方法以及同步队列操作外,还提供了一些模板方法让子类自己实现,如加锁操作以及解锁操作。
      在JDK 1.6之后,虚拟机对于synchronized关键字进行整体优化后,在性能上synchronized与ReentrantLock已没有明显差距,因此在使用选择上,需要根据场景而定,大部分情况下我们依然建议是synchronized关键字,原因之一是使用方便语义清晰,二是性能上虚拟机已为我们自动优化。而ReentrantLock提供了多样化的同步特性,如超时获取锁、可以被中断获取锁(synchronized的同步是不能中断的)、等待唤醒机制的多个条件变量(Condition)等,因此当我们确实需要使用到这些功能是,可以选择ReentrantLock.
     

    展开全文
  • JAVA线程同步与互斥

    2015-11-23 22:25:49
    1、什么是同步互斥同步一般有互斥一起讨论。在道程序设计的操作系统中,由于存在并发执行(个进程抢占一处理机的使用权),所以各个进程间的存在资源共享和相互合作的问题。而同步就是进程间的直接制约...
  • Java多线程[3]:线程同步互斥

    千次阅读 2015-09-30 16:26:27
    当两个或个线程需要访问共享资源时,它们需要以某种方式确保每次只有一个线程使用资源,实现这一目的的过程称为线程同步Java线程同步提供了很好的支持。 监视器的概念常用来解决线程同步问题。监视器是用做...
  • JAVA多线程机制之同步与互斥

    千次阅读 2016-01-04 20:12:40
    一个多线程的程序,两个或者多个线程可能需要访问同一个数据资源。这时就必须考虑数据安全的问题,需要线程互斥或者同步。线程的互斥当多个线程需要访问同一资源时,要求在一个时间段内只能允许一个线程来操作共享...
  • 同步是指在线程并发访问共享数据时,保证共享数据在同一时刻只被一个线程使用。互斥是实现同步的一种方式,临界区、互斥量和信号量都是主要的互斥实现方法。 1.sychronized  在java中实现互斥同步的最基本...
  • 下面小编就为大家带来一篇浅谈Java多线程实现及同步互斥通讯。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 所谓同步,是指在不同进程之间的若干程序片断,它们的运行必须严格按照规定的某种先后次序来运行,这种先后次序依赖于要完成的特定的任务。如果用对资源的访问来定义的...java学习中多线程同步互斥有哪些实现方法...
  • Java 多线程(一)线程间的互斥同步通信 Java 多线程(二)同步线程分组问题 Java 多线程(三)线程池入门 Callable 和 Future Java 多线程(四)ThreadPoolExecutor 线程池各参数的意义 Java 多线程(五)...
  • java多线程同步互斥处理中,synchronized是别不可少的东西.但是如果不清晰的编程的话,也就是乱用synchronized的话,死锁的发生就是离你很近的话题了.在前面的一个维护中就发现一个死锁的bug.究其原因是开发者没有...
  • Java线程并发、互斥与同步

    千次阅读 2018-01-03 11:23:08
    网络上对于线程的解析总是天花龙凤的,给你灌输一大堆概念,考研、...下面不取网站复制粘贴,在讲解自己的Java线程并发、互斥与同步之前先给大家解构操作系统书中那些给出书者为了出书者而写的废话到底是什么意思。
  • java 多线程同步与线程间通信

    千次阅读 2019-07-17 11:25:52
    java多线程同步和通信的方法有如下几种: 1、synchronized关键字修饰方法或代码段,实现数据的互斥访问 2、volatile修饰变量,实现多线程环境下数据的同步 3、ReentrantLock可重入锁,实现数据的互斥访问 3、...
  • Java线程并发、互斥与同步

    千次阅读 2015-01-17 09:22:15
    下面不取网站复制粘贴,在讲解自己的Java线程并发、互斥与同步之前先给大家解构操作系统书中那些给出书者为了出书者而写的废话到底是什么意思。 大神们如果只想看程序,可以自行跳过,反正我的文章从来新手向,不喜...
  • (1) 同步方法 修饰对象实例方法 e.g. [code="java"] class A{ private final byte[] INSTANCE_LOCK=new byte[0]; synchronized void instanceMethod(){ } } A a1 = new A(); A a2 = ne...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 72,277
精华内容 28,910
关键字:

java多线程同步与互斥

java 订阅