定时器
使用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];
}
}