设计一个同步块与同步方法进行同步的例子,如100张车票,有4个窗口同时卖票,编程实现相应的过程

发布时间:2024-05-24 00:17 发布:上海旅游网

问题描述:

要程序 最好有注释 有注释的追加200分 用Java语言

问题解答:

QQ给我 详细谈

把分给我吧,谢谢你了,我不胜感激.
我会祝你好运,身体健康!长命百岁!早生贵子!花开富贵!

这问题……我还是路过。用什么编?实现什么过程

我给您推荐几个关于同步编程设计的解释和讲解~保证无毒 可放心打开~
我相信看了之后对您一定会有用的~

多线程编程指南3--使用同步对象编程

http://blog.csdn.net/eiyaya/archive/2006/04/07/654601.aspx

Java 线程入门——线程的同步 - Java编程 - Java技巧

http://www.newasp.net/tech/java/14989.html

Java中,能通过两种方法使用线程。一是继承Thread类,二是实现Runnable接口。具体怎么使用,请参考相应的资料。该文档主要介绍的是多线程的同步问题。 我们能够创建许许多多的线程,来处理许许多多的事情。但线程的运行是不确定的,我们无法知道哪个线程会先被CPU执行,设置优先权也不能解决,因为CPU只要有一点点地空闲,它都会去执行其他的线程,高优先级线程只是抢资源比较狠一点而已。由于线程的不确定性,使得当多个线程同时对某一对象进行操作的时候,便有可能出现不可预知的情况。举个例子,当某个线程对一个文件进行操作,比如是往文件里写数据,而另一个线程又对该文件进行写数据,那最后这个文件里到底是什么数据呢?由于线程的不确定性,所以我们无法预知结果。怎么解决线程的不确定性呢?嗯,对了,线程的同步(synchronized),能使线程按照你自己的意愿,先处理什么,后处理什么。 在讲线程的同步之前,先讲讲同步线程(需要同步的线程)的几种状态吧。休眠状态、等待状态和执行状态是同步线程所特有的,其他的一些比如新状态、阻塞状态、死亡状态等是所有线程都有的,在这里不再讨论。 休眠状态:所有在同步代码中调用wait方法的线程都会进入该状态,在没有被唤醒或没到达休眠结束时间前,状态是不会改变的。休眠前,线程会自动记录本身的一些状态,比如局部变量的值等(非局部的值是不保存的)。休眠状态的线程会存放在休眠池中,由系统管理。 等待状态:被唤醒的或到达休眠结束时间的休眠线程,和进入同步代码(*1)的但没有得到钥匙(*2)的线程,都会进入等待状态。在没有得到钥匙前,线程的状态是不会改变的。等待状态的线程会存放在等待池中,由系统管理。只有在等待池中的线程,才有资格抢夺钥匙。 运行状态:即正在运行的线程。运行状态的线程,能够调用wait方法,让出钥匙,使自己休眠,进入到休眠池中,并会记录当前的运行状态,以便下次夺取钥匙的时候,能继续执行休眠前的动作。 注解:(*1:同步代码即同步方法或者同步块中的代码) (*2:这里讲到的钥匙,即其他资料中所说的锁,得到锁的线程能够运行同步代码,使线程进入运行状态,本人觉得不太好理解,所以就换成个人觉得比较好理解的钥匙了) 转入正题:先看一下下面的程序代码代码一:public class ThreadTest implements Runnable { public synchronized void run() { for (int i = 0; i < 10; i++) { System.out.print(" " + i); try { Thread.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { Runnable r1 = new ThreadTest(); Runnable r2 = new ThreadTest(); Thread t1 = new Thread(r1); Thread t2 = new Thread(r2); t1.start(); t2.start(); }}运行结果为:0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 代码二:public class ThreadTest implements Runnable { public synchronized void run() { for (int i = 0; i < 10; i++) { System.out.print(" " + i); try { Thread.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { Runnable r = new ThreadTest(); Thread t1 = new Thread(r); Thread t2 = new Thread(r); t1.start(); t2.start(); }}运行结果:0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 两次的运行结果很显然不一样,代码一的同步方法好像没有起到作用,代码二的就起到作用了。为什么了?看清楚没有?对了,就是t1和t2所指向的线程所包含的Runnable对象不一致引起的。代码一中,t1包含的是r1,t2包含的是r2,显然r1和r2不是同一个对象,而同步代码针对的是同一对象,所以在这里不钩成同步。代码二中,t1包含r,t2也是包含r,两个线程处理同一个对象,所以同步方法便起了同步的作用,达到了程序的同步。 嗯,很好!知道了线程的同步是针对同一对象来说的,但为什么会这样?OK,接下来我便讲讲线程同步的机制吧。 再看一下下面的代码。代码三:public class ThreadTest implements Runnable { public synchronized void run() { String name = Thread.currentThread().getName(); System.out.println(name + "抢到了钥匙,开始执行!"); for (int i = 0; i < 10; i++) { System.out.print(i + " "); try { Thread.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("\n" + name + "运行结束,释放了钥匙!"); } public static void main(String[] args) { Runnable r = new ThreadTest(); Thread t1 = new Thread(r, "t1"); Thread t2 = new Thread(r, "t2"); Thread t3 = new Thread(r, "t3"); t1.start(); t2.start(); t3.start(); }} 运行结果:t1抢到了钥匙,开始执行!0 1 2 3 4 5 6 7 8 9 t1运行结束,释放了钥匙!t2抢到了钥匙,开始执行!0 1 2 3 4 5 6 7 8 9 t2运行结束,释放了钥匙!t3抢到了钥匙,开始执行!0 1 2 3 4 5 6 7 8 9 t3运行结束,释放了钥匙! 看到上面的运行结果没?没错,每个对象都有且仅有一把钥匙,只有得到该钥匙的线程能够执行该对象的同步代码,那是不是同步代码也需要钥匙才能执行呢?“不是,非同步的我们不管,你想执行就执行吧,我们只管同步代码”,系统很明确的说。嗯,没错,其实可以这样理解,同步代码都是加了锁的代码,只有得到钥匙后,才能把锁打开,进入到同步代码中,没钥匙的只能在门口吃西北风。当得到钥匙的线程执行完任务后,便把钥匙归还给对象,站在门口的线程一看到钥匙归还了,就开始争夺钥匙,强到钥匙的线程,便能够把锁打开,执行任务了。对照一下上面所讲的线程状态,站在门口(等待池)等的,是等待状态线程,它们等待钥匙,并准备争夺钥匙;得到钥匙的就不用多说了吧,是运行状态的线程;那休眠状态的在哪?没错,它们太懒惰了,没有人叫醒他们,他们都在家(休眠池)里睡觉。 上面都是用同步方法来解释线程的同步,现在开始用另一种同步代码—同步块来解释线程的同步吧。先看下面的代码。 代码四:public class ThreadTest implements Runnable { public void run() { String name = Thread.currentThread().getName(); System.out.println(name + "的同步块之前"); synchronized (this) { System.out.println(name + "的同步块内"); for (int i = 0; i < 10; i++) { System.out.print(i + " "); try { Thread.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } } } System.out.println("\n" + name + "同步块之后"); } public static void main(String[] args) { Runnable r = new ThreadTest(); Thread t1 = new Thread(r, "t1"); Thread t2 = new Thread(r, "t2"); Thread t3 = new Thread(r, "t3"); t1.start(); t2.start(); t3.start(); }} 运行结果:t1的同步块之前t1的同步块内0 t2的同步块之前t3的同步块之前1 2 3 4 5 6 7 8 9 t2的同步块内0 t1同步块之后1 2 3 4 5 6 7 8 9 t3的同步块内0 t2同步块之后1 2 3 4 5 6 7 8 9 t3同步块之后 结果有问题啊,都乱掉了!不,结果没问题,因为同步块的作用就是使同步块里的代码进行同步,同步块外的不管。换句话说,就是同步块把同步块内的代码上锁了,没有得到钥匙的线程不能进入,只能在外面等待。你可能会问,为什么输出第一个0和1之间会输出“t2的同步块之前”,“t3的同步块之前”这两句话,不是进入到同步状态了吗?t1得到了钥匙,t2和t3没有钥匙,为什么能照样执行呢?嗯,问得很好,请再看一下代码,打印的那行(灰底)在同步块之外,因此不受同步的约束。可以这样理解,那行打印的程序并没有上锁,因此并不需要得到钥匙才能执行,所有线程都能自由的执行。在没有遇到关键字synchronized之前,线程会一直运行下去的。当线程运行到关键字synchronized的时候,便会进入到等待池中,然后查看对象的钥匙是否没人使用,是的话就去争夺,夺取钥匙后便能进入上了锁的同步代码中,没有夺到就只能在等待池中等待。 那同步块synchronized后面的this(红底)是什么意思呢?他的意思是:使用哪一个对象的对象管理器,来对同步块进行加锁呢?上述代码使用的是当前对象(this)的对象管理器来对同步块进行加锁,只能有得到当前对象(this)的钥匙才能够解锁,进入到同步块中。一个对象可以有多把锁,但只有一把钥匙,使用了这个对象的锁,就只能使用这个对象的钥匙来开锁。即这里的同步代码由这个对象的锁和钥匙来管理。同步方法没有明确的说明用哪个对象的对象管理器啊?嗯,java规定同步方法都只能由当前对象的对象管理器控制。 上面都明白了的话,就来点提高吧,先看下面代码。 代码五:(为了简便,我把要导入的包和异常处理都删掉了)public class OperatorFile { static Object obj; ReadThread rt; WriteThread wt; String fileName; boolean canRead; OperatorFile(String fileName) { this.fileName = fileName; obj = new Object(); rt = new ReadThread(); wt = new WriteThread(); rt.start(); wt.start(); } public void readFile(String fileName) { synchronized (obj) { while (!canRead){ obj.wait(); //使当前线程进入休眠状态} FileInputStream fis = new FileInputStream(new File(fileName)); byte[] data = new byte[fis.available()]; fis.read(data); System.out.println(new String(data)); fis.close(); } } public void writeFile(String fileName) { synchronized (obj) { FileOutputStream fos = new FileOutputStream(fileName); for (int i = 'a'; i <= 'z'; i++) { fos.write(i); } fos.close(); canRead = true; obj.notify(); //唤醒在休眠池中的任意一个线程 } } class ReadThread extends Thread { public void run() { readFile(fileName); } } class WriteThread extends Thread { public void run() { writeFile(fileName); } } public static void main(String[] args) { new OperatorFile("test.txt"); }} 运行结果:abcdefghijklmnopqrstuvwxyz 常理来说,先调用rt的start方法,应该会先运行readFile去读文件,此时“test.txt”里没有内容,应该是什么也不会打印的,但为什么会先调用wt的shart方法,执行writeFile,把a-z写到文件中,然后再把回到readFile去读文件呢?嗯,你猜得没错,就是因为readFile里有一个wait()方法,此方法使得当前的线程进入到休眠池中休眠,并且释放了obj对象的钥匙,因为同步块使用了obj的对象管理器进行对同步线程的管理。此时在等待池中等待的wt把空闲的钥匙夺了过来,所以能够把锁给打开,进入到同步块中,然后对文件进行写操作。注意,读和写是在不同的同步块中实现的,只要是属于同一对象的同步代码,便会给这些同步代码加上该对象的锁,由此可知,一个对象的锁可以有多个,但一个对象有且仅有一把钥匙。只有得到这把钥匙,才能随心所欲的打开任意一把锁。接下来writeFile把数据都写到文件中了,然后调用notify()方法,唤醒此对象管理器(这里是obj)中的一个线程,便把该线程从休眠池中移动到等待池中,rt线程被唤醒后,发觉钥匙空闲,因此把钥匙夺过来,然后继续上次运行,把文件的数据输出。 代码五有几个值得注意的地方。什么?你看出来了?没错,第一个是synchronized所使用的对象管理器,这段代码是使用obj的对象管理器来管理同步线程的,因此需要在synchronized后面的括号中填上obj。obj是静态的?对,没错,我们的同步线程只能由一个对象来管理,多个对象是管理不了同步线程的,这里定义obj为静态是为了确保obj只有一份,从而达到我们的要求。 第二个要注意的地方是:wait和notify这两个方法,必须是obj的方法,不能是其他对象的方法。你自己可以去改一下,用this的wait和notify方法,一定会有异常产生。在java doc上是这样写的“如果当前的线程不是此对象监视器的所有者”时,调用wait、notify和notifyAll便会产生“IllegalMonitorStateException”异常。说简单一点,使用哪个对象的对象管理器去控制同步线程,就只能使用该对象的wait、notify和notifyAll来控制同步。 第三个要注意的地方是:wait方法外面的while循环。为什么要用循环语句,并且要定义一个标示符来控制循环呢?这样做能够明确的指明所需唤醒的线程。例如现在有三个线程(A、B、C)在休眠,现在我想让在运行态中D线程来唤醒A线程,应该如何实现呢?使用一般的方法是实现不了的,只能按照上面所说的,循环语句结合标示符来实现。看一下下面的代码: 代码六:public class Test { boolean isA; boolean isB; boolean isC; synchronized void doA(){ while(!isA){ wait(); } //do someting...... } synchronized void doB(){ while(!isB){ wait(); } //do someting...... } synchronized void doC(){ while(!isC){ wait(); } //do someting...... } synchronized void doD(){ isA = true; notifyAll(); //do someting...... }} 分析:当执行doD方法的时候,令isA = true,然后唤醒所有的线程。此时doA中,由于isA为true,跳出循环,所以不会执行到wait方法,线程A能够开始执行。而doB和doC中,由于isB和isC还是false,因此进入到循环体中,执行wait方法继续休眠,达到了目的。所以通常情况下都是在循环内做休眠动作,并用一标示符控制线程是否应该被唤醒。 到此结束,可能和官方的解释略有不同,一切以官方的为准。以上是我对线程同步的一点点见解,希望对你们有所帮助,如果有不当之处,望能指出,万分感谢。

热点新闻