学习笔记之JavaSE(25)--多线程5
2016-11-17 14:54
387 查看
今天学习的内容是操作线程的方法
那么问题来了
线程池是什么?答案:线程池是由对象的锁管理的!实际上,这三个方法是Object类的方法,它们必须使用在同步代码之中,并由同步代码的锁所属的对象调用。这么说有点绕,举个例子:假设有一个含有wait()方法的同步代码块,它使用a对象的锁,那么这个wait()方法必须由a对象调用,且任何进入这个同步代码块的线程都会进入等待状态,被放置于a对象的锁管理的线程池中。那么问题又来了。。如果线程t1进入了这个同步代码块,那么a对象的所有同步代码都会被锁住,如何执行notify()方法唤醒线程t1呢?答案:处于等待状态的线程会将钥匙归还并打开对象的锁!也就是说这时线程t2,t3也可以进入该同步代码块并进入等待状态,t4则可以进入a对象锁住的其他同步代码块使用notifyAll()方法唤醒它们。注意:唤醒t1,t2和t3之后,只有持有钥匙的线程才能继续执行同步代码块中的代码,这也就避免了线程安全问题。
顺便说说wait()方法与sleep()方法的区别(sleep()方法比较简单,不细说了):
wait()方法由任意对象调用;sleep()方法由线程调用
wait()方法 可以指定时间也可以不指定;sleep()方法必须指定时间
在同步代码中,执行wait()方法而进入等待状态的线程会打开对象的锁并归还钥匙;执行sleep()方法而进入休眠状态的线程依旧持有钥匙,不会打开对象的锁
下面是使用等待唤醒机制交替执行线程的程序示例:
调用线程的join()方法使该线程优先执行完毕;调用线程的setPriority()方法可以修改线程的优先级,优先级分为Thread.MAX_PRIORITY(常数10)、默认的Thread.NORM_PRIORITY(常数5)和Thread.MIN_PRIORITY(常数1),不过这样并不能确保优先级高的线程一定会优先执行;调用线程的yield()方法可以使该线程暂时停止执行,让别的线程先执行,但这样不能确保该线程会暂时停止执行。示例程序:
:
//面试题1:下列代码编译能否通过,如不能,错误发生在第几行
class Test1 implements Runnable{
public void run(Thread t){}
}
/*
* 答案:不能。
* 原因:Runnable接口中只存在一个无参run()方法
* 这种定义方式实际上只是创建了一个Test1类的新方法而已
* 并没有实现Runnable接口中的方法
* 所以编译错误应该出现在第一行
* 解决方法:1.将Test1类改为抽象类 2.实现无参run()方法
*/
//面试题2:下列代码最终运行哪个run()方法(这里使用了两个匿名内部类)
public class Test45 {
public static void main(String[] args){
new Thread(new Runnable() {
public void run() {
System.out.println("Runnable实现类的的run()");
}
}){
public void run(){
System.out.println("Thread子类的run()");
}
}.start();
}
}
/*
* 答案:子类的run()方法会被执行
* 原因:实现Runnable接口的run()方法已经被子类覆盖了,当然会调用子类的方法
*/
一、wait()方法和notify()/notifyAll()方法
之前我们举过一个程序示例:多个线程都执行操作共享数据的代码时,即使它们的任务不同,也可能会出现线程安全问题,解决方法就是把每个线程操作共享数据的代码都同步化。可是这个例子的输出结果只能是一串“k:男”和一串“q:女”交替出现,如果希望交替打印这两个字符串,也就是希望两个线程交替执行,就可以使用等待唤醒机制。等待唤醒机制实际上是通过wait()方法和notify()方法完成的,之前说到线程状态的时候讲过,调用wait()方法可以使线程进入等待状态,必须用notify()或notifyAll()方法唤醒该线程。详细点说,wait()方法会让线程处于等待状态,并被存储到线程池中;notify()方法会唤醒线程池中任意一个线程;notifyAll()方法会唤醒线程池中所有线程。那么问题来了
线程池是什么?答案:线程池是由对象的锁管理的!实际上,这三个方法是Object类的方法,它们必须使用在同步代码之中,并由同步代码的锁所属的对象调用。这么说有点绕,举个例子:假设有一个含有wait()方法的同步代码块,它使用a对象的锁,那么这个wait()方法必须由a对象调用,且任何进入这个同步代码块的线程都会进入等待状态,被放置于a对象的锁管理的线程池中。那么问题又来了。。如果线程t1进入了这个同步代码块,那么a对象的所有同步代码都会被锁住,如何执行notify()方法唤醒线程t1呢?答案:处于等待状态的线程会将钥匙归还并打开对象的锁!也就是说这时线程t2,t3也可以进入该同步代码块并进入等待状态,t4则可以进入a对象锁住的其他同步代码块使用notifyAll()方法唤醒它们。注意:唤醒t1,t2和t3之后,只有持有钥匙的线程才能继续执行同步代码块中的代码,这也就避免了线程安全问题。
顺便说说wait()方法与sleep()方法的区别(sleep()方法比较简单,不细说了):
wait()方法由任意对象调用;sleep()方法由线程调用
wait()方法 可以指定时间也可以不指定;sleep()方法必须指定时间
在同步代码中,执行wait()方法而进入等待状态的线程会打开对象的锁并归还钥匙;执行sleep()方法而进入休眠状态的线程依旧持有钥匙,不会打开对象的锁
下面是使用等待唤醒机制交替执行线程的程序示例:
public class ThreadCommunicationTest{ public static void main(String[] args){ Resource r = new Resource();//资源 Input in = new Input(r);//任务1 Output out = new Output(r);//任务2 Thread t1 = new Thread(in,"线程1");//线程1 Thread t2 = new Thread(out,"线程2");//线程2 t1.start(); t2.start(); } } //资源 class Resource{ private String name; private String sex; private boolean flag = false;//标志着现在可否打印name:sex public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } public synchronized void set(String name,String sex){ if(flag){ try{ wait(); }catch(InterruptedException e){ e.printStackTrace(); } } this.name = name; this.sex = sex; flag = true; notify(); } public synchronized void get(){ if(!flag){ try{ wait(); }catch(InterruptedException e){ e.printStackTrace(); } } System.out.println(name+":"+sex); flag = false; notify(); } } //输入 class Input implements Runnable{ private Resource r; public Input(){} public Input(Resource r){ this.r = r; } public void run(){ int x = 0; while(true){ if(x==0){ r.set("k", "男"); }else{ r.set("q", "女"); } x = (x+1)%2; } } } //输出 class Output implements Runnable{ private Resource r ; public Output(){} public Output(Resource r){ this.r = r; } public void run(){ while(true){ r.get(); } } }
二、终止线程的方法
终止线程的方法只有等待该线程执行的run()方法结束(还有一种stop()方法,已废除),而使用多线程主要是因为程序出现阻塞(等待用户输入)或者程序中存在无限循环阻碍了程序的运行,那么就可以通过控制循环来终止线程。目前提倡使用一个布尔型标记控制循环的停止。而如果线程因为执行了wait()方法或者sleep()方法而无法执行到循环代码,就可以使用线程的interrupt()方法强制线程退出等待或休眠状态,这样做会抛出InterruptedException,我们可以在catch块设置改变标记的代码,从而停止无限循环,程序示例:public class Test43 { public static void main(String[] args){ StopThread st = new StopThread(); Thread t = new Thread(st); t.start(); int num = 0; for(;;){ if(num==50){ //st.setFlag(false); 退出循环以中断线程 t.interrupt(); break;//退出循环以中断主线程 } System.out.println(Thread.currentThread().getName()+":"+num); num++; } System.out.println("over"); } } class StopThread implements Runnable{ private boolean flag = true; public void run(){ try{ wait(); }catch(InterruptedException e){ e.printStackTrace(); flag = false; } while(flag){ System.out.println(Thread.currentThread().getName()); } } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } }
三、其他方法
调用线程的join()方法使该线程优先执行完毕;调用线程的setPriority()方法可以修改线程的优先级,优先级分为Thread.MAX_PRIORITY(常数10)、默认的Thread.NORM_PRIORITY(常数5)和Thread.MIN_PRIORITY(常数1),不过这样并不能确保优先级高的线程一定会优先执行;调用线程的yield()方法可以使该线程暂时停止执行,让别的线程先执行,但这样不能确保该线程会暂时停止执行。示例程序:public class Test44 { public static void main(String[] args){ JoinThread jt = new JoinThread(); Thread t1 = new Thread(jt,"线程1"); Thread t2 = new Thread(jt,"线程2"); t1.start(); try { t1.join();//调用join()方法后,线程1先执行,执行完毕后,才轮到线程2和主线程交替执行 } catch (InterruptedException e) { e.printStackTrace(); } t2.start(); t2.setPriority(Thread.MAX_PRIORITY);//优先级高,但是也不一定一直执行线程2 for(int i=0;i<50;i++){ System.out.println(Thread.currentThread().getName()+":"+i); } } } class JoinThread implements Runnable{ public void run(){ for(int i=0;i<50;i++){ System.out.println(Thread.currentThread().getName()+":"+i); //Thread.yield(); } } }
四、面试题补充
线程终于结束啦!~虽然还是有点晕,不过对线程的理解比第一次学习时强了很多,学到了很多新知识(比如同步代码块,对象的锁和wait()方法等等),最后再补充两个面试题:
//面试题1:下列代码编译能否通过,如不能,错误发生在第几行
class Test1 implements Runnable{
public void run(Thread t){}
}
/*
* 答案:不能。
* 原因:Runnable接口中只存在一个无参run()方法
* 这种定义方式实际上只是创建了一个Test1类的新方法而已
* 并没有实现Runnable接口中的方法
* 所以编译错误应该出现在第一行
* 解决方法:1.将Test1类改为抽象类 2.实现无参run()方法
*/
//面试题2:下列代码最终运行哪个run()方法(这里使用了两个匿名内部类)
public class Test45 {
public static void main(String[] args){
new Thread(new Runnable() {
public void run() {
System.out.println("Runnable实现类的的run()");
}
}){
public void run(){
System.out.println("Thread子类的run()");
}
}.start();
}
}
/*
* 答案:子类的run()方法会被执行
* 原因:实现Runnable接口的run()方法已经被子类覆盖了,当然会调用子类的方法
*/
相关文章推荐
- 学习笔记_JavaSE_23_多线程的两种方式、安全问题、锁机制
- Android(java)学习笔记215:多线程断点下载的原理(JavaSE实现)
- JavaSE基础学习笔记-多线程
- JAVASE学习笔记:第十三章 多线程和网络编程
- 学习笔记之JavaSE(23)--多线程3
- JavaSE学习笔记--多线程基础
- JavaSE学习笔记之多线程
- [置顶] JavaSE学习笔记_10:Java多线程
- FreeBSD学习笔记25-安装DHCP服务
- Asp.Net Ajax 学习笔记25 利用Microsoft AJAX Library开发客户端组件(下)
- 多线程开发学习笔记之线程同步——互斥量
- 多线程学习笔记1
- Java学习笔记(五、多线程)
- 多线程学习笔记 四
- JAVA学习笔记之多线程
- Symbian学习笔记(6)——关于多线程与活动对象
- 多线程学习笔记 一
- 多线程学习笔记
- Smart Client学习笔记(7) 使用多线程创建高响应智能客户端应用程序
- 多线程开发学习笔记之线程同步——临界区