线程以及多线程应用

引用:通过学习线程这一块我知道了以下一些关于线程的知识。也懂得线程的重要性,在很多情况下我们都要用到线程,所以我们来看看下面关于线程到底有什么。
[b]一:线程的定义[/b]:线程是程序中可以并发执行的子任务,主要就是并发,就是通过线程我们可以实现让不同的代码块同时运行起来。多线程就是很多歌单线程一起组合在一起就构造了多线程。
[b]二:线程的创建 [/b] 二.1 程序中至少有一要拥有一个线程,它就是程序开始运行时所创建的线程,在一般的java引用中,此线程是以main()函数开始的。线程对象总是从run()方法开始的,但是确要通过调用start()方法来启动程序。那么线程类到底是怎么创建的呢?
这里将提供两种方法:一种是将其定义为Thread类的子类,复写里面的run()方法。
另一种就是实现Runnable接口,通过接口声明run()方法,然后在需要的时候创建一个Thread类对象。看看具体代码吧
二.1.1通过继承Thread类,
public class TryThread extends Thread {
/**
* 构造方法用来给线程赋值相应的属性
* @param firstName
* @param secondName
* @param awhile
*/
public TryThread(String firstName,String secondName,long delay){
this.firstName=firstName;
this.secondName=secondName;
this.awhile=delay;
setDaemon(true); //设置线程在后台运行
}
//主函数程序的入口
public static void main(String[] args) {
//创建三个线程
Thread first=new TryThread("线程1","线程2",200L);
Thread second=new TryThread("线程3","线程4",300L);
Thread third=new TryThread("线程5","线程6",500L);
System.out.println("Press Enter when you have had enough...\n");
//启动三个线程
first.start();
second.start();
third.start();

try{
System.in.read();
System.out.println("Enter press...\n");
}catch(Exception e){
System.out.println(e);
}
System.out.println("Ending main()");
return; //当按下回车键的时候结束线程
}
/**
* 线程的开始运行方法
*/
public void run(){
try {
while(true){
System.out.println(firstName);
sleep(awhile);
System.out.println(secondName+"\n");
}
}catch (InterruptedException e) {
System.out.println(firstName+secondName+e);
}
}
private String firstName; //声明第一个姓名的属性
private String secondName; //声明第一个姓名的属性
private long awhile; //定义一个时间类型变量
}
运行结果如下:
[img]http://dl2.iteye.com/upload/attachment/0085/6238/18bb0781-7664-369d-9347-a840dfdb62dd.jpg[/img]
对上面这程序简单的做一个分析吧,在构造方法中我们会很奇怪为什么要设置后台运行,通过这样设置了的线程我们叫做守护线程,它从属与创建它的线程也就是主方法线程。那么作为守护线程有什么特点呢,通过分析我们了解到守护线程当创建它的线程结束时,守护线程也随之结束,但是若不是守护线程,在主程序结束后也还有可能继续存在。所以如果在上述构造方法没有设置后台运行的 那么就要在敲打回车键退出主程序代码块加上 first.interrupt()
;second.interrupt(); third.interrupt();通过这样的方法来确保线程结束,从而不占用cpu资源

二.1.2 通过实现Runnale接口实现线程 代码如下:
public class JumbleNames implements Runnable {
//构造方法
public JumbleNames(String firstName,String secondName,long delay){
//通过构造方法保存相应的属性值
this.firstName=firstName;
this.secondName=secondName;
this.awhile=delay;
}

//主函数,程序的入口
public static void main(String []args){
//创建三个线程 ,注意这里是要把JumbleNames实例作为实参传递给Thread类的构造方法
//而Thread通过多态实现线程对象,
Thread first=new Thread(new JumbleNames("线程1","线程2",200L));
Thread second=new Thread(new JumbleNames("线程3","线程4",300L));
Thread third=new Thread(new JumbleNames("线程5","线程6",500L));

//设置线程为守护线程,便于在主线程结束时候随之消失
first.setDaemon(true);
second.setDaemon(true);
third.setDaemon(true);

System.out.println("Press Enter when you have had enough...");
//启动线程
first.start();
second.start();
third.start();

//设置在敲回车键的时候结束主程序
try{
System.in.read();
System.out.println("Enter pressed...\n");

}catch(IOException e){
System.out.println(e);
}
System.out.println("ending main()");
return;
}
/**
* 实现接口中的run()方法
* 用来验证线程输出是否有规律
*/
public void run() {
try{
while(true){
System.out.print(firstName);
Thread.sleep(awhile); //线程休眠
System.out.print(secondName+"\n");
}
}catch(Exception e){
System.out.println(firstName+secondName+e);
}
}
private String firstName;
private String secondName;
private long awhile;
}
在这里我们主要区别的是,构造方法的时候我们没有直接设置是在后台运行,那是因为该类是通过实现接口来定义的,并不是直接从Thread类继承而来,所以就不能直接调用setDaemon()方法,那么作替代我们就必须要在main()方法中通过实例化好的对象来调用setDaemon()方法。
运行结果如下:

[img]http://dl2.iteye.com/upload/attachment/0085/6292/c682f437-842b-37cb-9773-17da51eb2c3a.jpg[/img]

通过这两个例子相信我们应该能很好的应用这两种方法去定义线程了

