多线程同步互斥实例——使用synchronized实现线程通信和互斥

  • 线程互斥概念

线程互斥是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。

  • 实现线程同步互斥的四种方式

临界区(Critical Section):适合一个进程内的多线程访问公共区域或代码段时使用

互斥量 (Mutex):适合不同进程内多线程访问公共区域或代码段时使用,与临界区相似。

事件(Event):通过线程间触发事件实现同步互斥

信号量(Semaphore):与临界区和互斥量不同,可以实现多个线程同时访问公共区域数据,原理与操作系统中PV操作类似,先设置一个访问公共区域的线程最大连接数,每有一个线程访问共享区资源数就减一,直到资源数小于等于零。

  • 实例说明线程同步和互斥

以一道面试题为例:

子线程循环10次,接着主线程循环100次,接着又回到子线程循环10次,接着在回到主线程循环100次,如此循环50次,请写出程序。

解题思路:

由题可知道,需要创建一个程序,然后创建两个线程(new Thread().start();)。在线程中,使用runnable 分别实现循环10次和100次。在将整个程序循环50次。则代码如下:

package cn.itcast.heima2;

public class TraditionalThreadCommuniction {

	public static void main(String[] args) {

		new Thread(new Runnable() {

			@Override
			public void run() {
				for (int j = 1; j <= 50; j++) {<span style="font-family: Arial, Helvetica, sans-serif;">//子程序循环50次</span>
					for (int i = 1; i <= 10; i++) {//执行10次输出子程序
						System.out.println("sub sthread sequece of " + i
								+ ",loop of" + j);

					}
				}
			}
		}).start();
		for (int j = 1; j <= 50; j++) {//主程序循环50次
			for (int i = 1; i <= 100; i++) {//执行100次输出主程序
				System.out.println("main sthread sequece of " + i
						+ ",loop of" + j);
			}
		}

	}

}

执行结果如下,从结果中,我们可以看到,这并不是我们要的结果。

主线程和子线程的执行都被打断了,所以  我们要把中间打印的循环代码保护起来。在这里,我们可以使用锁(synchronized), 实现主线程和子线程互相不被打断。可以使用TraditionalThreadSynchronized.class 。

则第一步改进代码如下:

package cn.itcast.heima2;

public class TraditionalThreadCommuniction {

	public static void main(String[] args) {

		new Thread(new Runnable() {

			@Override
			public void run() {
				for (int j = 1; j <= 50; j++) {
					synchronized (TraditionThradSynchronized.class) {
						for (int i = 1; i <= 10; i++) {
							System.out.println("sub sthread sequece of " + i
									+ ",loop of" + j);
						}
					}
				}
			}
		}).start();
		for (int j = 1; j <= 50; j++) {
			synchronized (TraditionThradSynchronized.class) {
				for (int i = 1; i <= 100; i++) {
					System.out.println("main sthread sequece of " + i
							+ ",loop of" + j);
				}

			}
		}

	}

}

生成结果,虽然结果并非我们所期望的结果,但是,从结果中,我们可以看到,主线程和子线程已经实现了独立的执行,但是,并不能保证子线程和主线程交替出现,这怎么解决呢?

继续改进:

思路:我们可以把有关联的方法,集中到一个类中,这样是不是更好维护和更改呢?这就是高内聚的思想。即把相关联的方法放到同一个类上。所以,我们可以改造我们的类,生成一个类Business,其拥有两个方法,sub和main。则main方法中,放入主线程互斥的那部分代码;sub中放入子线程的互斥部分代码。然后再程序中分别调用这两个方法。

在business中,使用synchronized实现两个方法互斥。然后,在实现两个方法的交替出现。即实现主线程和子线程的互相通信。这样就非常简单,我们可以引入一个内部变量,bshouldSub ,如果bshouldSub是true,则执行子线程,否则等待。如果是flase则执行主线程,否则主线程等待。在执行主线程之后,将bshouldSub设置为true,并唤醒等待程序,子线程执行同理。则程序代码如下:

package cn.itcast.heima2;

public class TraditionalThreadCommunictionn {

	public static void main(String[] args) {

		final Business business= new Business();
		new Thread(new Runnable() {

			@Override
			public void run() {
				for (int j = 1; j <= 50; j++) {
					business.sub(j);
				}
			}
		}).start();
		for (int j = 1; j <= 50; j++) {
			business.main(j);
		}

	}

}

class Business{
	private boolean bshouldSub = true;//子线程和主线程通信信号
	public synchronized void sub(int j){
		if(!bshouldSub){
					try {
					this.wait();
				} catch (InterruptedException e) {

					e.printStackTrace();
				}
		}
			for (int i = 1; i <= 10; i++) {
				System.out.println("sub sthread sequece of " + i
						+ ",loop of" + j);

			}
			bshouldSub = false;//运行结束,设置值为FALSE 让主程序运行
			this.notify();//唤醒等待的程序

	}
	public synchronized void main(int j){
		if(bshouldSub){//如果bshouldsub=true ,等待  让子程序运行
			try {
				this.wait();
			} catch (InterruptedException e) {

				e.printStackTrace();
			}
		}
		for (int i = 1; i <= 100; i++) {
			System.out.println("main sthread sequece of " + i
					+ ",loop of" + j);
		}
		bshouldSub = true;//让子程序运行
		this.notify();//唤醒等待的一个程序
	}

}

由此程序,我们可以看到输出的结果为(由于篇幅限制,这里只截几张图来说明结果):

  • 总结:

