为什么要使用定时器呢?
比如说一个web应用,如果这个应用规模很大,那它的日志数据是不是很多。如果一直存下来服务器的存储量怕是不行吧,需要隔一段时间删除,那么就需要一个线程每隔一段时间去删除日志数据。
package cn.huangwei;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class TraditionalTimerTest {
private static int count = 0;
public static void main(String[] args) {
new Timer().schedule(new TimerTask(){
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("bombing!");
}
}, 10000, 3000);//第一次10s炸,接下来每隔3s炸一次
while(true){
System.out.println(new Date().getSeconds());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
定时器常用的两种启动方法
通过new Timer().schedule(TimeTask,毫秒值,周期)表示,第一次是过了毫秒值之后,执行TimeTask里面的run方法,接下来
按照每隔周期毫秒执行TimeTask里面的run方法;
通过new Timer().schedule(TimeTask,毫秒值)启动TimeTask里面的run方法,过了毫秒值之后,执行TimeTask里面的run方法,这个timer的任务就结束了。
上面的代码实现了一个功能,每隔两秒、四秒执行TimeTaskrun方法一次,交替两秒与四秒
此处用到的思想就是在TimeTask的run方法里面在定义一个定时器Timer;此处不能使用匿名内部类;因为交替执行需要用到计数器,而如果是匿名内部类,将计数器定义在内部类中,每次计数器都只会重新计数。因此只能定义在外面,而匿名内部类只能访问外部类的final属性,或者可以在外部类中定义成静态变量;代码如下:
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class Test {
static int count = 0;
public static void main(String[] args){
new Timer().schedule(new TimerTask(){
@Override
public void run() {
// TODO Auto-generated method stub
Test.count = (Test.count + 1) % 2;
System.out.println("bombing!");
new Timer().schedule(new TimerTask(){
@Override
public void run() {
// TODO Auto-generated method stub
Test.count = (Test.count + 1) % 2;
System.out.println("bombing!");
new Timer().schedule(new TimerTask(){
@Override
public void run() {
// TODO Auto-generated method stub
}
}, 2000 + 2000 * count);
}
}, 2000 + 2000 * count);
}
}, 2000);
while(true){
System.out.println(new Date().getSeconds());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
想要交替不断的运行,就得在每个匿名内部类里面调用一个Timer计时器,而且传入的TimeTask也要嵌套一个Timer定时器,这样无穷无尽,做不下去,重复代码过多;此时可以想到,将匿名内部类TimeTask代码抽取出来,自定义一个TimeTask的的子类;只要在子类中启动一个定时器,new 一个子类的实例,并作为TimeTask传入Timer就会形成一个递归一样的,循环调用的过程,代码如下
package cn.huangwei;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class TraditionalTimerTest {
private static int count = 0;
public static void main(String[] args) {
class MyTimerTask extends TimerTask{
@Override
public void run() {
count = (count + 1) % 2;
System.out.println("bombing!");
new Timer().schedule(new MyTimerTask(), 2000 + 2000 * count);
}
}
new Timer().schedule(new MyTimerTask(), 2000);
while(true){
System.out.println(new Date().getSeconds());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
2.传统的synchronized
一种同步机制是使用synchronized关键字,这种机制也称为互斥锁机制,这就意味着同一时刻只能有一个线程能够获取到锁,获得的锁也被称为互斥锁。其他需要获取该互斥锁的线程只能被阻塞,直到获取到该锁的线程释放锁。
下面通过一段代码演示synchronized的用法
package cn.huangwei;
public class TraditionalThreadSynchronized {
public static void main(String[] args) {
new TraditionalThreadSynchronized().init();
}
private void init(){
final Outputer outputer = new Outputer();
new Thread(new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
outputer.output("zhangxiaoxiang");
}
}
}).start();
new Thread(new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
outputer.output("lihuoming");
}
}
}).start();
}
static class Outputer{
public void output(String name){//或者方法修饰符加synchronized
int len = name.length();
synchronized(this){
for(int i = 0; i < len; i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
}
}
}
该程序的功能是,启动两个线程,一个线程输出“zhangxiaoxiang”,一个字符一个字符输出;另一个线程输出“lihuoming”,也是按字符输出,要求一个线程没输出完成,另一个线程不可输出字符,通过同步机制进行控制;
思想:定义一个输出类Outputer,用于输出给定的字符串,并在输出字符串的方法中,加入同步控制;然后init
方法中,定义Outputer类的实例,由于此处为了方便,线程传入runnable的匿名内部类;在run方法中调用outputer的output方法;这样可以减少一些重复代码;
对于synchronized,找到合适锁对象挺重要的;那么问题来了,判断synchronized的锁对象,决定了同步块的能否实现同步;
public void output(String name){//或者方法修饰符加synchronized
int len = name.length();
synchronized(this){
for(int i = 0; i < len; i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
}
public synchronized void output2(String name){
int len = name.length();
for(int i = 0; i < len; i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
public static synchronized void output3(String name){
int len = name.length();
for(int i = 0; i < len; i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
output1和output2这两个方法能互斥嘛?
答案是可以的,因为output2等价于
public void output2(String name){
synchronized(this){
int len = name.length();
for(int i = 0; i < len; i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
}
两个方法用的同一个锁对象,可以实现互斥;
那么output3与output犯法能不能互斥?
答案是不行的,因为output里面的this是指调用该方法的对象的实例;而output3是静态方法,他的锁是Outputer.class字节码,锁不一样,不能实现同步;