精华内容
下载资源
问答
  • 通过JAVA多线程同步和互斥的技术实现CSMA/CD协议的模拟
  • 本文全文以售票系统为例,简诉了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线程同步互斥

    2021-03-17 17:53:21
    本例将模仿经典的线程同步互斥例子——生产者消费者问题,来演示  java 强大的多线程机制。生产者消费者共享一个数据,当数据为0 时,消费者不可访问,生产者可访问数据,每次访问数据加1;当数据到达100 时,...
  • 所谓同步,是指在不同进程之间的若干程序片断,它们的运行必须严格按照规定的某种先后次序来运行,这种先后次序依赖于要完成的特定的任务。如果用对资源的访问来定义的...java学习中多线程同步和互斥有哪些实现方法...

    所谓同步,是指在不同进程之间的若干程序片断,它们的运行必须严格按照规定的某种先后次序来运行,这种先后次序依赖于要完成的特定的任务。如果用对资源的访问来定义的话,同步是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源。java学习中多线程同步和互斥有哪些实现方法

    所谓互斥,是指散布在不同进程之间的若干程序片断,当某个进程运行其中一个程序片段时,其它进程就不能运行它们之中的任一程序片段,只能等到该进程运行完这个程序片段后才可以运行。如果用对资源的访问来定义的话,互斥某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。

    当我们有多个线程要同时访问一个变量或对象时,如果这些线程中既有读又有写操作时,就会导致变量值或对象的状态出现混乱,从而导致程序异常。因此多线程同步就是要解决这个问题。线程同步是指线程之间所具有的一种制约关系,一个线程的执行依赖另一个线程的消息,当它没有得到另一个线程的消息时应等待,直到消息到达时才被唤醒。

    线程互斥是指对于共享的进程系统资源,在各单个线程访问时的排它性。当有若干个线程都要使用某一共享资源时,任何时刻最多只允许一个线程去使用,其它要使用该资源的线程必须等待,直到占用资源者释放该资源。线程互斥可以看成是一种特殊的线程同步。

    线程间的同步方法大体可分为两类:用户模式和内核模式。顾名思义,内核模式就是指利用系统内核对象的单一性来进行同步,使用时需要切换内核态与用户态,而用户模式就是不需要切换到内核态,只在用户态完成操作。

    用户模式下的方法有:原子操作(例如一个单一的全局变量),临界区。内核模式下的方法有:事件,信号量,互斥量。

    展开全文
  • 操作系统实验 多线程同步互斥 java编写 可动态创建
  • Java多线程[3]:线程同步互斥

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

    什么是线程同步

    当两个或多个线程需要访问共享资源时,它们需要以某种方式确保每次只有一个线程使用资源,实现这一目的的过程称为线程同步。Java为线程同步提供了很好的支持。

    监视器的概念常用来解决线程同步问题。监视器是用做互斥锁的对象。在任何时刻,只有一个线程可以拥有监视器。当线程取得锁时,也就进入了监视器。其它所有企图进入加锁监视器的线程都会被挂起,直到第一个线程退出监视器。

    在Java中,可以使用两种方法来实现线程之间的同步,这两种方法都会用到synchronized关键字。

    1. 同步方法
    2. 同步语句块

    不使用线程同步导致的问题

    首先我们先给出一个应该使用线程同步而没有使用的应用场景。
    首先,我们定义一个工具类叫PrintUtil,用来实现打印输出的功能

    public class PrintUtil {
            public static void Print(String content) {
            System.out.print("[" + content);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("]");
        }
    }

    然后定义一个类NewThread,实现Runnable接口,这个类在run方法中调用了PrintUtil.Print()方法。

    public class NewThread implements Runnable {
    
        Thread t;
        String message;
    
        public NewThread(String msg) {
            this.message = msg;
            t = new Thread(this, "my test thread");
            t.start();
        }
    
        @Override
        public void run() {
            PrintUtil.Print(message);
        }
    }

    下面是main方法。

    public class Program {
    
        public static void main(String[] args) {
            NewThread obj1= new NewThread("hello");
            NewThread obj2= new NewThread("java");
            NewThread obj3= new NewThread("servlet");
            try {
                obj1.t.join();
                obj2.t.join();
                obj3.t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    在main方法中,实例化了三个NewThread对象,也就启动了三个线程,每个线程的run方法都调用了PrintUtil.Print() 方法。由于没有做线程同步的处理,输出的结果如下:

    [hello[java[servlet]
    ]
    ]

    很明显,这不是我们想要的结果

    使用同步方法来实现线程同步

    我们必须要限制每次只能由一个线程的run方法来调用Print()方法,并且当该方法执行完毕之后下一个线程才能调用。在Java中,想做到这个是非常容易的,只需要在PrintUtil.Print() 方法前面加上一个关键字synchronized就能轻松搞定。修改后的PrintUtil类的代码如下

    public class PrintUtil {
        synchronized public static void Print(String content) {
            System.out.print("[" + content);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("]");
        }
    }
    

    其它的地方无需修改。重新运行程序,结果如下:

    [hello]
    [servlet]
    [java]

    输出的顺序可能会不一样,但左右括号都是完整的。

    使用同步语句块来实现线程同步

    虽然在类中创建synchronized方法是一种比较容易并且行之有效的实现线程同步的方法,但是,它并不能适用于所有场景。例如:假设PrintUtil类不是由你设计的,你也不能修改它,肿么办?同步语句块诞生了!
    首先,我们将PrintUtil.Print() 方法前面的synchronized关键字去掉,然后假装我们没有权限修改这个类。然后,我们来修改NewThread的run方法,修改后的NewThread类的代码如下

    public class NewThread implements Runnable {
    
        Thread t;
        String message;
        //这个对象其实没有卵用,只是为了对它进行加锁,因为这个对象对于多个线程来说是同一个引用
        static Object obj = new Object();
        public NewThread(String msg) {
            this.message = msg;
            t = new Thread(this, "my test thread");
            t.start();
        }
    
        @Override
        public void run() {     
            synchronized (obj) {
                PrintUtil.Print(message);
            }
        }
    }
    

    重新运行代码,输出的结果是

    [hello]
    [servlet]
    [java]

    下面说一下synchronized关键字后面的括号中的对象。这个对象对于多个线程来说,必须是同一个引用,通常情况下是类外部的一个对象。如果在类内部的话,必须声明为静态的。总之,它不能属于当前线程对象,要么属于外部,要么属于当前类(静态)。

    展开全文
  • 同步是指在线程并发访问共享数据时,保证共享数据在同一时刻只被一个线程使用。互斥是实现同步的一种方式,临界区、互斥信号量都是主要的互斥实现方法。 1.sychronized  在java中实现互斥同步的最基本...

    1.互斥同步方式实现线程安全

            互斥同步是常见的一种保证并发正确性的手段。同步是指在多个线程并发访问共享数据时,保证共享数据在同一时刻只被一个线程使用。互斥是实现同步的一种方式,临界区、互斥量和信号量都是主要的互斥实现方法。

    1.sychronized

           在java中实现互斥同步的最基本的方法是sychronized关键字,这个关键字经过编译过后,会在同步代码块前后分别形成monitorenter和moniterexit俩个字节码指令。这俩个字节码都需要一个reference类型来指明要锁定和解锁的对象。如果程序中sychronized明确指定了对象参数,那就是这个对象的reference,如果没有明确指定,那就根据synchronized关键字修饰的是类的实例方法还是类方法,去取对应的对象实例或Class对象作为锁对象。

          根据JVM规范要求,在执行monitorernter指令的时候,首先要获得对象的锁。如果这个对象没有被锁定,或者当前线程已经获得了那个对象的锁,把锁的计数器加一,如果线程在执行monitorexit指令的时候会将锁计数器减1.当计数器为0的时候,锁就会被释放。如果线程获取对象锁失败,那么当前线程就要阻塞,直到对象锁被另一个线程被释放为止。

    2.ReentrantLock

         除了sycnchronized关键字之外,还可以使用java.utils.concurrent包中的重入锁(ReentrantLock)来实现线程同步,在基本用法上ReentrantLock与Sycnchronized相似。都具有一定可重入的特性。只是语法上不同。ReentrantLock具有以下几点高级特性:

    • 等待可中断:是指持有锁的线程长时间不释放的时候,正在等待的线程可以选择放弃等待,改为处理其他的事情,可中断特性对处理长时间的同步代码块有较大的帮助。
    • 公平锁:多个线程等待同一个锁的时,必须按照申请加锁的时间顺序来依次获得锁;而非公平锁不能保证这一点,在锁被释放的时候任何一个线程都有机会获得锁。synchronized中的锁是非公平的。ReentrantLock默认也是非公平的,但是可以通过带boolean值的构造参数来要求使用公平锁。
    • 锁绑定多个条件:一个ReentrantLock对象可以同时绑定多个Condition对象,而synchronized中,锁对象的wait()和notify()或者notifyAll()方法可以实现一个 隐含的条件,如果需要多余一个条件的时候,就不得不额外的添加一个锁,而ReentrantLock则无需这样做,只需要多次调用newCondition()方法就行。


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

    千次阅读 2017-09-13 00:55:06
    同步:用关键字synchronized给针对共享资源的操作(方法或代码块)加锁,这把锁就叫作互斥锁。所以有两种加锁方式,一种是同步方法,一种是同步代码块。如图: 特别提示:锁住的不是方法,也不是代码块,而是对象,...
  • 下面小编就为大家带来一篇浅谈Java多线程实现及同步互斥通讯。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • Thread安全线程安全不是指数据本身或者数据传输中的安全,而主要是指在高并发多线程的访问过程中,多线程对数据本身的读写所造成的数据不一致、脏数据等情况的避免...在多线程运行过程中,需要保持各个线程之间的同步
  • Java 多线程(一)线程间的互斥和同步通信 Java 多线程(二)同步线程分组问题 Java 多线程(三)线程池入门 Callable 和 Future Java 多线程(四)ThreadPoolExecutor 线程池各参数的意义 Java 多线程(五)...
  • 本文主要讲述了操作系统中同步和互斥这两个概念,并说明了操作系统中是如何实现同步和互斥的。除此之外,本文还重点讲述了线程和进程的概念。
  • 线程的同步与互斥(同步线程与异步线程,线程同步和异步问题) Java 虚拟机中的同步(Synchronization)基于进入退出管程(Monitor)对象实现, 无论是显式同步(有明确的 monitorenter monitorexit 指令,即同...
  • 线程同步是指线程之间所具有的一种制约关系,一个线程的执行依赖另外一个线程的消息,当它没有得到另一个线程的消息时应等待,直到消息到达时才被唤醒。...线程互斥可以看成是一种特殊的线程同步。  线程间的...
  • java多线程同步互斥处理中,synchronized是别不可少的东西.但是如果不清晰的编程的话,也就是乱用synchronized的话,死锁的发生就是离你很近的话题了.在前面的一个维护中就发现一个死锁的bug.究其原因是开发者没有...
  • JAVA多线程机制之同步互斥

    千次阅读 2016-01-04 20:12:40
    一个多线程的程序,两个或者多个线程可能需要访问同一个数据资源。这时就必须考虑数据安全的问题,需要线程互斥或者同步。线程的互斥当多个线程需要访问同一资源时,要求在一个时间段内只能允许一个线程来操作共享...
  • JAVA线程同步互斥

    2015-11-23 22:25:49
    道程序设计的操作系统中,由于存在并发执行(个进程抢占一处理机的使用权),所以各个进程间的存在资源共享相互合作的问题。而同步就是进程间的直接制约问题,互斥是申请临界资源进程间的间接制约问题。 2...
  • (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 订阅