Java线程的终止和同步

来源:互联网 发布:js父元素 编辑:程序博客网 时间:2024/06/03 20:11

线程:是程序内部的顺序控制流。

Java线程:通过java.lang.Thread类来实现。

一、实现线程方法

1、新建一个Thread对象

2、新建一个Thread类的子类对象

3、新建一个实现Runnable接口的对象

二、Java VM的主线程:VM启动一个主线程(由public static void main(String[] args){}定义)。

多线程:在操作系统中同时运行几个线程(顺序流)。即程序运行期间多个进程(主方法、其他进程实现方法的实例对象)之间共享CPU。

三、线程的状态

1、创建:创建线程对象。

2、就绪:调用start()方法后进入就绪状态,就绪状态的线程,等待CPU的调度,调度后执行。

3、运行:就绪状态获取CPU资源,线程就可以执行

4、阻塞:阻塞事件发生触发线程从运行状态进入阻塞状态。当阻塞解除后线程进入就绪状态。阻塞事件例同步方法中调用this.wait()方法,阻塞当前锁定该对象的线程。阻塞解除事件如notify/notifyAll来唤醒wait进程。注wait方法在wait pool中不占用锁,唤醒后重新占用锁,而sleep在休眠时也占用锁。

5、终止:销毁线程。原来的JDK中使用stop方法来销毁线程。在JDK6中已经停用了改方法。现在一般处理方法是要终止线程,即线程的run方法运行完毕。可设置一个boolean flag属性来控制run方法的执行,当且仅当flag为true,run方法内部才执行。同时,可以定义一个shutDown方法来利用修改flag为false来终止线程的运行。

如:定义一个线程类

class TreadStop implements Runnable {

  private boolean flag=true; 

    public void run() {

      int i = 0;

     while (flag==true) {

          System.out.print("Thread of ThreadStop: " + i++);

                                }

                            }

      public void shutDown() {

           flag = false;

                                       }

}

 在调用该线程类的一个实例时:使用实例调用shutDown方法类终止线程

 

 

public class TestThreadStop{

  public static void main(String args[]){

    ThreadStop t = new ThreadStop();

      t.start();

       for(int i=0;i<100000;i++){

           if(i%10000==0 & i>0)

                 System.out.println("in thread main i=" + i);

        }

     System.out.println("Thread main is over");

      t.shutDown();

  }

}

 

四、线程状态控制方法:

 

方法名控制说明isAlive线程是否启动并未终止getPriority()获取线程的调度优先级数值。优先级高的占用CPU时间片多。setPriority()设置线程的调度优先级数值。数值为1-10,默认为5。Thread.sleep()当前线程睡眠的毫秒数join()

调用某个线程的该方法,将当前进程和该进程合并,直到该进程终止,再恢复当前进程的运行

相当于方法调用

yeild让出CPU,进入就绪状态等待调度wait进入对象的wait poolnotify/notifyAll唤醒对象的wait pool的一个或全部线程

注;wait和sleep的区别:

wait是别的线程可以访问锁定对象,且调用wait方法时必须锁定该对象。而sleep时别的线程也不可以访问锁定对象。

五、线程同步

    在Java中,用对象互斥锁来保证共享数据的完整性。每个对象都对应一个“互斥锁”标记。这个标记保证在任意时刻,只有一个线程在访问该对象。Java中,互斥锁标记用Synchronized关键字来说明。当某个对象用Synchronized修饰时表示任一时刻只有一个线程可以访问该对象,这样保证了数据的完整性。同样,Synchronized可以用于修饰方法,说明该方法是同步方法(方法执行时当前对象被锁定)。

synchronized (this){ //执行语句块时当前对象被锁定 }

同理同步方法为:

public synchronized void methodName(){ //执行该方法时当前对象被锁定 }

 否则,在多个线程运行时不能保证原来一个语句块的原子性:

例:我们希望在add方法中输出该方法是被第几次访问。

public static int num=0;

public void add(){

   num++;

   System.out.println("这是第 "+num+"次调用该方法");

}

在进程1中:在num++;和System.out.println();之间若没有锁定当前对象的话,其他进程(进程2)打断当前进程(进程1)的执行,执行了add方法中的num++,此时num值为2。然后回到原来的进程1 执行,输出进程1是第2次调用该方法。然后回到进程2,输出这是第二次调用该方法。这就导致执行结果和我们预期的不一致。

这就是为什么要用synchronized关键字来锁定当前对象。若锁定在方法add中当前对象或使用synchronized(this){}将要原子执行的语句放在花括号中也可以。

之所以上面问题的解决方案可有:

1、用synchronized(this){}锁定当前对象

public void add(){

  synchronized(this){

    num++;

    System.out.println("这是第 "+num+"次调用该方法");

   }

}

2、用synchronized关键字修饰add方法使在add方法内锁定当前对象

public synchronized void add(){
    num++;
    System.out.println("这是第 "+num+"次调用该方法");
}

 

注:当一个线程类中有同步方法和非同步方法时,要仔细考虑可以访问关注对象的所有方法是否加锁。对于同步方法,只能有一个线程独占执行完成后释放。而非同步方法,可以由多个线程交叉访问。

对于一个对象,我们对对象"写"的方法需要设置为同步方法,这是因为多个同步方法都要锁定对象,所以,只有一个线程在一段时间内"写"该对象。"读"方法可以不用设置为同步(效率高)。 

原创粉丝点击