要用到共同数据(包括同类锁)或共同算法的若干方法应该归在同一个类身上,这种设计正好体现了高内聚和程序的健壮性。在解决线程的问题时,更多的从面相对象的思想上去思考问题和解决问题,会得到更好的效果。

时间: 2024-09-30 18:43:58

多线程同步互斥实例——使用synchronized实现线程通信和互斥的相关文章

Java多线程-同步:synchronized 和线程通信:生产者消费者模式

大家伙周末愉快,小乐又来给大家献上技术大餐.上次是说到了Java多线程的创建和状态|乐字节,接下来,我们再来接着说Java多线程-同步:synchronized 和线程通信:生产者消费者模式. 一.同步:synchronized 多个线程同时访问一个对象,可能造成非线程安全,数据可能错误,所谓同步:就是控制多个线程同时访就是控制多线程操作同一个对象时,注意是同一个对象,数据的准确性, 确保数据安全,但是加入同步后因为需要等待,所以效率相对低下. 如:一个苹果,自己一个人去咬怎么都不会出问题,但是

多线程同步-主线程等待所有子线程完成案例

有时候我们会遇到这样的问题:做一个大的事情可以被分解为做一系列相似的小的事情,而小的事情无非就是参数上有可能不相同而已! 此时,如果不使用线程,我们势必会浪费非常多的时间来完成整个大的事情,而使用线程的话将会存在这样的问题: 主线程启动所有子线程并发执行后主线程就直接返回了,导致外部函数判读整个大的事情完成了,但是实际上并没有完成! 针对以上情况我想我会采用多线程方式执行同时解决主线程等待子线程的问题.如图: 在这里我使用Java进行案例分析. 首先建立一个线程管理类,用于启动所有子线程和等待所

进程的同步与通信,进程与线程同步的区别,进程与线程通信的区别【转】

本文转载自:http://www.cnblogs.com/youngforever/p/3250270.html 这两天看进程的同步与通信,看了几本书上的介绍,也从网上搜了很多资料,越看越迷惑,被这几个问题搞得很纠结. 进程同步与互斥的区别? 进程的同步方式有哪些? 进程的通信方式有哪些? 进程同步与通信的区别是什么? 线程的同步/通信与进程的同步/通信有区别吗? 在好多教材上(包括国内与国外的)也没有明确这些概念,现在对每个问题还没有准确的答案,下面将自己的理解记下来,以后再补充. 参考资料:

[Java][Android] 多线程同步-主线程等待全部子线程完毕案例

有时候我们会遇到这种问题:做一个大的事情能够被分解为做一系列相似的小的事情,而小的事情无非就是參数上有可能不同样而已! 此时,假设不使用线程,我们势必会浪费许多的时间来完毕整个大的事情.而使用线程的话将会存在这种问题: 主线程启动全部子线程并发运行后主线程就直接返回了,导致外部函数判读整个大的事情完毕了,可是实际上并没有完毕! 针对以上情况我想我会採用多线程方式运行同一时候解决主线程等待子线程的问题.如图: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQ

【python标准库学习】thread,threading(二)多线程同步

继上一篇介绍了python的多线程和基本用法.也说到了python中多线程中的同步锁,这篇就来看看python中的多线程同步问题. 有时候很多个线程同时对一个资源进行修改,这个时候就容易发生错误,看看这个最简单的程序: import thread, time count = 0 def addCount(): global count for i in range(100000): count += 1 for i in range(10): thread.start_new_thread(ad

多线程一共就俩问题:1.线程安全(访问共享数据) 2.线程通信(wait(),notify())

1.线程安全,无非就是加锁,访问共享资源时,synchronized 2.线程通信,就是控制各个线程之间的额执行顺序,线程之间是无法进行通讯的,也是借助于第三方object,object的wait()和notify()通讯 原文地址:https://www.cnblogs.com/panxuejun/p/8416546.html

JAVA多线程提高二:传统线程的互斥与同步&amp;传统线程通信机制

本文主要是回顾线程之间互斥和同步,以及线程之间通信,在最开始没有juc并发包情况下,如何实现的,也就是我们传统的方式如何来实现的,回顾知识是为了后面的提高作准备. 一.线程的互斥 为什么会有线程的互斥?可以想银行取款的问题,如果不做监控,多个人同时针对一个存折取钱的时候就会出现钱不对的问题,下面我们通过两个例子来分析一下线程的互斥问题以及为什么会产生这个线程? 例子1:一个人生产信息,一个人消费信息 面向对象的思想:类 信息类 生产者 消费者 public class TriditionalTh

JAVA之旅(十三)——线程的安全性,synchronized关键字,多线程同步代码块,同步函数,同步函数的锁是this

JAVA之旅(十三)--线程的安全性,synchronized关键字,多线程同步代码块,同步函数,同步函数的锁是this 我们继续上个篇幅接着讲线程的知识点 一.线程的安全性 当我们开启四个窗口(线程)把票陆陆续续的卖完了之后,我们要反思一下,这里面有没有安全隐患呢?在实际情况中,这种事情我们是必须要去考虑安全问题的,那我们模拟一下错误 package com.lgl.hellojava; import javax.security.auth.callback.TextInputCallback

JAVA 并发编程-传统线程互斥技术(Synchronized)(三)

java线程互斥是为了保证,同一时刻最多只有一个线程执行该段代码.那么它的出现又是为了解决什么问题呢?账户存取款,在同一时间段只能让一个人进行操作. 下面来看一个简单实例(多线程带来的问题): public class TraditionalThreadSynchronized { /** * @param args */ public static void main(String[] args) { new TraditionalThreadSynchronized().init(); }