精华内容
下载资源
问答
  • 2018-12-01 23:56:31

    当使用多线程访问同一个资源的时候,非常容易出现线程安全的问题(例如,当多个线程同时对一个数据进行修改的时候,会导致某些线程对数据的修改丢失)。

    因此,需要采用同步机制来解决这种问题。而Java主要提供了三种实现同步机制的方法。今天我们就来认识一下~~

    一、synchronized关键字

    在Java语言中,每个对象都有一个对象锁与之相关联,该锁表明对象在任何时候只允许被一个线程锁拥有,当一个线程调用对象的一段synchronized代码时,需要先获取这个锁,然后去执行相应的代码,执行结束之后,释放锁。

    而synchronized关键字主要有两种用法:synchronized方法synchronized块。此外,这个关键字还可以作用于静态方法、类或者某个实例,但这都对程序的效率有很大的影响

    1.synchronized方法。在方法的声明前主要有synchronized关键字,示例如下:

    public synchronized void mutiThreadAccess();

    只要把多个线程对类需要被同步的资源的操作放到mutiThreadAccess()方法中,就能保证这个方法在同一时刻只能被同一个线程访问,从而保证了多线程访问的安全性。然而,当一个方法的方法体规模非常大时,把该方法声明为synchronized会大大影响程序的执行效率。为了提高程序的效率,Java提供了synchronized块。

    2.synchronized块

    synchronized块既可以把任意的代码段声明为synchronized,也可以指定上锁的对象,有非常高的灵活性。其用法如下:

    synchronized(syncObject){
    
    //访问synchObject的代码
    
    }

    二、wait()方法与notify()方法

    当使用synchronized来修饰某个共享资源时,如果线程A1在执行synchronized代码,另一个线程A2也要同时执行同一个对象的同一synchronized代码时,线程A2将要等到线程A1执行完成之后,才能继续执行。在这种情况下可以使用wait()方法和notify()方法。

    在synchronized代码被执行期间,线程可以调用对象的wait()方法,释放对象锁,进入等待状态,并且可以调用notify()方法或者notifyAll()方法通知正在等待的其他线程。notify()方法仅唤醒一个线程(即等待队列中的第一个线程),并允许它去获得锁,notifyAll()方法唤醒所有等待这个对象的线程并允许他们去获得锁,但并不是让所有唤醒线程都去获取到锁,而是让他们去竞争。

    三、Lock

    JDK5新增加了Lock接口以及它的一个实现类ReentrantLock(重入锁),Lock也可以用来实现多线程的同步。具体而言,它提供了如下一些方法来实现多线程的同步:

    1.lock()。

    这个方法以阻塞的方式获取锁,也就是说,如果获取到锁了,立即返回;如果别的线程持有锁,当前线程就等待,知道获取到锁之后返回。

    2.tryLock()。

    这个方法与lock()方法不同,它以非阻塞的方式来获取锁。此外,它只是常识性地去获取一下锁,如果获取到了锁,立即返回true,否则立即返回false。

    3.tryLock(long timeout,TimeUnit unit)

    如果获取到锁,立即返回true,否则会等待参数给定的时间单元,在等待的过程中,如果获取到了锁,就返回true,如果等待超时则返回false。

    4.lockInterruptibly()

    如果获取了锁,立即返回;如果没有获取到锁,当前线程处于休眠状态,直到获取到锁,或者当前线程被别的线程中断(会受到InterruptedException异常)。它与lock()方法最大的区别在于如果lock()方法获取不到锁就会一直处于阻塞状态,而且还会忽略interrupt()方法。

    示例如下:

    package javatest;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class LockTest {
    
    	public static void main(String[] args)throws InterruptedException {
    		// TODO Auto-generated method stub
    		final Lock lock=new ReentrantLock();
    		lock.lock();
    		Thread t1=new Thread(new Runnable() {
    			public void run() {
    				try {
    					lock.lockInterruptibly();
    				}catch(InterruptedException e)
    				{
    					System.out.println("interrupted");
    				}
    			}
    		});
    		t1.start();
    		t1.interrupt();
    		Thread.sleep(1);
    	}
    }
    

    运行结果如下: 

    如果把lock.lockInterruptibly()替换为lock.lock(),编译器将会提示lock.lock()catch代码块无效,这是因为lock.lock()不会抛出异常,由此可见lock()方法会忽略interrupt()引发的异常。

     

    好啦,以上就是实现Java多线程同步的三种方法的相关总结,如果大家有什么更具体的发现或者发现文中有描述错误的地方,欢迎留言评论,我们一起学习呀~~

     

    Biu~~~~~~~~~~~~~~~~~~~~宫å´éªé¾ç«è¡¨æå|é¾ç«gifå¾è¡¨æåä¸è½½å¾ç~~~~~~~~~~~~~~~~~~~~~~pia!

    更多相关内容
  • 多线程之间如何实现同步

    万次阅读 2019-07-01 11:56:53
    线程安全问题一般是发生再多线程环境,当多个线程同时共享一个全局变量或静态变量做写的操作时候,可能会发生数据冲突问题,也就是线程安全问题,在读的操作不会发生数据冲突问题 下面看个简单的买票例子 案例:需求...

    一、为什么会有线程安全问题?

    线程安全问题一般是发生再多线程环境,当多个线程同时共享一个全局变量或静态变量做写的操作时候,可能会发生数据冲突问题,也就是线程安全问题,在读的操作不会发生数据冲突问题 下面看个简单的买票例子 案例:需求现在有100张火车票,有两个窗口同时抢火车票,请使用多线程模拟抢票效果。 代码:

    public class ThreadTrain1 implements Runnable { private int count = 100;
    
    @Override
    public void run() {
    	while (count > 0) {
    		try {
    			Thread.sleep(50);
    		} catch (Exception e) {
    			// TODO: handle exception
    		}
    		sale();
    	}
    }
    
    public void sale() {
    
    		System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - count + 1) + "票");
    		count--;
    	 }
    }
    
    
    }
    
    public class ThreadDemo { public static void main(String[] args) { ThreadTrain1 threadTrain1 = new ThreadTrain1(); Thread t1 = new Thread(threadTrain1, "①号窗口"); Thread t2 = new Thread(threadTrain1, "②号窗口"); t1.start(); t2.start(); } } 运行结果
    

    在这里插入图片描述

    我们可以发现一号窗口和二号窗口会卖出重复或者超卖现象,这就是在多线程环境下共享资源造成的线程安全问题

    (想自学习编程的小伙伴请搜索圈T社区,更多行业相关资讯更有行业相关免费视频教程。完全免费哦!)

    二、如何解决线程安全问题

    Synchronized(/'sɪŋkrənaɪzd/) ------相当于自动挡 Lock(/lɒk/ )—jdk1.5并发包才又 ------相当于手动挡

    1. 如何解决多线程之间线程安全问题? 答:使用多线程之间同步synchronized或使用锁(lock)。
    2. 为什么使用线程同步或使用锁能解决线程安全问题呢? 答:将可能会发生数据冲突问题(线程不安全问题),只能让当前一个线程进行执行。代码执行完成后释放锁,让后才能让其他线程进行执行。这样的话就可以解决线程不安全问题。
    3. 什么是多线程之间同步? 答:当多个线程共享同一个资源,不会受到其他线程的干扰。
    4. 什么地方需要考虑枷锁? 答:真正产生共享同一个全局变量的时候。
    5. 锁是在什么时候释放的? 答:代码执行完毕或者是程序抛出异常,都会把锁释放掉

    三、同步

    3.1、什么是同步代码块? 答:就是将可能会发生线程安全问题的代码,给包括起来。 synchronized(同一个数据){ 可能会发生线程冲突问题 } 就是同步代码块 synchronized(对象) { //这个对象可以为任意对象 需要被同步的代码 } 对象如同锁,持有锁的线程可以在同步中执行 没持有锁的线程即使获取CPU的执行权,也进不去 同步的前提:

    1. 必须要有两个或者两个以上的线程
    2. 必须是多个线程使用同一个锁 必须保证同步中只能有一个线程在运行 好处:解决了多线程的安全问题 弊端:多个线程需要判断锁,较为消耗资源、抢锁的资源。 原理:有一个线程已经拿到了锁,其他线程已经有了cpu执行权,一直排队等待其他线程释放锁。 代码样例: private static Object oj = new Object(); public void sale() { // 前提 多线程进行使用、多个线程只能拿到一把锁。 // 保证只能让一个线程 在执行 缺点效率降低 synchronized (oj) { if (count > 0) { System.out.println(Thread.currentThread().getName() + “,出售第” + (100 - count + 1) + “票”); count–; } } } 4、同步函数 4.1、什么是同步函数? 答:在方法上修饰synchronized 称为同步函数 代码样例: public synchronized void sale() { if (trainCount > 0) { try { Thread.sleep(40); } catch (Exception e) { } System.out.println(Thread.currentThread().getName() + “,出售 第” + (100 - trainCount + 1) + “张票.”); trainCount–; } } 同学们思考问题?同步函数用的是什么锁? 答:同步函数使用this锁。 证明方式: 一个线程使用同步代码块(this明锁),另一个线程使用同步函数。如果两个线程抢票不能实现同步,那么会出现数据错误。 代码:
    package com.itmayiedu;
    class ThreadTrain2 implements Runnable { private int count = 100; public boolean flag = true; private static Object oj = new Object();
    
    @Override
    public void run() {
    	if (flag) {
    
    		while (count > 0) {
    
    			synchronized (this) {
    				if (count > 0) {
    					try {
    						Thread.sleep(50);
    					} catch (Exception e) {
    						// TODO: handle exception
    					}
    					System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - count + 1) + "票");
    					count--;
    				}
    			}
    
    		}
    
    	} else {
    		while (count > 0) {
    			sale();
    		}
    	}
    
    }
    
    public synchronized void sale() {
    	// 前提 多线程进行使用、多个线程只能拿到一把锁。
    	// 保证只能让一个线程 在执行 缺点效率降低
    	// synchronized (oj) {
    	if (count > 0) {
    		try {
    			Thread.sleep(50);
    		} catch (Exception e) {
    			// TODO: handle exception
    		}
    		System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - count + 1) + "票");
    		count--;
    	}
    	// }
    }
    
    }
    
    public class ThreadDemo2 { public static void main(String[] args) throws InterruptedException { ThreadTrain2 threadTrain1 = new ThreadTrain2(); Thread t1 = new Thread(threadTrain1, "①号窗口"); Thread t2 = new Thread(threadTrain1, "②号窗口"); t1.start(); Thread.sleep(40); threadTrain1.flag = false; t2.start(); } } 
    

    5、静态同步函数 5.1、什么是静态同步函数?

    1. 方法上加上static(/'stætɪk/)关键字,使用synchronized 关键字修饰 或者使用类.class文件。
    2. 静态的同步函数使用的锁是 该函数所属字节码文件对象
    3. 可以用 getClass方法获取,也可以用当前 类名.class 表示。 代码: synchronized (ThreadTrain.class) { System.out.println(Thread.currentThread().getName() + “,出售 第” + (100 - trainCount + 1) + “张票.”); trainCount–; try { Thread.sleep(100); } catch (Exception e) { } } 总结: synchronized 修饰方法使用锁是当前this锁。 synchronized 修饰静态方法使用锁是当前类的字节码文件 。 多线程死锁 3.1、什么是多线程死锁 答:同步中嵌套同步,导致锁无法释放 代码:
     package com.itmayiedu;
    class ThreadTrain6 implements Runnable { // 这是货票总票数,多个线程会同时共享资源 private int trainCount = 100; public boolean flag = true; private Object mutex = new Object();
    
    @Override
    public void run() {
    	if (flag) {
    		while (true) {
    			synchronized (mutex) {
    				// 锁(同步代码块)在什么时候释放? 代码执行完, 自动释放锁.
    				// 如果flag为true 先拿到 obj锁,在拿到this 锁、 才能执行。
    				// 如果flag为false先拿到this,在拿到obj锁,才能执行。
    				// 死锁解决办法:不要在同步中嵌套同步。
    				sale();
    			}
    		}
    	} else {
    		while (true) {
    			sale();
    		}
    	}
    }
    
    /**
     * 
     * @methodDesc: 功能描述:(出售火车票)
     */
    public synchronized void sale() {
    	synchronized (mutex) {
    		if (trainCount > 0) {
    			try {
    				Thread.sleep(40);
    			} catch (Exception e) {
    
    			}
    			System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - trainCount + 1) + "张票.");
    			trainCount--;
    		}
    	}
    }
    
    }
    
    public class DeadlockThread {
    
    public static void main(String[] args) throws InterruptedException {
    
    	ThreadTrain6 threadTrain = new ThreadTrain6(); // 定义 一个实例
    	Thread thread1 = new Thread(threadTrain, "一号窗口");
    	Thread thread2 = new Thread(threadTrain, "二号窗口");
    	thread1.start();
    	Thread.sleep(40);
    	threadTrain.flag = false;
    	thread2.start();
    }
    
    }
    

    4、多线程有三大特性

    1. 原子性 一个操作或者多个操作要么全部执行要么就都不执行。
    2. 可见性 私有内存修改能及时同步到主内存,保证私有和主的可见一致性
    3. 有序性 程序执行的顺序按照代码的先后顺序执行。
      4.1、什么是原子性 即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。 一个很经典的例子就是银行账户转账问题: 比如从账户A向账户B转1000元,那么必然包括2个操作:从账户A减去1000元,往账户B加上1000元。这2个操作必须要具备原子性才能保证不出现一些意外的问题。 我们操作数据也是如此,比如i = i+1;其中就包括,读取i的值,计算i,写入i。这行代码在Java中是不具备原子性的,则多线程运行肯定会出问题,所以也需要我们使用同步和lock这些东西来确保这个特性了。 原子性其实就是保证数据一致、线程安全一部分,

    4.2、什么是可见性 当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。 若两个线程在不同的cpu,那么线程1改变了i的值还没刷新到主存,线程2又使用了i,那么这个i值肯定还是之前的,线程1对变量的修改线程没看到这就是可见性问题。

    4.3、什么是有序性 程序执行的顺序按照代码的先后顺序执行。 一般来说处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。如下: int a = 10; //语句1 int r = 2; //语句2 a = a + 3; //语句3 r = a*a; //语句4 则因为重排序,他还可能执行顺序为 2-1-3-4,1-3-2-4 但绝不可能 2-1-4-3,因为这打破了依赖关系。 显然重排序对单线程运行是不会有任何问题,而多线程就不一定了,所以我们在多线程编程时就得考虑这个问题了。

    五、java内存模型

    共享内存模型指的就是Java内存模型(简称JMM),JMM决定一个线程对共享变量的写入时,能对另一个线程可见。从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。
    在这里插入图片描述

    从上图来看,线程A与线程B之间如要通信的话,必须要经历下面2个步骤:

    1. 首先,线程A把本地内存A中更新过的共享变量刷新到主内存中去。
    2. 然后,线程B到主内存中去读取线程A之前已更新过的共享变量。 下面通过示意图来说明这两个步骤:
      在这里插入图片描述
      如上图所示,本地内存A和B有主内存中共享变量x的副本。假设初始时,这三个内存中的x值都为0。线程A在执行时,把更新后的x值(假设值为1)临时存放在自己的本地内存A中。当线程A和线程B需要通信时,线程A首先会把自己本地内存中修改后的x值刷新到主内存中,此时主内存中的x值变为了1。随后,线程B到主内存中去读取线程A更新后的x值,此时线程B的本地内存的x值也变为了1。 从整体来看,这两个步骤实质上是线程A在向线程B发送消息,而且这个通信过程必须要经过主内存。JMM通过控制主内存与每个线程的本地内存之间的交互,来为java程序员提供内存可见性保证。 总结:什么是Java内存模型:java内存模型简称jmm,定义了一个线程对另一个线程可见。共享变量存放在主内存中,每个线程都有自己的本地内存,当多个线程同时访问一个数据的时候,可能本地内存没有及时刷新到主内存,所以就会发生线程安全问题。

    六、volatile实现线程可见性

    Volatile 关键字的作用是变量在多个线程之间可见,但不保证原子性,下面的文章链接讲的非常好,这里不详细说了 juejin.im/post/5afd22…

    六、AtomicInteger原子类

    AtomicInteger是一个提供原子操作的Integer类,通过线程安全的方式操作加减。下面是个例子可以运行对比下count和atomicInteger的结果会发现无论运行多少次,atomicInteger的结果都是正确的 package com;

    import java.util.concurrent.atomic.AtomicInteger;
    
    public class VolatileNoAtomic extends Thread { static int count = 0; private static AtomicInteger atomicInteger = new AtomicInteger(0);
    
    @Override
    public  void  run() {
    	for (int i = 0; i < 1000; i++) {
    		//等同于i++
    		atomicInteger.incrementAndGet();
    		count++;
    	}
    	System.out.println(atomicInteger+","+count);
    }
    
    public static void main(String[] args) {
    	// 初始化10个线程
    	VolatileNoAtomic[] volatileNoAtomic = new VolatileNoAtomic[10];
    	for (int i = 0; i < 10; i++) {
    		// 创建
    		volatileNoAtomic[i] = new VolatileNoAtomic();
    	}
    	for (int i = 0; i < volatileNoAtomic.length; i++) {
    		volatileNoAtomic[i].start();
    	}
    	} 
    }
    

    上面只是简单的一个原子类的介绍

    七、volatile(/'vɒlətaɪl/)与synchronized(/'sɪŋkrənaɪzd/)区别

    仅靠volatile不能保证线程的安全性。(原子性) ①volatile轻量级,只能修饰变量。synchronized重量级,还可修饰方法 ②volatile只能保证数据的可见性,不能用来同步,因为多个线程并发访问volatile修饰的变量不会阻塞。 synchronized不仅保证可见性,而且还保证原子性,因为,只有获得了锁的线程才能进入临界区,从而保证临界区中的所有语句都全部执行。多个线程争抢synchronized锁对象时,会出现阻塞。 线程安全性 线程安全性包括两个方面,①可见性。②原子性。 从上面自增的例子中可以看出:仅仅使用volatile并不能保证线程安全性。而synchronized则可实现线程的安全性。 八、ThreadLocal深度解析

    展开全文
  • Java 多线程 —— 同步代码块(解决线程安全问题)

    千次阅读 多人点赞 2021-10-23 19:59:26
    目录火车站抢票问题同步代码块同步方法(this锁)同步方法,在public的后面加上synchronized关键字this锁静态同步方法 火车站抢票问题 由于现实中买票也不会是零延迟的,为了真实性加入了延迟机制,也就是线程休眠...

    火车站抢票问题

    由于现实中买票也不会是零延迟的,为了真实性加入了延迟机制,也就是线程休眠语句

    package test.MyThread.ticketDemo;
    
    public class RunnableThread implements Runnable{
        private int ticket = 100;
        @Override
        public void run(){
            while(true){
                if(ticket>0){
                    try {
                        Thread.sleep(100);  //语句一
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
    
                    System.out.println(Thread.currentThread().getName()+"正在出售第 "+ticket+" 张票");  //语句二
                    ticket--;  //语句三
                }
            }
        }
    }
    
    
    package test.MyThread.ticketDemo;
    
    public class ticketDemo1 {
        public static void main(String[] args) {
            RunnableThread r1 = new RunnableThread();
    
            Thread t1 = new Thread(r1,"窗口一");
            Thread t2 = new Thread(r1,"窗口二");
            Thread t3 = new Thread(r1,"窗口三");
    
            t1.start();
            t2.start();
            t3.start();
    
        }
    }
    
    

    在这里插入图片描述
    但是结果和我们想象中的不一样,三个窗口卖出了同样的票

    这是因为,CPU的操作具有原子性,单独执行一条指令或者说语句,在执行完毕前不会被中断。
    三个线程被启动后,都会处于就绪状态,然后开始抢夺CPU执行语句。

    1. 语句一:Thread.sleep(100);
    2. 语句二: System.out.println(Thread.currentThread().getName()+“正在出售第 “+ticket+” 张票”);
    3. 语句三: ticket–;

    我将程序中需要执行的三条主要语句列了出来
    三条线程中,加入线程一先抢到了CPU,这时就会开始执行语句,也就是至少会完成一条语句一,然后进入休眠。

    注:如果语句一不是休眠语句,而是别的语句,那么线程一就可以继续往下执行,因为原子性,正在执行的语句不会被打断,所以只会在一条语句结束,下一条语句未开始时,被抢走CPU或者中断,导致线程退出运行状态,转为就绪或者阻塞状态。所以线程一可以一次性完成多条语句,也有可能刚完成一条语句就被抢走了CPU。

    接着,线程二,线程三也抢到了CPU,也开始执行语句一,然后也进入休眠状态。之后线程一二三从休眠中醒来,开始争抢CPU完成语句二,但是三者都在完成语句三之前被抢走了CPU,导致一直没有执行ticket–语句,ticket也就没有减少,因此三条线程一共打印三条输出语句,里面的ticket都是相同。

    然后三条线程又开始争抢CPU来完成语句三,一个线程让ticket减一,三个线程减少三张票。完成语句三后,又开始新的循环,三个线程开始争抢CPU完成语句一。

    因此,看到的结果会是,三条语句的ticket都相同,然后ticket突然减三,接着又输出三条ticket相同的输出语句。

    那么,该如何解决这种情况呢?
    这种延迟卖票的问题被称为线程安全问题,要发生线程安全问题需要满足三个条件(任何一共条件不满足都不会造成线程安全问题):

    1. 是否存在多线程环境
    2. 是否存在共享数据/共享变量
    3. 是否有多条语句操作着共享数据/共享变量

    火车站延迟卖票问题满足这三个条件,因此造成了线程安全问题,而前两条都不可避免,那么就可以着手于破坏掉第三个条件,让线程安全问题不成立。

    思路是将多条语句包装成一个同步代码块,当某个线程执行这个同步代码块的时候,就跟原子性一样,其他的线程不能抢占CPU,只能等这个同步代码块执行完毕。

    解决办法:

    1. synchronized —— 自动锁
    2. lock —— 手动锁

    synchronized

    synchronized(对象){  
    	//可能会发生线程安全问题的代码
    }
    //这里的对象可以是任意对象,我们可以用 Object obj =  new Object()里面的obj放入括号中
    

    使用synchronized的条件:

    1. 必须有两个或两个以上的线程
    2. 同一时间只有一个线程能够执行同步代码块
    3. 多个线程想要同步时,必须共用同一把锁
      synchronized(对象)括号里面的对象就是一把锁

    使用synchronized的过程:

    1. 只有抢到锁的线程才可以执行同步代码块,其余的线程即使抢到了CPU执行权,也只能等待,等待锁的释放。
    2. 代码执行完毕或者程序抛出异常都会释放锁,然后还未执行同步代码块的线程争抢锁,谁抢到谁就能运行同步代码块。

    同步代码块

    因此,修改后的代码为:

    package test.MyThread.ticketDemo;
    
    public class RunnableThread implements Runnable{
        private int ticket = 100;
        Object obj = new Object();
        @Override
        public void run(){
            while(true){
                synchronized (obj) {
                    if (ticket > 0) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
    
                        System.out.println(Thread.currentThread().getName() + "正在出售第 " + ticket + " 张票");
                        ticket--;
                    }
                }
            }
        }
    }
    
    
    package test.MyThread.ticketDemo;
    
    public class ticketDemo1 {
        public static void main(String[] args) {
        //这里没有改动,只是在上一个代码中加了一把锁
            RunnableThread r1 = new RunnableThread();
    
            Thread t1 = new Thread(r1,"窗口一");
            Thread t2 = new Thread(r1,"窗口二");
            Thread t3 = new Thread(r1,"窗口三");
    
            t1.start();
            t2.start();
            t3.start();
    
        }
    }
    
    

    在这里插入图片描述
    可以看出来结果符合我们的预期,是正确的

    现在又有了新的问题,那就是如果我在构造线程的RunnableThread类里面加入方法呢?同步代码块里面出现方法时,我们应该怎么“上锁”呢?

    同步方法(this锁)

    同步方法,在public的后面加上synchronized关键字
    package test.MyThread.ticketDemo;
    
    public class RunnableThread1 implements Runnable{
        private int ticket = 100;
        Object obj = new Object();
        public boolean flag = true;
        @Override
        public void run(){
            if(flag==true){
                while(ticket>0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    SellTicket1();
                }
            }
        }
    
    //同步方法,在public的后面加上synchronized关键字
        public synchronized void SellTicket1(){
            if(ticket>0){
                System.out.println(Thread.currentThread().getName()+"正在出售第 "+ticket+" 张票");
                ticket--;
            }
        }
    }
    
    package test.MyThread.ticketDemo;
    
    public class ticketDemo2 {
        public static void main(String[] args) throws InterruptedException {
            RunnableThread1 r = new RunnableThread1();
    
            Thread t1 = new Thread(r,"窗口一");
            Thread t2 = new Thread(r,"窗口二");
    
            t1.start();
            t2.start();
    
        }
    }
    
    
    this锁

    先来看看,如果有两条路径,一条路径是使用同步代码块,但是对象是obj,另一条路径是使用同步方法

    package test.MyThread.ticketDemo;
    
    public class TicketWindow2 implements Runnable{
        //定义100张票
        private static int tickets = 100;
        Object obj = new Object();
        int i =0;
        @Override
        public void run() {
            while (true){
                if(i%2==0){
                    synchronized (obj){
                        if(tickets>0){
                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            System.out.println(Thread.currentThread().getName()+" 正在出售第 "+(tickets--)+" 张票");
    
                        }
                    }
                }else {
                    sellTicket();
                }
    
                i++;
            }
    
        }
        
        public synchronized void sellTicket(){
                if(tickets>0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+" 正在出售第 "+(tickets--)+" 张票");
                }
        }
    }
    
    

    在这里插入图片描述
    结果出错,说明同步方法的用的对象锁不能是任意的对象,不同的线程应该用相同的锁。同步方法是属于对象,而在这个类里面调用方法的是this对象,也就是this.sellTicket(),因此把this提取出来作为对象锁中的对象。这样多个线程都用的是this锁

    package test.MyThread.ticketDemo;
    
    public class TicketWindow2 implements Runnable{
        //定义100张票
        private static int tickets = 100;
        Object obj = new Object();
        int i =0;
    
        @Override
        public void run() {
            while (true){
                if(i%2==0){
                    synchronized (this){
                        if(tickets>0){
                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            System.out.println(Thread.currentThread().getName()+" 正在出售第 "+(tickets--)+" 张票");
    
                        }
                    }
                }else {
                    sellTicket();
                }
    
                i++;
            }
    
        }
    
        public synchronized void sellTicket(){
                if(tickets>0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+" 正在出售第 "+(tickets--)+" 张票");
                }
        }
    }
    
    

    修改完成后再运行代码,发现没有错误

    注:

    1. 一个线程使用同步方法,另一个线程使用同步代码块this锁,可以实现同步
    2. 一个线程使用同步方法,另一个线程使用同步代码块,但是不是this锁。这种情况不能实现同步。

    静态同步方法

    同步方法的锁对象是this,
    静态同步方法的锁对象是:这个静态同步方法所属的类的字节码文件

    下面代码挺长的,但其实就修改了上面同步方法的代码的两处地方

    1. public synchronized void sellTicket(){}改为
      public synchronized static void sellTicket(){}
    2. synchronized (this){}改为synchronized (TicketWindow2.class){}
    package test.MyThread.ticketDemo;
    
    public class TicketWindow2 implements Runnable{
        //定义100张票
        private static int tickets = 100;
        Object obj = new Object();
        int i =0;
    
        @Override
        public void run() {
            while (true){
                if(i%2==0){
                    synchronized (TicketWindow2.class){
                        if(tickets>0){
                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            System.out.println(Thread.currentThread().getName()+" 正在出售第 "+(tickets--)+" 张票");
    
                        }
                    }
                }else {
                    sellTicket();
                }
                i++;
            }
    
        }
    
        public synchronized static void sellTicket(){
            if(tickets>0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+" 正在出售第 "+(tickets--)+" 张票");
            }
        }
    }
    
    

    main()方法里面创建进程和启动进程的代码,和上面同步方法里面的代码相同
    结果也和上面的一样,都不再列出来了

    死锁问题

    package test.MyThread.ticketDemo;
    //两个不同的锁对象
    public class LockObject {
        public static final Object lock1 = new Object();
        public static final Object lock2 = new Object();
    }
    
    
    package test.MyThread.ticketDemo;
    
    public class DieLockThread extends Thread{
        public boolean flag;
    
        public DieLockThread(boolean flag){
            this.flag = flag;
        }
        @Override
        public void run() {
            if(flag){
                synchronized(LockObject.lock1){
                    System.out.println("lock1");
                    synchronized(LockObject.lock2){
                        System.out.println("lock2");
                    }
                }
            }else{
                synchronized(LockObject.lock2){
                    System.out.println("lock2");
                    synchronized(LockObject.lock1){
                        System.out.println("lock1");
                    }
                }
            }
    
        }
    }
    
    
    package test.MyThread.ticketDemo;
    
    public class DieLockDemo {
        public static void main(String[] args) {
            DieLockThread d1 = new DieLockThread(true);
            DieLockThread d2 = new DieLockThread(false);
    
            d1.start();
            d2.start();
        }
    }
    
    

    在这里插入图片描述
    程序会卡在这一步,不能进行下一步也不能停止
    利用有参构造,构造出来的线程d1应该是先获得锁对象LockObject.lock1然后执行打印语句。接着获取锁对象LockObject.lock2,然后打印lock2。
    但是这里因为线程d2是先获取的锁对象LockObject.lock2,并占据这个锁对象,然后想获得锁对象LockObject.lock1,但LockObject.lock1此时被线程d1占据着

    两个线程都在等待对方释放锁对象,然后进行下一步,但是两者都不释放,导致程序卡死在这里。这就造成了死锁。

    lock

    package test.MyThread.ticketDemo;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class LockThread implements Runnable{
        private int ticket = 100;
        Lock lock = new ReentrantLock();
        @Override
        public void run(){
            while(ticket>0){
                try{
                    lock.lock();
                    if(ticket>0){
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + " 正在出售第 " + (ticket--) + " 张票");
                    }
                }finally {
                    lock.unlock();
                }
            }
    
        }
    }
    
    
    package test.MyThread.ticketDemo;
    
    public class LockDemo {
        public static void main(String[] args) {
            LockThread lt = new LockThread();
    
            Thread t1 = new Thread(lt,"窗口一");
            Thread t2 = new Thread(lt,"窗口二");
            Thread t3 = new Thread(lt,"窗口三");
    
            t1.start();
            t2.start();
            t3.start();
        }
    }
    
    

    在这里插入图片描述
    结果正确

    展开全文
  • JUC多线程:AQS抽象队列同步器原理

    千次阅读 2021-10-09 12:58:04
    AQS,Abstract Queued Synchronizer,抽象队列同步器,是 J.U.C 中实现锁及同步组件的基础。工作原理就是如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态,...

    一、AQS 的工作原理:

    1.1、什么是 AQS:

            AQS,Abstract Queued Synchronizer,抽象队列同步器,是 J.U.C 中实现锁及同步组件的基础。工作原理就是如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态,如果被请求的共享资源被占用,那么就将获取不到锁的线程加入到等待队列中。这时,就需要一套线程阻塞等待以及被唤醒时的锁分配机制,而 AQS 是通过 CLH 队列实现锁分配的机制。

    1.2、CLH 同步队列的模型:

            CLH 队列是由内部类 Node 构成的同步队列,是一个双向队列(不存在队列实例,仅存在节点之间的关联关系),将请求共享资源的线程封装成 Node 节点来实现锁的分配;同时利用内部类 ConditionObject 构建等待队列,当调用 ConditionObject 的 await() 方法后,线程将会加入等待队列中,当调用 ConditionObject 的 signal() 方法后,线程将从等待队列转移动同步队列中进行锁竞争。AQS 中只能存在一个同步队列,但可拥有多个等待队列。AQS 的 CLH 同步队列的模型如下图:

            AQS 有三个主要变量,分别是 head、tail、state,其中 head 指向同步队列的头部,注意 head 为空结点,不存储信息。而 tail 则是同步队列的队尾,同步队列采用的是双向链表的结构是为了方便对队列进行查找操作。当 Node 节点被设置为 head 后,其 thread 信息和前驱结点将被清空,因为该线程已获取到同步状态,正在执行了,也就没有必要存储相关信息了,head 只保存后继结点的指针即可,便于 head 结点释放同步状态后唤醒后继结点。

            队列的入队和出队操作都是无锁操作,基于 CAS+自旋锁 实现,AQS 维护了一个 volatile 修饰的 int 类型的 state 同步状态,volatile 保证线程之间的可见性,并通过 CAS 对该同步状态进行原子操作、实现对其值的修改。当 state=0 时,表示没有任何线程占有共享资源的锁,当 state=1 时,则说明当前有线程正在使用共享变量,其他线程必须加入同步队列进行等待;

    二、内部类 Node 数据结构分析:

    static final class Node {
        //共享模式
        static final Node SHARED = new Node();
        //独占模式
        static final Node EXCLUSIVE = null;
    
        //标识线程已处于结束状态
        static final int CANCELLED =  1;
        //等待被唤醒状态
        static final int SIGNAL    = -1;
        //条件状态
        static final int CONDITION = -2;
        //在共享模式中使用表示获得的同步状态会被传播
        static final int PROPAGATE = -3;
    
        //等待状态,存在CANCELLED、SIGNAL、CONDITION、PROPAGATE 4种取值
        volatile int waitStatus;
    
        //同步队列中前驱结点
        volatile Node prev;
        //同步队列中后继结点
        volatile Node next;
        //请求锁的线程
        volatile Thread thread;
        //等待队列中的后继结点,这个与Condition有关,稍后会分析
        Node nextWaiter;
    
        //判断是否为共享模式
        final boolean isShared() {
            return nextWaiter == SHARED;
        }
        
        //.....
    }

            AQS分为两种模式:独占模式 EXCLUSIVE 和 共享模式 SHARED,像 ReentrantLock、CyclicBarrier 是基于独占模式模式实现的,Semaphore,CountDownLatch 等是基于共享模式。

            变量 waitStatus 表示当前封装成 Node 节点的线程的等待状态,共有4种取值 CANCELLED、SIGNAL、CONDITION、PROPAGATE:

    • CANCELLED:值为1,表示在同步队列中的线程等待超时或者被中断,处于已结束状态,需要从同步队列中移除该 Node 节点

    • SIGNAL:值为-1,表示后继结点在等待当前结点唤醒。后继结点入队时,会将前继结点的状态更新为 SIGNAL,当该节点释放了同步锁之后,就会唤醒该节点的后继节点

    • CONDITION:值为-2,与 Condition 相关,表示该结点在 condition 等待队列中阻塞,当其他线程调用了Condition 的 signal() 方法后,CONDITION 状态的结点将从等待队列转移到同步队列中,等待获取同步锁。

    • PROPAGATE:值为-3时,在共享模式下使用,表示该线程以及后继线程进行无条件传播。前继结点不仅会唤醒其后继结点,同时也可能会唤醒后继的后继结点。

    三、AQS 的设计模式:

    3.1、AQS 的模板方法模式:

            AQS 的基于模板方法模式设计的,在 AQS 抽象类中已经实现了线程在等待队列的维护方式(如获取资源失败入队/唤醒出队等),而对于具体共享资源 state 的获取与释放(也就是锁的获取和释放)则交由具体的同步器来实现,具体的同步器需要实现以下几种方法:

    • isHeldExclusively():该线程是否正在独占资源,只有用到 condition 才需要去实现它

    • tryAcquire(int):独占模式,尝试获取资源,成功则返回 true,失败则返回 false

    • tryRelease(int):独占方式,尝试释放资源,成功则返回 true,失败则返回 false

    • tryAcquireShared(int):共享方式,尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源

    • tryReleaseShared(int):共享方式,尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false

    3.2、JUC 中提供的同步器:

    • 闭锁 CountDownLatch:用于让主线程等待一组事件全部发生后继续执行。

    • 栅栏 CyclicBarrier:用于等待其它线程,且会阻塞自己当前线程,所有线程必须全部到达栅栏位置后,才能继续执行;且在所有线程到达栅栏处之后,可以触发执行另外一个预先设置的线程。

    • 信号量 Semaphore:用于控制访问资源的线程个数,常常用于实现资源池,如数据库连接池,线程池。在 Semaphore 中,acquire 方法用于获取资源,有的话,继续执行,没有资源的话将阻塞直到有其它线程调用 release 方法释放资源;

    • 交换器 Exchanger:用于线程之间进行数据交换;当两个线程都到达共同的同步点(都执行到exchanger.exchange 的时刻)时,发生数据交换,否则会等待直到其它线程到达;

    CountDownLatch 和 CyclicBarrier 的区别?

    两者都可以用来表示代码运行到某个点上,二者的区别在于:

    ① CyclicBarrier 的某个线程运行到某个位置之后就停止运行,直到所有的线程都到达了这个点,所有线程才重新运行;CountDownLatch 的某线程运行到某个位置之后,只是给计数值-1而已,该线程继续运行;

    ② CyclicBarrier 可重用,CountDownLatch 不可重用,计数值 为 0 时该 CountDownLatch 就不可再用了。

    推荐阅读:https://juejin.cn/post/6989419875366076447

    3.3、ReentranLock 中独占模式下非公平锁的获取流程:

            获取独占锁的过程是定义在 tryAcquire() 中的,当前线程尝试获取同步状态,如果获取失败,就将线程封装成 Node 节点插入到 CLH 同步队列中。插入同步队列后,线程并没有放弃获取同步状态,而是根据前置节点状态状态判断是否继续获取,如果前置节点是 head 结点,继续尝试获取,否则就将线程挂起。如果成功获取同步状态则将自己设置为 head 结点。当持有同步状态的线程释放资源后,也会唤醒队列中的后继线程。

    四、ConditionObject 阻塞队列:

     4.1、什么是 Condition 接口:

            AQS 的阻塞队列是基于内部类 ConditionObject 实现的,而 ConditionObject 实现了 Condition 接口。那 Condition 接口是什么呢?Condition 主要用于线程的等待和唤醒,在JDK5之前,线程的等待唤醒是用 Object 类的 wait/notify/notifyAll 方法实现的,这些方法必须配合 synchronized 关键字使用,使用起来不是很方便,为了解决这个问题,在 JDK5 之后,J.U.C 提供了Condition。

    • Condition.await 对应于 Object.wait;

    • Condition.signal 对应于 Object.notify;

    • Condition.signalAll 对应于 Object.notifyAll;

            与 synchronized 的等待唤醒机制相比,Condition 能够精细的控制多线程的休眠与唤醒,具备更多的灵活性, 通过多个 Condition 实例对象建立不同的等待队列,从而实现同一个锁拥有多个等待队列。而 synchronized 关键字只能有一组等待唤醒队列,使用 notify() 唤醒线程时只能随机唤醒队列中的一个线程。

    4.2、ConditionObject 阻塞队列实现原理:

            Condition 的具体实现之一是 AQS 的内部类 ConditionObject,每个 Condition 都对应着一个等待队列,也就是说如果一个锁上创建了多个 Condition 对象,那么也就存在多个等待队列。当调用 ConditionObject 的 await() 方法后,线程将会加入等待队列中,当调用 ConditionObject 的 signal() 方法后,线程将从等待队列转移动同步队列中进行锁竞争。AQS 的 ConditionObject 中的等待队列模型如下:

     4.3、AQS 的 线程唤醒机制原理:

     AQS 的线程唤醒是通过 singal() 方法实现的,我们先看下 singal() 方法线程唤醒的流程图:

    流程图说明:

    signal() 方法主要调用了 doSignal(),而 doSignal() 方法中做了两件事:

    • (1)从条件等待队列移除被唤醒的节点,然后重新维护条件等待队列的 firstWaiter 和 lastWaiter 的指向。
    • (2)将从等待队列移除的结点加入同步队列(在 transferForSignal() 方法中完成的),如果进入到同步队列失败并且条件等待队列还有不为空的节点,则继续循环唤醒后续其他结点的线程

    注意:无论是同步队列还是等待队列,使用的 Node 数据结构都是同一个,不过是使用的内部变量不同罢了

    所以 signal() 的流程可以概述为:

    • signal() 被调用后,先判断当前线程是否持有独占锁

    • 如果有,那么唤醒当前 Condition 等待队列的第一个结点的线程,并从等待队列中移除该结点,添加到同步队列中

    • 如果加入同步队列失败,那么继续循环唤醒等待队列中的其他结点的线程

    • 如果成功加入同步队列,那么如果其前驱结点已结束或者设置前驱节点状态为 Node.SIGNAL 状态失败,则通过 LockSupport.unpark() 唤醒被通知节点代表的线程

    到此 signal() 任务完成,被唤醒后的线程,将调用 AQS 的 acquireQueued() 方法加入获取同步状态的竞争中,这就是等待唤醒机制的整个流程实现原理。

    文章总结自:https://blog.csdn.net/javazejian/article/details/75043422

    相关阅读:https://juejin.cn/post/6844903997438951437

    展开全文
  • Java多线程同步优化的6种方案

    万次阅读 2020-06-24 17:44:34
    Java中可以使用锁来解决多线程的同步问题,保障了数据的一致性,但也会代理很多问题,本章总结了多线程同步的几种优化方案:包括读写锁、写时复制机制、锁细化等方案。
  • C#的线程同步

    千次阅读 2022-04-05 12:21:23
    使用线程有几个原因。假设从应用程序中进行网络调用需要一定的时间。...这种程序结构非常适合于其中有一个可识别的任务序列的程序,但程序常常需要同时完成个任务。线程对客户端和服务器端应用程序都非常重要。
  • Java synchronized关键字实现线程同步

    千次阅读 2022-04-02 08:47:30
    synchronized关键字实现线程同步
  • C++多线程并发(二)---线程同步之互斥锁

    万次阅读 多人点赞 2019-03-20 00:08:29
    一、何为线程同步 在前一篇文章《C++多线程并发编程(一)—线程管理》中解释多线程并发时说到两个比较重要的概念: 多线程并发:在同一时间段内交替处理多个操作,线程切换时间片是很短的(一般为毫秒级),一个...
  • C++11 多线程同步

    千次阅读 2016-11-09 21:28:05
    出现数据竞争,一般会用临界区(Critical Section)、互斥量(Mutex)、信号量(Semaphore)、事件(Event)这四种方法来完成线程同步。 1、临界区 对于临界资源,多线程必须互斥地对它进行访问。每个...
  • python的多线程线程同步方式

    千次阅读 2019-07-17 09:37:23
    1.线程执行 join与setDaemon 1.子线程在主线程运行结束后,会继续执行完,如果给子线程设置为守护线程(setDaemon=True),主线程运行结束子线程即结束; 2 .如果join()线程,那么主线程会等待子线程执行完再执行...
  • 线程同步 这里写目录标题线程同步1、并发并发 :2、线程同步机制3、三大不安全案例4、同步方法5、同步块6、CopyOnWriteArrayList7、死锁8、Lock(锁)9、synchronized 与 Lock 的对比 ...处理多线程问题时 , 多个线程访
  • 感觉这种情况下多线程就没用了。 vector是线程安全的。 官方在可能涉及到线程不安全的操作都进行了synchronized操作,相当于官方帮你加了一把同步锁。核心的数组扩容实现和array list相差无几。 1.Vector中的方法是...
  • 多线程间的通信和同步

    千次阅读 多人点赞 2019-06-15 11:21:07
    最近看了很多关于网络编程和多线程的书,为了以后查看相关内容方便,整理了几本书的精华形成这篇博文,希望能帮助观看这篇博文的读者。 目录 一、什么是多线程? 二、为什么要创建线程 三、线程之间如何通信 四...
  • Qt多线程同步

    千次阅读 2018-09-15 12:06:04
    一、Qt中使用多线程时候,多线程同步就是一个不可避免的问题。多线程同步就是使多个线程在同时执行同一段代码的时候,有顺序的执行,不会出现同时有两个或者多个线程执行同一段代码的情况,特别是在对变量或者...
  • Python多线程线程同步

    千次阅读 2019-03-25 23:05:17
    线程同步的真实意思和字面意思恰好相反。 线程同步的真实意思,其实是“排队”:几个线程之间要排队,一个一个对共享资源进行操作,而不是同时进行操作。 Python threading模块提供了Lock/RLock、Condition、queue...
  • Java 多线程同步和异步详解

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

    千次阅读 2020-08-25 17:12:44
    为什么需要线程同步? 当个线程同时运行时,线程的调度由操作系统决定,程序本身无法决定。因此,任何一个线程都有可能在任何指令处被操作系统暂停,然后在某个时间段后继续执行。 这个时候,有个单线程模型不...
  • 第1关:并发编程的三个概念 第2关:使用synchronized关键字同步线程 第3关:使用线程锁(Lock)实现线程同步,使用sleep()函数解决了第三关线程随机导致的需要评测次问题 第3关:使用线程锁(Lock)实现线程同步
  • C#多线程——线程同步

    万次阅读 2018-08-25 13:11:53
    一、为什么要线程同步个线程同时使用共享对象会造成很问题,同步这些线程使得对共享对象的操作能够以正确的顺序执行是非常重要的。 二、实现线程同步的方法: • 使用Mutex类 • 使用SemaphoreSlim类 • ...
  • JAVA多线程——实现同步

    万次阅读 多人点赞 2018-07-26 17:20:33
    java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查), 将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用, 从而保证了...
  • windows系统多线程同步机制原理总结

    千次阅读 2018-12-24 21:24:33
    windows系统多线程同步机制原理总结 同步问题是开发过程中遇到的重要问题之一。同步是要保证在并发执行的环境中各个控制流可以有序地执行,包括对于资源的共享或互斥访问,以及代码功能的逻辑顺序。 为了保证多线程...
  • 进程相当于一个应用程序,线程就是进程中的一个应用场景或者说是一个执行单元,一个进程可以启动线程,每个线程执行不同的任务,一个线程不能单独存在,他必须是进程的一部分,当进程中所有的非守护线程都结束...
  • 万字图解Java多线程

    万次阅读 多人点赞 2020-09-06 14:45:07
    java多线程我个人觉得是javaSe中最难的一部分,我以前也是感觉学会了,但是真正有多线程的需求却不知道怎么下手,实际上还是对多线程这块知识了解不深刻,不知道多线程api的应用场景,不知道多线程的运行流程等等,...
  • java中实现多线程同步方式

    千次阅读 2018-05-22 14:46:21
    多线程实现方式 1)、实现Runnable接口,并实现run()方法 以下是主要步骤: 1、自定义类并实现Runnable接口,并实现run()方法。 2、创建Thread对象,用实现Runnable接口的对象作为参数实例化该Thread对象。 3...
  • ndk开发之多线程同步与通信

    万次阅读 2018-02-13 00:40:33
    线程同步是利用互斥锁(mutex)与条件(condition)变量的结合,经常出现于生产者与消费者模式场景中。先定义相关变量:#include &lt;pthread.h&gt; //生产者线程 pthread_t thread_product; //消费者线...
  • C#多线程同步的几种方法

    千次阅读 2017-03-06 14:37:07
    多线程操作的时候我们知道要避免线程之间共享数据,但是很多时候我们要使用多线程并且还要访问同一块内存的数据,这是我们就必须要使用同步技术,确保一次只有一个线程访问和改变共享状态。 下面我就来说一下同步...
  • linux中实现线程同步的6种方法

    万次阅读 多人点赞 2020-10-22 16:37:21
    linux线程同步的方法 下面是一个线程不安全的例子: #include<stdio.h> #include<pthread.h> int ticket_num=10000000; void *sell_ticket(void *arg) { while(ticket_num>0) { ticket_num--; }...
  • java多线程同步的五种方法

    千次阅读 2017-09-12 10:15:13
    因为当我们有线程要同时访问同一个变量或对象时,如果这些线程中午既有读又有写操作时,就会导致变量值或者对象的状态出现混乱,从而导致程序异常,举个例子:如果同一个银行账户被连个线程操作,一个存钱1000....
  • - 在CyclicBarrier类的内部有一个计数器,每个线程在到达屏障点的时候都会调用await方法将自己阻塞,此时计数器会减1,当计数器减为0的时候所有因调用await方法而被阻塞的线程将被唤醒。这就是实现一组线程相互等待...
  • C++ 11 多线程实例详解

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 865,881
精华内容 346,352
关键字:

多线程下的线程同步