专注于互联网--专注于架构

最新标签
网站地图
文章索引
Rss订阅

首页 »Java教程 » 多线程同步:彻底明白Java的多线程-实现多线程及线程的同步 »正文

多线程同步:彻底明白Java的多线程-实现多线程及线程的同步

来源: 发布时间:星期三, 2008年12月17日 浏览:95次 评论:0
. 实现多线程 1. 虚假多线程 例1: public TestThread { i=0, j=0; public void go( flag){ while(true){ try{ Thread.sleep(100); } catch(InterruptedException e){ .out.prln("Interrupted"); } (flag0) i; .out.prln("i=" + i); } { j; .out.prln("j=" + j); } } } public void (String args){ TestThread.go(0); TestThread.go(1); }} 上面运行结果为: i=1 i=2 i=3 结果将直打印出I我们意图是当在while循环中sleep个线程就将起动打印出j但结果却并不是这样有关sleep为什么不会出现我们预想结果在下面将讲到 2. 实现多线程 通过继承 Thread或实现Runnable接口我们可以实现多线程 2.1 通过继承 Thread实现多线程  Thread中有两个最重要run和start 1) run必须进行覆写把要在多个线程中并行处理代码放到这个 2) 虽然run实现了多个线程并行处理但我们不能直接run而是通过startrunstart时候start会首先进行和多线程相关化(这也是为什么不能直接run原因)然后再run 例2: public TestThread extends Thread{ private threadCount = 0; private threadNum = threadCount; private i = 5; public void run{ while(true){ try{ Thread.sleep(100);  } catch(InterruptedException e){ .out.prln("Interrupted"); } .out.prln("Thread " + threadNum + " = " + i); (--i0) ; } } public void (String args){ for( i=0; i<5; i) TestThread.start; }} 运行结果为: Thread 1 = 5 Thread 2 = 5 Thread 3 = 5 Thread 4 = 5 Thread 5 = 5 Thread 1 = 4 Thread 2 = 4 Thread 3 = 4 Thread 4 = 4 Thread 1 = 3 Thread 2 = 3 Thread 5 = 4 Thread 3 = 3 Thread 4 = 3 Thread 1 = 2 Thread 2 = 2 Thread 5 = 3 Thread 3 = 2 Thread 4 = 2 Thread 1 = 1 Thread 2 = 1 Thread 5 = 2 Thread 3 = 1 Thread 4 = 1 Thread 5 = 1 从结果可见例2能实现多线程并行处理 **:在上面例子中我们只用产生Thread对象并没有用reference来记录所产生Thread对象根据垃圾回收机制个对象没有被reference引用时它将被回收但是垃圾回收机制对Thread对象“不成立”个Thread都会进行注册动作所以即使我们在产生Thread对象时没有指定个reference指向这个对象实际上也会在某个地方有个指向该对象reference所以垃圾回收器无法回收它们 3) 通过Thread子类产生线程对象是区别对象线程 TestSynchronized extends Thread{ public TestSynchronized(String name){ super(name); } public synchronized void prt{ for( i=10; i<20; i){ .out.prln(Thread.currentThread.getName + " : " + i); try{ Thread.sleep(100); } catch(InterruptedException e){ .out.prln("Interrupted"); } } } public synchronized void run{ for( i=0; i<3; i){ .out.prln(Thread.currentThread.getName + " : " + i); try{ Thread.sleep(100); } catch(InterruptedException e){ .out.prln("Interrupted"); } } }}public TestThread{ public void (String args){ TestSynchronized t1 = TestSynchronized("t1"); TestSynchronized t2 = TestSynchronized("t2"); t1.start; t1.start; //(1) //t2.start; (2) }} 运行结果为: t1 : 0 t1 : 1 t1 : 2 t1 : 0 t1 : 1 t1 : 2 由于是同个对象启动区别线程所以run实现了synchronized如果去掉(2)注释把代码(1)注释掉结果将变为: t1 : 0 t2 : 0 t1 : 1 t2 : 1 t1 : 2 t2 : 2 由于t1和t2是两个对象所以它们所启动线程可同时访问run 2.2 通过实现Runnable接口实现多线程 如果有个类它已继承了某个类又想实现多线程那就可以通过实现Runnable接口来实现 1) Runnable接口只有个run 2) 把个实现了Runnable接口对象作为参数产生个Thread对象Thread对象start就可执行并行操作如果在产生个Thread对象时以个Runnable接口实现类对象作为参数那么在startstartRunnable接口实现类中run 例3.1: public TestThread implements Runnable{ private threadCount = 0; private threadNum = threadCount; private i = 5; public void run{ while(true){ try{ Thread.sleep(100); } catch(InterruptedException e){ .out.prln("Interrupted"); } .out.prln("Thread " + threadNum + " = " + i); (--i0) ; } } public void (String args){ for( i=0; i<5; i) Thread( TestThread).start; //(1) }} 运行结果为: Thread 1 = 5 Thread 2 = 5 Thread 3 = 5 Thread 4 = 5 Thread 5 = 5 Thread 1 = 4 Thread 2 = 4 Thread 3 = 4 Thread 4 = 4 Thread 4 = 3 Thread 5 = 4 Thread 1 = 3 Thread 2 = 3 Thread 3 = 3 Thread 4 = 2 Thread 5 = 3 Thread 1 = 2 Thread 2 = 2 Thread 3 = 2 Thread 4 = 1 Thread 5 = 2 Thread 1 = 1 Thread 2 = 1 Thread 3 = 1 Thread 5 = 1 例3是对例2修改它通过实现Runnable接口来实现并行处理代码(1)处可见TestThread中并行操作部分要把个TestThread对象作为参数来产生Thread对象Thread对象start 3) 同个实现了Runnable接口对象作为参数产生所有Thread对象是同对象下线程 例3.2: package mypackage1;public TestThread implements Runnable{ public synchronized void run{ for( i=0; i<5; i){ .out.prln(Thread.currentThread.getName + " : " + i); try{ Thread.sleep(100); } catch(InterruptedException e){ .out.prln("Interrupted"); } } } public void (String args){ TestThread testThread = TestThread; for( i=0; i<5; i) // Thread(testThread, "t" + i).start; (1) Thread( TestThread, "t" + i).start; (2) }} 运行结果为: t0 : 0 t1 : 0 t2 : 0 t3 : 0 t4 : 0 t0 : 1 t1 : 1 t2 : 1 t3 : 1 t4 : 1 t0 : 2 t1 : 2 t2 : 2 t3 : 2 t4 : 2 t0 : 3 t1 : 3 t2 : 3 t3 : 3 t4 : 3 t0 : 4 t1 : 4 t2 : 4 t3 : 4 t4 : 4 由于代码(2)每次都是用个新TestThread对象来产生Thread对象所以产生出来Thread对象是区别对象线程所以所有Thread对象都可同时访问run如果注释掉代码(2)并去掉代码(1)注释结果为: t0 : 0 t0 : 1 t0 : 2 t0 : 3 t0 : 4 t1 : 0 t1 : 1 t1 : 2 t1 : 3 t1 : 4 t2 : 0 t2 : 1 t2 : 2 t2 : 3 t2 : 4 t3 : 0 t3 : 1 t3 : 2 t3 : 3 t3 : 4 t4 : 0 t4 : 1 t4 : 2 t4 : 3 t4 : 4 由于代码(1)中每次都是用同个TestThread对象来产生Thread对象所以产生出来Thread对象是同个对象线程所以实现run同步 2. 共享资源同步 1. 同步必要性 例4: Seq{ private number = 0; private Seq seq = Seq; private Seq {} public Seq getInstance{ seq; } public get{ number;  //(a) number; //(b) }}public TestThread{ public void (String args){ Seq.getInstance.get; //(1) Seq.getInstance.get; //(2) }} 上面是个取得序列号单例模式例子get可能会产生两个相同序列号: 当代码(1)和(2)都试图get取得个唯序列当代码(1)执行完代码(a)正要执行代码(b)时它被中断了并开始执行代码(2)旦当代码(2)执行完(a)而代码(1)还未执行代码(b)那么代码(1)和代码(2)就将得到相同 2. 通过synchronized实现资源同步 2.1 锁标志 2.1.1 每个对象都有个标志锁当对象个线程访问了对象某个synchronized数据(包括)时这个对象就将被“上锁”所以被声明为synchronized数据(包括)都不能被(当前线程取走了对象“锁标志”)只有当前线程访问完它要访问synchronized数据释放“锁标志”后个对象其它线程才能访问synchronized数据 2.1.2 每个也有个“锁标志”对于synchronized 数据(包括)可以在整个下进行锁定避免数据同时访问 例5: Seq{ private number = 0; private Seq seq = Seq; private Seq {} public Seq getInstance{ seq; } public synchronized get{ //(1) number; number; }} 例5在例4基础上把get声明为synchronized那么在同个对象中就只能有个线程get所以每个线程取得number值就是唯 例6: Seq{ private number = 0; private Seq seq = null; private Seq {}synchronized public Seq getInstance{ //(1) (seqnull) seq = Seq; seq; } public synchronized get{ number; number; }} 例6把getInstance声明为synchronized那样就保证通过getInstance得到是同个seq对象 2.2 non-synchronized数据只能在同个对象纯种实现同步访问区别对象线程仍可同时访问 例7: TestSynchronized implements Runnable{ public synchronized void run{ //(1) for( i=0; i<10; i){.out.prln(Thread.currentThread.getName + " : " + i);/*(2)*/ try{ Thread.sleep(100); } catch(InterruptedException e){ .out.prln("Interrupted"); } } }}public TestThread{ public void (String args){ TestSynchronized r1 = TestSynchronized; TestSynchronized r2 = TestSynchronized; Thread t1 = Thread(r1, "t1"); Thread t2 = Thread(r2, "t2"); //(3) //Thread t2 = Thread(r1, "t2"); (4) t1.start; t2.start; }} 运行结果为: t1 : 0 t2 : 0 t1 : 1 t2 : 1 t1 : 2 t2 : 2 t1 : 3 t2 : 3 t1 : 4 t2 : 4 t1 : 5 t2 : 5 t1 : 6 t2 : 6 t1 : 7 t2 : 7 t1 : 8 t2 : 8 t1 : 9 t2 : 9 虽然我们在代码(1)中把run声明为synchronized但由于t1、t2是两个对象(r1、r2)线程而run是non-synchronized数据所以仍可被同时访问(代码(2)中sleep由于在暂停时不会释放“标志锁”线程中循环很难被中断去执行另个线程所以代码(2)只是为了显示结果) 如果把例7中代码(3)注释掉并去年代码(4)注释运行结果将为: t1 : 0 t1 : 1 t1 : 2 t1 : 3 t1 : 4 t1 : 5 t1 : 6 t1 : 7 t1 : 8 t1 : 9 t2 : 0 t2 : 1 t2 : 2 t2 : 3 t2 : 4 t2 : 5 t2 : 6 t2 : 7 t2 : 8 t2 : 9 修改后t1、t2是同个对象(r1)线程所以只有当个线程(t1或t2中个)执行run个线程才能执行 2.3 对象“锁标志”和“锁标志”是相互独立 例8: TestSynchronized extends Thread{ public TestSynchronized(String name){ super(name); } public synchronized void prt{ for( i=10; i<20; i){ .out.prln(Thread.currentThread.getName + " : " + i); try{ Thread.sleep(100); } catch(InterruptedException e){ .out.prln("Interrupted"); } } } public synchronized void run{ for( i=0; i<10; i){ .out.prln(Thread.currentThread.getName + " : " + i); try{ Thread.sleep(100); } catch(InterruptedException e){ .out.prln("Interrupted"); } } }}public TestThread{ public void (String args){ TestSynchronized t1 = TestSynchronized("t1"); TestSynchronized t2 = TestSynchronized("t2"); t1.start; t1.prt; //(1) t2.prt; //(2) }} 运行结果为: : 10 t1 : 0 : 11 t1 : 1 : 12 t1 : 2 : 13 t1 : 3 : 14 t1 : 4 : 15 t1 : 5 : 16 t1 : 6 : 17 t1 : 7 : 18 t1 : 8 : 19 t1 : 9 : 10 : 11 : 12 : 13 : 14 : 15 : 16 : 17 : 18 : 19 在代码(1)中虽然是通过对象t1来prt但由于prt是静态所以它时不用经过任何对象它所属线程为线程 由于run取走是对象锁prt取走所以同个线程t1(由上面可知实际上是区别线程)run且还没完成run它就能prt但prt只能被个线程如代码(1)和代码(2)即使是两个区别对象也不能同时prt 3. 同步优化 1) synchronized block 语法为:synchronized(reference){ do this } reference用来指定“以某个对象锁标志”对“大括号内代码”实施同步控制 例9: TestSynchronized implements Runnable{ j = 0; public synchronized void run{for( i=0; i<5; i){ //(1) .out.prln(Thread.currentThread.getName + " : " + j); try{ Thread.sleep(100); } catch(InterruptedException e){ .out.prln("Interrupted"); } } } } public TestThread{ public void (String args){ TestSynchronized r1 = TestSynchronized; TestSynchronized r2 = TestSynchronized; Thread t1 = Thread(r1, "t1"); Thread t2 = Thread(r1, "t2"); t1.start; t2.start; }} 运行结果为: t1 : 0 t1 : 1 t1 : 2 t1 : 3 t1 : 4 t2 : 5 t2 : 6 t2 : 7 t2 : 8 t2 : 9 上面代码run实现了同步使每次打印出来j总是不相同但实际上在整个run我们只关心j同步而其余代码同步和否我们是不关心所以可以对它进行以下修改: TestSynchronized implements Runnable{ j = 0; public void run{ for( i=0; i<5; i){ //(1) synchronized(this){ .out.prln(Thread.currentThread.getName + " : " + j); } try{ Thread.sleep(100); } catch(InterruptedException e){ .out.prln("Interrupted"); } } }}public TestThread{ public void (String args){ TestSynchronized r1 = TestSynchronized; TestSynchronized r2 = TestSynchronized; Thread t1 = Thread(r1, "t1"); Thread t2 = Thread(r1, "t2"); t1.start; t2.start; }} 运行结果为: t1 : 0 t2 : 1 t1 : 2 t2 : 3 t1 : 4 t2 : 5 t1 : 6 t2 : 7 t1 : 8 t2 : 9 由于进行同步范围缩小了所以效率将提高同时代码(1)指出当对大括号内prln语句进行同步控制时会取走当前对象“锁标志”即对当前对象“上锁”不让当前对象下其它线程执行当前对象其它synchronized数据
0

相关文章

读者评论

发表评论

  • 昵称:
  • 内容: