首页 > 代码库 > java系统学习(十) --------线程

java系统学习(十) --------线程

线程的基本概念

进程以及使用环境

程序是计算机指令的集合,它以文件形式存储在磁盘上,而进程就是一个执行中的程序,每一个进程都有其独立的内存空间和系统资源。
进程就是一个运行的程序,Windows操作系统是支持多进程的操作系统,即同一时间l可以执行多个程序,每个程序是在自己独立的内存空间内,使用自己被分配到的系统资源。其实,这种说法并不准确,一个CPU在某个时刻,实际
只能运行一个程序,即一个进程。所谓的支持多进程,其实就是CPU在非常快速的交替轮流执行多个程序,例如,利用Windows操作系统可以边听歌曲、一边上网等。

线程以及使用环境

线程是CPU调度和分派的基本单位,一个进程可以由多个线程组成,而这多个线程共享同一个存储空间,这使得线程间的通信比较容易。在一个多进程的程序中,如果要切换到另一个进程,需要改变地址空间的位置。然而在多线程的程)犷中,就不会出现这种情况因为它们位于同一个内存空间内.只需改变运行的顺序即可。
多线程指单个程序可通过同时运行多个不同线程,以执行不同任务。所谓同时,也要依据CPU。如果是多个CPU,则并发运行,如果是一个CPU,则根据系统具体情况,执行多个线程。


线程创建

创建线程的方法一般有两种:

1.通过实现Runnable接口的方式创建线程。

2.通过继承Thread类来创建线程。

通过Runnable接口的方式创建线程

 在Java中,线程是一种对象,但不是所有的对象都可以称为线程。只有实现Runnable接口的类,才可以称为线程。先看看Runnable接口的定义:
public interface Runnable
{
  public abstract void run();
}
Runnable接口只有一个抽象方法run()。要实现这个接口。只要实现这个抽象方法就可以。只要实现了这个接口的类,才有资格称为线程。

创建线程的结构如下:

Thread t=new Thread(runnable 对象);

runnable对象是指实现了Runnable接口类的对象。当线程执行时.runnable对象中的"run()”方法会被调用,如果想要运行上面创建的线程,还需要调用一个Thread类的方法。

t.start();


例子:

public class threadtest{                      //创建测试两个线程类,让其交替运衍
        public static void main(String[]args)  {
              //创建对象c和c1
                compute c=new compute();
                compute1   c1=new compute1();
              //创建线程对象t和t1
                Thread t=new Thread(c);
                Thread t1=new Thread(c1);
                 t.start();//启动线程对象t
                 t1.start();//启动线程对象t1
               }
         }

//创建通过循环语句输出数字的类
class compute implemeats  Runnable{         //创建实现线程的类compute                                                          
        public void run(){                                       //实现方法run()
                for(int i=0;  i<10;i++){
                            System.out .println(i);
                  }
        }
}

//创建通过循环语句输出数字的类
class compute1   implements Runnable{ //创建实现线程的类compute1
      public void run(){//实现方法run()
              for(int i=0;i<10;i++){
                      System.out.println(”这个数字是:”+i);
                }
        }
}

运行结果:

0
这个数字是:0
1
这个数字是:1
2
这个数字是:2
3
这个数字是:3
4
这个数字是:4
5
这个数字是:5
6
这个数字是:6
7
这个数字是:7
8
这个数字是:8
9
这个数字是:9


PS:这个程序段中,创建了两个线程,不过多次运行后结果可能会有不同。那为什么会输出不同的结果呢?
    因为在程序具体运行时会存在执行顺序的问题。在Java技术中,线程通常是通过调度模式来执行的。所谓抢占式调度模式是指,许多线程处于可以运行状态,即等待状态,但实际只有一个线程在运行。该线程一直运行到它终止,或者另一个具有更高优先级变成可运行状态。在后一种情况下,低优先级线程被高优先级线程抢占且获得运行机会。




通过继承Thread类来创建线程

其实Thread类本身也实现了Runnable接口,所以只要让一个类能够继承Thread类,并覆盖"run()”方法,也会创建线程。


例子:

public class threadtest{                      //创建测试两个线程类,让其交替运衍
        public static void main(String[]args)  {
              //创建对象c和c1
                compute c=new compute();
                compute1   c1=new compute1();
              //创建线程对象t和t1
                Thread t=new Thread(c);
                Thread t1=new Thread(c1);
                 t.start();//启动线程对象t
                 t1.start();//启动线程对象t1
               }
         }