[b]三:线程中存在的问题[/b]
从上面的运行结果我们可以看出来,和我们所期待我结果并不完全一致,我们需要的是第一个名字,和第二个名字要同时出现,但是通过结果我们可以知道他们并不是按照这种顺序来执行的,而是随便的进行组合。那么存在这样的问题我们就需要想办法解决
并且由这个方法我们可以了应用到 生产消费模型;那么我们看看怎么处理生产消费问题的混乱问题
三.1 管理这些线程加入同步处理;(关键字synchronized)
同步处理同时又有两种方式:一种是同步方法,另一种是同步代码块;
例如:calss MyClass{
synchronized public void method1(){
}
synchronized public void method1(){
}

}
三.1.1 我们看看生产与消费模型的代码示例吧。
首先定义一个信息类:代码如下
public class Info1 {
private String name;
private String content;
/**
* 同步设置的方法 ,使得设置不发生紊乱
*/
public synchronized void set(String name,String content){
this.setName(name);
try {
Thread.sleep(300); //线程休眠
} catch (InterruptedException e) {
e.printStackTrace();
}
this.setContent(content);
}
/**
* 同步得到的方法, 使得去信息的时候不发生紊乱
*/
public synchronized void get(){
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.getName()+"-->"+this.getContent());
}

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}

}
然后定义一个生产者类:
public class Producer1 implements Runnable {
private Info1 info; //定义info类的属性
public Producer1(Info1 info){
this.info=info;
}
public void run(){ //重写接口中的抽象方法
boolean flag=false; //定义标记
for(int i=0;i<50;i++){
if(flag==true){
this.info.set("huzong", "Java讲师");
flag=false; //该表标记
}else{
this.info.set("lanjie","www.net.java.cn");
flag=true; //改变标记
}
}
}
}
在定义一个消费者类
public class Consumer1 implements Runnable { //实现接口
private Info1 info;
public Consumer1(Info1 info){ //构造方法,用来
this.info=info;
}

public void run() { //重写接口中的抽象方法
for(int i=0;i<50;i++){ //循环50次取出
try {
Thread.sleep(100); //睡眠
} catch (InterruptedException e) {
e.printStackTrace();
}
this.info.get(); //取出信息
}

}

}
现在让我们来运行看看结果:

[img]http://dl2.iteye.com/upload/attachment/0085/6367/03bf0e0d-21ec-3c2e-a291-49e7431fd6e6.jpg[/img]

也许到这个时候有人觉得该任务已经完成了,没有出现混乱的想象了,但是细心的人会发现,该结果中有事候在生产几个消费者以后才进行取出,而有的时候生产一个消费者被取出很多次,而我们希望得得的是生产一个就消费者就消费取出一个,这样才是我们需要的结果,在这样的情况下我们就需要用到另外一种办法了。等待与唤醒

[b]四:线程的等待与唤醒[/b]
四.1 所用到的方法是(wait() notify()和notifyAll())继续对上一个例子进行改进
改进后的信息类:
public class Info2 {
private String name;
private String content;
boolean flag =true;
/**
* 同步设置的方法 ,使得设置不发生紊乱
*/
public synchronized void set(String name,String content){
if(!flag){
try {
super.wait(); //等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.setName(name);
try {
Thread.sleep(300); //线程休眠
} catch (InterruptedException e) {
e.printStackTrace();
}
this.setContent(content);
flag=false;
super.notify();//唤醒等待线程
}
/**
* 同步得到的方法, 使得取信息的时候不发生紊乱
*/
public synchronized void get(){
if(flag){ //标记和生产相反
try {
super.wait(); //等待生产
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.getName()+"-->"+this.getContent());
flag=true; //改变标记
super.notify(); //唤醒
}

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}

}
改后的生产者类代码:
public class Producer2 implements Runnable {
private Info2 info; //定义info类的属性
public Producer2(Info2 info){
this.info=info;
}
public void run(){ //重写接口中的抽象方法
boolean flag=true; //定义标记
for(int i=0;i<50;i++){
if(flag==true){
this.info.set("张三", "Java讲师");
flag=false; //该表标记
}else{
this.info.set("百大青年","www.netjava.cn");
flag=true; //改变标记
}
}
}
}
改后的消费者类
public class Consumer2 implements Runnable { //实现接口
private Info2 info;
public Consumer2(Info2 info){ //构造方法,用来
this.info=info;
}

public void run() { //重写接口中的抽象方法
for(int i=0;i<50;i++){ //循环50次取出
try {
Thread.sleep(100); //睡眠
} catch (InterruptedException e) {
e.printStackTrace();
}
this.info.get(); //取出信息
}

}

}
通过改进后我们来看看 结果是否按我们想要的呢?

[img]http://dl2.iteye.com/upload/attachment/0085/6369/a46c1d1f-1e29-3f12-bca2-1f06cc1e9e83.jpg[/img]


这样我们所想要看到的结果就出来了,生产一个就消费一个。生产消费模型也就解决了,简单的说说等待与唤醒吧,我认为等待与唤醒其实就是线程间的通信。通过设置不同的标志告诉另一线程自己所处的状态,例如,当生产者在生产时,由于有标记它就会告诉消费者让其等待生产等生产完了以后消费者在取,然后消费者等待,线程将就是通过设置不同的表情使线程等待与唤醒来完成他们之间的约定,有就取,没有就生产。

[b]五:线程的优先级[/b]
线程同样具有优先级,优先程度高的就先执行,优先程度低的就后执行(通过setPriority()方法来实现)这个代码比较简单就不上了
但是在这里我们需要注意的一点是:通过该方法设置线程的优先级实际执行的优先级和决定于线程的优先级和本地的操作系统的优先级之间对应的关系 ,操作系统的线程调度算法也影响java线程的执行和他们分配到处理器的时间比例,只有在支持优先级调度环境下优先级才会对线程产生影响。

[b]六:线程的死锁[/b]
1:定义 死锁就是无限制的循环下去。
2:产生的原因:线程之间的相互依赖,一个线程执行依赖于另一个线程 而另一个线程同时又依赖第一个线程,这样使得两个线程都无法进行下去,被死锁了。
在编程过程中我们要可能的减少死锁的存在。


总结。。这些就是我所学到的。希望和各位公共学习,也能指导我。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值