为了更加深刻的理解java多线程机制,先从最简单的demo入手实现数字和字母的交叉打印。实现思路是: 首先定义一个对象其中创建三个变量,flag用于控制应该打印字母还是数字,numberPrint输出打印数字,wordPrin用于计算输出的打印字母。然后分别定义一个打印数字的线程和打印字母的线程。最后在主函数中测试即可。
(一)定义两个线程可以访问到的公共资源
public class IsNum { boolean flag=true; int numberPrint=0; int wordPrin=0; public IsNum(boolean flag){ this.flag=flag; } }
(二)定义打印数字的线程
public class PrinNumber extends Thread { private IsNum isNum; public PrinNumber(String name,IsNum isnum){ super(name); this.isNum=isnum; } @Override public void run(){ while(isNum.numberPrint<100){ synchronized (isNum){ if(isNum.flag) { System.out.println(isNum.numberPrint); isNum.numberPrint++; isNum.flag=false; isNum.notifyAll(); }else{ try{ isNum.wait(); }catch (InterruptedException e){ new RuntimeException(e); } } } } } }
(三)定义打印字母的线程
public class PrintWord extends Thread{ private IsNum isNum; public PrintWord(String name,IsNum isnum){ super(name); this.isNum=isnum; } @Override public void run(){ while(isNum.numberPrint<100){ synchronized (isNum) { if(!isNum.flag) { System.out.println((char) (96 + isNum.wordPrin)); isNum.wordPrin++; if (isNum.wordPrin == 26) { isNum.wordPrin = 0; } isNum.flag=true; isNum.notifyAll(); }else{ try{ isNum.wait(); }catch (InterruptedException e){ new RuntimeException(e); } } } } } }
(四)测试程序
public static void main(String[] args) { IsNum isNum=new IsNum(false); new PrintWord("打印字母",isNum).start(); new PrinNumber("打印数字",isNum).start(); }
(五)运行效果
Connected to the target VM, address: '127.0.0.1:54262', transport: 'socket'
`
1
a
2
b
3
c
。。。。。。
(六)深入思考升华
(1) sycronized 线程同步加锁
sycronized和sycronized(变量)加锁,获得的是成员锁,一次只能有一个线程进入该方法或者该代码块。使用sycronized(this)和sycronized(类名)两种方式加的是对象锁,此时别的线程在类中所有操作都不能进行。
(2)wait()
在 Java 中可以用 wait、notify 和 notifyAll 来实现线程间的通信。线程在运行的时候,如果发现某些条件没有被满足,可以调用wait方法暂停自己的执行,并且放弃已经获得的锁,然后进入等待状态。当该线程被其他线程唤醒并获得锁后,可以沿着之前暂停的地方继续向后执行,而不是再次从同步代码块开始的地方开始执行。
(3)notifyAll()
当有线程调用了对象的 notifyAll()方法(唤醒所有 wait 线程)或 notify()方法(只随机唤醒一个 wait 线程),被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。也就是说,调用了notify后只要一个线程会由等待池进入锁池,而notifyAll会将该对象等待池内的所有线程移动到锁池中,等待锁竞争。
(4)start()
线程调用该方法将启动线程
(5)run()
Thread类的run()方法与Runnable接口中的run()方法的功能和作用相同,都用来定义线程对象被调度之后所执行的操作,都是系统自动调用而用户程序不得引用的方法。