//创建通过循环语句输出数字的类
class compute extends  Thread{         //创建实现线程的类compute                                                          
        public void run(){                                       //实现方法run()
                for(int i=0;  i<10;i++){
                            System.out .println(i);
                  }
        }
}

//创建通过循环语句输出数字的类
class compute1   extends  Thread{ //创建实现线程的类compute1
      public void run(){//实现方法run()
              for(int i=0;i<10;i++){
                      System.out.println(”这个数字是:”+i);
                }
        }
}



这两种方式的详细使用:

ExecutorService实现java多线程



线程的使用

线程的优先级

线程的执行顺序是一种抢占方式,优先级高的比优先级低的要获得更多的执行时间,如果想让一个线程比其他线程有更多的时间运行,可以通过设置线程的优先级解决。
如一个线程创建后,可通过在线程中调用“set Priority()”方法,来设置其优先级,具体方法如下:

public final void setPriority(int newPriority);
newPriority是一个1到10之间的正整数,数值越大,优先级别越高,系统定义了一些常数值如下:
    public final static int MIN_PRIORITY=1,表示最低优先级。
    public final static int MAX_ PRIORITY=10,表示最高优先级。
    public final static int NORM_PRIORITY=5,表示默认优先级。


例子:

public class threadtest{                      //创建测试两个线程类,让其交替运衍
        public static void main(String[]args)  {
              //创建对象c和c1
                compute c=new compute();
                compute1   c1=new compute1();
              //创建线程对象t和t1
                Thread t=new Thread(c);
                Thread t1=new Thread(c1);
		t .setPrioritp(10);
		t1.setPrioritp(1);
                 t.start();//启动线程对象t
                 t1.start();//启动线程对象t1
               }
         }


//创建通过循环语句输出数字的类
class compute extends  Thread{         //创建实现线程的类compute                                                          
        public void run(){                                       //实现方法run()
                for(int i=0;  i<10;i++){
                            System.out .println(i);
                  }
        }
}


//创建通过循环语句输出数字的类
class compute1   extends  Thread{ //创建实现线程的类compute1
      public void run(){//实现方法run()
              for(int i=0;i<10;i++){
                      System.out.println(”这个数字是:”+i);
                }
        }
}


输出结果:

0
1
2
3
4
5
6
7
8
9
这个数字是:0
这个数字是:1
这个数字是:2
这个数字是:3
这个数字是:4
这个数字是:5
这个数字是:6
这个数字是:7
这个数字是:8
这个数字是:9


此时的输出变得很有规律,因为程序中将输出数字的线程的优先级设为最高了,而输出汉字的线程是系统最低优先级,所以程序执行时,会给数字输出线程更多的时间执行。



线程的休眠和唤醒

线程的休眠

线程的休眠,是指线程暂时处于等待的一种状态,通俗地说,就是线程暂I付停止运行了。
要达到这种功能需要调用Thread类的“sleep()"方法。"sleep()”方法可以使线程在指定的时间,处于暂时停止的状态,等到指定时间结束后,暂时停止状态就会结束,然后继续执行没有完成的任务。"sleep()”方法的方法结构如下

public static native void sleep(long millis)throws interruptedExcption
 "millis”参数是指线程休眠的毫秒数。上述代码涉及了抛出异常的问题,由于目前还没有开始讲述抛出异常,所以只要知道是抛出异常就可以。

线程的唤醒

线程的唤醒是指,使线程从休眠等待状态进入可执行状态,可以通过调用方法"interrupt()”来实现。

用法:

