zoukankan      html  css  js  c++  java
  • 线程系列3--Java线程同步通信技术

           上一篇文章我们讲解了线程间的互斥技术,使用关键字synchronize来实现线程间的互斥技术。根据不同的业务情况,我们可以选择某一种互斥的方法来实现线程间的互斥调用。例如:自定义对象实现互斥(synchronize("自定义对象"){}),同一个类实例对象(synchronize(this){}),类的字节码对象(synchronize(字节码对象){})。这三种方法均可实现线程间的互斥,我们实际运用中灵活使用。

    下面进入今天的正题:线程--线程间的同步通信技术;我们还是以传智播客中的代码为例来讲解,子线程运行10次,主线程运行10次,如此交替运行50次。

    首先看不适用同步技术时的问题代码:

    public static void main(String[] args) {
    	new Thread(new Runnable() {
    		@Override
    		public void run() {
    			for(int i=1;i<=50;i++){
    				for(int j=1;j<=10;j++){
    					System.out.println("子线程:"+Thread.currentThread().getName()+"运行第"+i+"次,重复第"+j+"次");
    				}
    			}
    		}
    	}).start();
    		
    	for(int i=1;i<=50;i++){
    		for(int j=1;j<=10;j++){
    			System.out.println("主线程:"+Thread.currentThread().getName()+"运行第"+i+"次,重复第"+j+"次");
    		}
    	}
    }
    

    上面代码的运行结果可知,子线程与主线程间是杂乱无章的运行,显然不能满足我的要求。那我们来稍作调整。代码如下:

    public class ThreadSynchronizedTechnology {
    
    	public static void main(String[] args) {
    		new Thread(new Runnable() {
    			@Override
    			public void run() {
    				for(int i=1;i<=50;i++){
                           //使用类的字节码作为互斥对象 synchronized(ThreadSynchronizedTechnology.class){ for(int j=1;j<=10;j++){ System.out.println("子线程:"+Thread.currentThread().getName()+"运行第"+i+"次,重复第"+j+"次"); } } } } }).start(); for(int i=1;i<=50;i++){ synchronized(ThreadSynchronizedTechnology.class){ for(int j=1;j<=10;j++){ System.out.println("主线程:"+Thread.currentThread().getName()+"运行第"+i+"次,重复第"+j+"次"); } } } } }

    上面的代码我们使用类的字节码作为互斥对象,显然程序不再是杂乱无章的运行,子线程与主线程都能完整的运行完,但是没有实现我们要求的交替运行,不要急我接着调整,我非常喜欢张孝祥老师循序渐进的讲课方式(我不是托,是发自内心的),下面我们接着调整:

    public class ThreadSynchronizedTechnology {
    	public static void main(String[] args) {
    		final Songzl song = new Songzl();
    		new Thread(new Runnable() {
    			@Override
    			public void run() {
    				for(int i=1;i<=50;i++){
    					song.sub(i);
    				}
    			}
    		}).start();
    		
    		for(int i=1;i<=50;i++){
    			song.main(i);
    		}
    	}
    }
    class Songzl{
    	//子线程运行的方法
    	public void sub(int i){
    		synchronized(Songzl.class){
    			for(int j=1;j<=10;j++){
    				System.out.println("子线程:"+Thread.currentThread().getName()+"运行第"+i+"次,重复第"+j+"次");
    			}
    		}
    	}
    	//主线程运行的方法
    	public void main(int i){
    		synchronized(Songzl.class){
    			for(int j=1;j<=10;j++){
    				System.out.println("主线程:"+Thread.currentThread().getName()+"运行第"+i+"次,重复第"+j+"次");
    			}
    		}
    	}
    }
    

    打印结果依然不是交替运行,我调成这样是为了体现编程的面向对象思想,将相关联的方法封装到同一个类中,方便代码运维。我们接着调整代码:

    public class ThreadSynchronizedTechnology {
    	public static void main(String[] args) {
    		final Songzl song = new Songzl();
    		new Thread(new Runnable() {
    			@Override
    			public void run() {
    				for(int i=1;i<=50;i++){
    					song.sub(i);
    				}
    			}
    		}).start();
    		
    		for(int i=1;i<=50;i++){
    			song.main(i);
    		}
    	}
    }
    class Songzl{
    	//实现线程同步通信,互斥的方法共享次变量
    	private boolean jiaoti = true;
    	//子线程运行的方法:同一个类中方法互斥,类似与synchronized(this){}
    	public synchronized void sub(int i) {
    		if(!jiaoti){
    			try {
    				this.wait();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    		for(int j=1;j<=10;j++){
    			System.out.println("子线程:"+Thread.currentThread().getName()+"运行第"+i+"次,重复第"+j+"次");
    		}
    		jiaoti = false;
    		this.notify();
    	}
    	//主线程运行的方法:同一个类中方法互斥,类似与synchronized(this){}
    	public synchronized void main(int i){
    		if(jiaoti){
    			try {
    				this.wait();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    		for(int j=1;j<=10;j++){
    			System.out.println("主线程:"+Thread.currentThread().getName()+"运行第"+i+"次,重复第"+j+"次");
    		}
    		jiaoti = true;
    		this.notify();
    	}
    }
    

    打印结果可见,已经实现子线程和主线程有条不紊的交替运行,线程间既能互斥,同时又可以相互同步通信运行;线程的互斥是通过synchronized实现的,线程间的同步通信是两个线程间共同持有一个变量来实现的。但是线程有一个“假唤醒”的情况,虽然发生率低,但是我们不能忽略,继续调整代码:

    public class ThreadSynchronizedTechnology {
    	public static void main(String[] args) {
    		final Songzl song = new Songzl();
    		new Thread(new Runnable() {
    			@Override
    			public void run() {
    				for(int i=1;i<=50;i++){
    					song.sub(i);
    				}
    			}
    		}).start();
    		
    		for(int i=1;i<=50;i++){
    			song.main(i);
    		}
    	}
    }
    class Songzl{
    	//实现线程同步通信,互斥的方法共享次变量
    	private boolean jiaoti = true;
    	//子线程运行的方法:同一个类中方法互斥,类似与synchronized(this){}
    	public synchronized void sub(int i) {
    		//避免线程的假唤醒
    		while(!jiaoti){
    			try {
    				this.wait();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    		for(int j=1;j<=10;j++){
    			System.out.println("子线程:"+Thread.currentThread().getName()+"运行第"+i+"次,重复第"+j+"次");
    		}
    		jiaoti = false;
    		this.notify();
    	}
    	//主线程运行的方法:同一个类中方法互斥,类似与synchronized(this){}
    	public synchronized void main(int i){
    		//避免线程的假唤醒
    		while(jiaoti){
    			try {
    				this.wait();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    		for(int j=1;j<=10;j++){
    			System.out.println("主线程:"+Thread.currentThread().getName()+"运行第"+i+"次,重复第"+j+"次");
    		}
    		jiaoti = true;
    		this.notify();
    	}
    }

    我们使用while循环来避免这种假唤醒的情况,当CPU任性的给与不该执行的线程、或者线程神经病的自己唤醒自己,我们可以使用while循环来避免上述情况。好了到此为止,代码已经完全满足我们的需求了。通过上面代码的循序渐进,我们很容易理解线程间的同步与互斥技术。

    总结:线程之间的制约关系?

           当线程并发执行时,由于资源共享和线程协作,使用线程之间会存在以下两种制约关系。

         (1)间接相互制约。一个系统中的多个线程必然要共享某种系统资源,如共享CPU,共享I/O设备,所谓间接相互制约即源于这种资源共享,打印机就是最好的例子,线程A在使用打印机时,其它线程都要等待。

         (2)直接相互制约。这种制约主要是因为线程之间的合作,如有线程A将计算结果提供给线程B作进一步处理,那么线程B在线程A将数据送达之前都将处于阻塞状态。

           间接相互制约可以称为互斥,直接相互制约可以称为同步,对于互斥可以这样理解,线程A和线程B互斥访问某个资源则它们之间就会产个顺序问题——要么线程A等待线程B操作完毕,要么线程B等待线程操作完毕,这其实就是线程的同步了。因此同步包括互斥,互斥其实是一种特殊的同步

  • 相关阅读:
    【UVa 1592】Database
    【UVa 400】Unix ls
    【UVa 136】Ugly Numbers
    【UVa 540】Team Queue
    【Uva 12096】The SetStack Computer
    【POJ 1050】To the Max
    【UVa 156】Ananagrams
    【UVa 10815】Andy's First Dictionary
    [HNOI/AHOI2018]转盘
    CF46F Hercule Poirot Problem
  • 原文地址:https://www.cnblogs.com/aoshicangqiong/p/7707288.html
Copyright © 2011-2022 走看看