为了更加深刻的理解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()方法的功能和作用相同,都用来定义线程对象被调度之后所执行的操作,都是系统自动调用而用户程序不得引用的方法。