public class thread6{
        public static void main(String[] args){
                compute t=new compute();
                t .start()
                t .interrupt();
        }
}
//创建一个线程类在这个类中通过休眠来输出不同结果
class compute extends Thread{
        int  i  =  0;
        public void run(){
              //输出相应信息
            System.out.println(”在工作中,不要打扰‘’);
                try{
                      sleep(1000000);
                }catch(exception e){
                  System.out.priatla("哦,电话来了’‘);
                }
        }
}


线程让步

所谓线程让步,就是使当前正在运行的线程对象退出运行状态,让其他线程运行,其方法是通过调用“yield()”方法来实现。这个方法不能将运行权让给指定的线程,只是允许这个线程把运行权让出来,至于给谁,这就需要看由哪个线程抢占到了。

例子:

//创建一个主运行类
public class thread7{
        public static void main(String[]args){
              //创建两个线程对象t和t1
                compute t=new compute();
                compute1 t1=new compute1();
              //启动两个线程
                t.start();
                  t1.start();
        }
}
//创建通过循环语句输出数字的类
class compute extends Thread{   //创建继承线程的类compute
        int i=0;                                       //创建成员变量
      public void run(){                       //实现run()方法
              for(int i=0;i<10;i++){
                        System.out.println(i);
                            Yield();                  //让线程暂停
                  }
        }
}
//创建通过循环语句输出数字的类
class compute1  extends Thread{//创建继承线程的类compute1
      public void run(){//实现run()方法
              for(int i=0;i<10;i++){
                      System.out.println(”这个数字是:”+i);
                }
        }
}

结果:

0
这个数字是:0

这个数字是:1
这个数宇是:2
这个数字是:3
这个数字是:4
这个数字是:5
这个数字是:6
这个数字是:7
1
这个数字是:8
这个数字是:9

2

3

4

5

6

7

8

9
从运行结果来看.第1个线程比第0个线程运行的几率要小,因为它总是放弃运行权


线程同步

前面讲述过,线程的运行权通过一种叫抢占的方式获得。一个程序运行到一半时,突然被另一个线程抢占了运行权,此时这个线程数据处理了一半.而另一个线程也在处理这个数据那么会出现重复操作数据的现象,最终整个系统将会混乱。

同步块

同步块是使具有某个对象监视点的线程,获得运行权限的一种方法,每个对象只能在拥有这个监视点的情况下才能获得运行权限。举个例子,一个圆桌有4个人吃饭,但是只有一个勺子,4人中只有一个人能吃饭,并且,这个人必项是拥有勺子的人,而这个勺子就相当于同步块中的监视点。
同步块的结构如下

synchronized(someobject)
{
代码段
}

  "someobject”是一个监视点对象可以是实际存在的,也可以是假设的。在很多程序段中,这个监视点对象都是假设的。其实这个监视点;就相当于一把锁,给一个线程上了锁,那么其他线程就会被拒之门外,就无法得到这把锁。直到这个线程执行完了,才会将这个锁交给其他线程。其他的线程得到锁后,将自己的程序锁住,再将其他线程拒之门外。

例子:

//创建一个主运行类
public class threadl0{
        public static void main(String[]
            //创建两个线程对象t, t1和t2
              compute t=new compute(’a’);
              compute t1=new computel(‘b‘);
              compute t2”new compute(’c’);
              //启动三个线程
                t.start();
                t1.start();
                t2.start();
        }
}
//创建通过循环语句输出数字的类
class compute extends Thread{
        char ch;
        static Object obj=new Object();
        compute(char ch){
                this.ch=ch;
        }
        public void print(char ch){
                for(int i,0;i<10;i++){
                        System.out.print(ch);
         }
     }
    public void run(){
      synchronized (obj){
          for(int i=1;i<10;i++){
                    print(ch);
                    System.out.println();
          }
        }}
}

结果:

aaaaaaaaa
aaaaaaaaa
aaaaaaaaa
aaaaaaaaa
aaaaaaaaa
aaaaaaaaa
aaaaaaaaa
aaaaaaaaa
aaaaaaaaa

bbbbbbbbb
bbbbbbbbb
bbbbbbbbb
bbbbbbbbb
bbbbbbbbb
bbbbbbbbb
bbbbbbbbb
bbbbbbbbb
bbbbbbbbb

ccccccccc
ccccccccc
ccccccccc
ccccccccc
ccccccccc
ccccccccc
ccccccccc
ccccccccc
ccccccccc

在运行程序中添加一个监视点,那么将锁内的程序段执行完后,就会自动打开锁,再由另外两个线程抢占这个锁。然后反复执行这个同步块中的程序,这样一个线程执行完后,才会执行另一个线程。对于多线程操作同一个数据。就不会出现混乱的现象。




同步化方法

同步化方法就是对整个方法进行同步。它的结构如下:

synchronized void f()
{
代码
}

例子:

//创建一个主运行类
public class threadl0{
        public static void main(String[]
            //创建两个线程对象t, t1和t2
              compute t=new compute(’a’);
              compute t1=new computel(‘b‘);
              compute t2”new compute(’c’);
              //启动三个线程
                t.start();
                t1.start();
                t2.start();
        }
}
//创建通过循环语句输出数字的类
class compute extends Thread{
        char ch;
        static Object obj=new Object();
        compute(char ch){
                this.ch=ch;
        }
        synchronized  void print(char ch){
                for(int i,0;i<10;i++){
                        System.out.print(ch);
         }
     }
    public void run(){   
          for(int i=1;i<10;i++){
                    print(ch);
                    System.out.println();
          }
     }
}

从上面的结果可以看出,使用同步块和同步方法的输出结果是一样的。


实例分析

生产者与消费者

1个汉堡包店,有1个厨师和1个营业员。厕师负责做汉堡包,营业员负责卖汉堡包,当然还有1个存放汉堡包的箱子。厨师不停地做汉堡包,做好了就放在箱子里面,当每次客人来的时候,营业员就会从箱子里面取出1个汉堡包卖掉。假设前提是客人每隔1秒来1个,也就是说营业员1秒卖1个汉堡包,而厨师3秒做1个汉堡包。

目前总共只能做10个汉堡(材料只有10份),箱子中已经有了5个汉堡,请编写一个程序代码来显示这个买卖的关系。

技术分享


代码如下:

设计汉堡包盒子类,将其作为监视点,代码如下:
class  ham {  //创建把装汉堡包的盒子作为监视器类
Static Object  box = new Object();  //创建对象box
static int totalmaterial=10;  //关于制作汉堡包的材料属性
static int sales=0;  //关于销售多少个汉堡包属性
static int production=5; //关于一共有多少个汉堡包属性
}
设计厨师类,代码如下:
class hmaker extends Thread//厨师线程类
{
      //make方法使用了一个同步块,在这个函数里会不断地生产汉堡包
      public void make(){
          synchroaiaed (ham.box){//创建同步块
                  (ham.production)++;
                    try{
                            ham.box.notify();
                    }catch(Exception e){
                    }
              }
      }
public void run(){    //重写run()方法
//使用循环语句来保证在汉堡包材料用完之前,不断地生产汉堡包
	while(ham.production<ham.totalmaterial){
		//使用判断语句判断只要有汉堡包,厨师就通知营业员可以卖了
		if (ham.production>0){
			System.out.println("厨师‘.+getName()+”:‘’十”汉堡包来了(总共‘’
      	 	   +(ham.production-ham.sales)+‘’个)‘’);
		}
		try{
     		   sleep(3000); //因为3秒后才能制造好一个,线程休眠3秒
		}catch(Exception e){
		}
		make();  //调用make()方法
		}
	}
}

