定时器与线程的同步

时间:2021-10-09 00:11:53
 

定时器

使用Timer和TimerTask组合

Timer类实现类似闹钟的功能,定时或者每隔一定时间间隔触发一次线程,Timer类本身就是一个线程,用来实现调用其它线程。TimerTask类是一个抽象类,实现Runnable接口,具备多线程的能力。

使用Timer和TimerTask组合实现的多线程实例

import java.util.*;

public class TestTimer {

public static void main(String[] args) {

//创建Timer

Timer t = new Timer();

//创建TimerTask

TimerTask1 tt1 = new TimerTask1();

//启动线程

t.schedule(tt1, 0);

try{

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

Thread.sleep(1000);

System.out.println("Main:" + i);

}

}catch(Exception e){}

}

}

/* 以继承TimerTask类的方式实现多线程 */

class TimerTask1 extends TimerTask {      

public void run() {

try{

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

Thread.sleep(1000);

System.out.println("Run" + i);

}

}catch(Exception e){}

}

}

1、public void schedule(TimerTask task,Date time):该方法的作用是在到达time指定的时间或已经超过该时间时执行线程task。

Date d=new Date(2009-1900,10-1,1,10,0,0);

t.schedule(task,d);//在时间到达d(如2009年10月2号)时,启动线程task

2、public void schedule(timerTask task,Date firstTime,long period):该方法的作用是在时间到达firstTime时开始,每隔period毫秒就启动一次task指定的线程。

Date d=new Date(2009-1900,10-1,1,10,0,0);

t.schedule(task,d,20000);//当时间到达或超过d以后,每隔20000毫秒启动一次线程task.

3、public void schedule(TimerTask task,long delay):作用是在执行schedule方法delay毫秒以后自动启动线程。

t.schedule(task,1000);//在执行该行启动代码1000毫秒后启动一次线程task.

4、public void schedule(TimerTask task,long delay,long period):作用是在执行schedule方法delay毫秒以后启动线程task,然后每隔period毫秒重复启动线程task.

Timer类中启动线程还包含两个scheduleAtFixedRate方法,作用是实现重复启动线程时的精确延时。

线程的同步

互斥锁

每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。

Java对象默认是可以被多个线程共用的,只是在需要时才启动“互斥锁”机制,成为专用对象。

关键字synchronized用来与对象的互斥锁联系

当某个对象用synchronized修饰时,表明该对象已启动“互斥锁”机制,在任一时刻只能由一个线程访问,即使该线程出现堵塞,该对象的被锁定状态也不会解除,其他线程任不能访问该对象。

synchronized关键字的使用方式有两种:

ü     用在对象前面限制一段代码的执行(同步代码块)
public void push(char c){
     …
     sychronized(this){
          data[index]=c;
          index++
     }
}

ü     用在方法声明中,表示整个方法为同步方法

public synchronized char pop(){

      index--;

      return data[index];

}

同步代码块可以使用任意对象作为锁,同步方法使用的锁只有一个--this。static同步方法使用的锁是该方法所属类的对象。类型.class

同步好处:决了线程安全问题

同步弊端

ü     降低了运行效率(判断锁是较为消耗资源的)

ü     同步嵌套,容易出现死锁

死锁最终的结果:两个线程互相等待,都无法运行。

public class TestDeadLock {

      public static void main(String[] args) {

           StringBuffer sb=new StringBuffer("ABCDEF");

           MyThread t=new MyThread(sb);

           t.start();

           synchronized(sb){

                 try{

                      t.join();

                 }catch(InterruptedException e){

                 }

                 System.out.println(sb);

           }

           System.out.println("Main thread is over!");

      }

}

class MyThread extends Thread{

      private StringBuffer sb;

      public MyThread(StringBuffer sb){

           this.sb=sb;

      }

      public void run(){

           synchronized(sb){

                 sb.reverse();

           }

           System.out.println("Sub thread is over!");

      }

}

线程同步通信
为避免死锁,就应该让线程在进入阻塞状态时尽量释放其锁定的资源,以为其他的线程提供运行的机会,Object类中定义了几个有用的方法:wait()、notify()、notifyAll()。

wait():被锁定的对象可以调用wait()方法,这将导致当前线程被阻塞并释放该对象的互斥锁,即解除了wait()方法当前对象的锁定状态,其他的线程就有机会访问该对象。

notify():唤醒调用wait()方法后被阻塞的线程。每次运行该方法只能唤醒一个线程。

notifyAll():唤醒所有调用wait()方法被阻塞的线程。

实例,生产者和消费者

public class SyncTest {

      public static void main(String[] args) {

           SyncStack s=new SyncStack();

           Producer p=new Producer(s);

           Consumer c=new Consumer(s);

           Thread t1=new Thread(p);

           Thread t2=new Thread(c);

           t1.start();

           t2.start();

      }

}

class Producer implements Runnable{

      SyncStack stack;

      public Producer(SyncStack stack) {

           this.stack = stack;

      }

      public void run() {

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

                 char ch=(char)(Math.random()*26+'A');

                 stack.push(ch);

                 try {

                      Thread.sleep((int)(Math.random()*300));

                 } catch (InterruptedException e) {

                      e.printStackTrace();

                 } } } }

class Consumer implements Runnable{

      SyncStack stack;

      public Consumer(SyncStack stack) {

           this.stack = stack;

      }

      public void run() {

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

                 char ch=stack.pop();

                 try {

                      Thread.sleep((int)(Math.random()*800));

                 } catch (InterruptedException e) {

                      e.printStackTrace();

}}}}

public class SyncStack {

      private int index=0;

      private char[] data=new char[6];

      public synchronized void push(char ch){

           while(index==data.length){

                 try {

                      this.wait();

                 } catch (InterruptedException e) {

                      e.printStackTrace();

                 }

           }

           this.notify();

           data[index]=ch;

           index++;

           System.out.println("produced:"+ch);

      }

      public synchronized char pop(){

           while(index==0){

                 try {

                      this.wait();

                 } catch (InterruptedException e) {

                      e.printStackTrace();

                 }

           }   

           this.notify();

           index--;

           System.out.println("custom:"+data[index]);

           return data[index];

      }

}