设计营业员类:
class hassistant  extends Thread{     //关于营业员的线程类
public void sell(){                                    //创建营业员卖汉堡包的方法
          if(ham.production==0)               //当没有汉堡包时
System.out.println(顾客朋友们,请稍微等一下,汉堡包没了!!);
}
try{
        ham.box.wait();    //使线程暂停,等待厨师类的通知
}catch(Exception e){
}
ham.sales++;
System.out.println("营业员:顾客好汉堡包上来了 (总共卖了‘’十ham.sales十’·个));
}
public void run(){                                     //重写run()方法
      //当盒子里面有汉堡包的情况下不断地卖
      while(ham.sales<ham.production){
                  try{
                      sleep(1000);   //线程休眠1秒
                }catch(Exception e){
               } 
         sell();   //调用sell()方法
       }
    }
}
 

测试类:
public class thread13{                        //创建测试类
      public static void main(String[]args){           //主方法
              hmaker maker=new hmaker();                        //创建对象maker
              hassistant assistant=new hassistant();          //创建对象assistant
            //对对象maker进行设置
            maker .setName(”甲”);
            //启动线程
              maker.start();
              assistant.start();
      }
}


厨师类能做汉堡包的产品数量,应该小于总材料的数量。在这个前提条件下,如果产品不等于零,就让厨师告诉一声:汉堡包上来了,曾、共有多少个。接下来开始3秒做1个汉堡包,做好了后,再通知一声:汉堡包上来了,总共几个。另外,只要箱子里的汉堡包不等于零,就通知营业员可以卖了。而营业员类,主要是销售汉堡包,每1秒卖1个,如果箱子里面的汉堡包等于零,就通知顾客:汉堡包没了,需要等待。

结果:

厨师甲:汉堡包来了(总共5个)
营业员:顾客好,汉堡包上来了 (总共卖了1个)
营业员:顾客好,汉堡包上来了 (总共卖了2个)
厨师甲:汉堡包来了(总共4个)
营业员:顾客好,汉堡包上来了 (总共卖了3个)
营业员:顾客好,汉堡包上来了 (总共卖了4个)
营业员:顾客好,汉堡包上来了 (总共卖了5个)
厨师甲:汉堡包来了(总共2个) 
营业员:顾客好,汉堡包上来了 (总共卖了6个)
营业员:顾客好汉堡包上来了  (总共卖了7个)
厨师甲:汉堡包来了(总共1个)
厨师甲:汉堡包来了(总共2个)





java系统学习(十) --------线程