1.线程的三种写法:
方案一:外部类 写一个类继承自Thread,重写run方法,在这个方法加入耗时的操作或阻塞操作 方案二:内部类写法:只有这个Test1_thread这个类才会用到InnerThread 方案三:匿名内部类
public class Test1_thread {
public static void main(String[] args) {
System.out.println("主方法的开头");
MyThread myThread=new MyThread();
myThread.start();
InnerThread innerThread=new InnerThread();
innerThread.start(); //启动线程要用start(); -->jvm会自动调用 线程中run()
//匿名内部类:
Thread nimingThread=new Thread(new Runnable() {
@Override
public void run() {
for ( int i = 0; i <=100 ; i++ ) {
System.out.println("匿名内部类中i的值为:"+i);
try {
Thread.sleep(1000);
}catch ( InterruptedException e){
e.printStackTrace();
}
}
}
});
nimingThread.start();
}
//内部类写法:只有这个Test1_thread这个类才会用到InnerThread
static class InnerThread extends Thread{
@Override
public void run() {
for ( int i = 0; i <=100 ; i++ ) {
System.out.println("内部类中i的值为:"+i);
try {
Thread.sleep(1000);
}catch ( InterruptedException e){
e.printStackTrace();
}
}
}
}
}
//方案一:外部类 写一个类继承自Thread,重写run方法,在这个方法加入耗时的操作或阻塞操作
class MyThread extends Thread{
@Override
public void run() {
for ( int i = 0; i <=100 ; i++ ) {
System.out.println("i的值为:"+i);
try {
Thread.sleep(1000);
}catch ( InterruptedException e){
e.printStackTrace();
}
}
}
}
//缺点:Java 是继承,以上方法影响类的扩展性
2.线程实现的四种方式:
方案一:继承Thread类
方案二:实现runnable接口 任务对象
方案三:匿名内部类写法
方案四:函数式编程 ->lambda写法
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 输出当前时间
* 线程实现的;两种方式
*/
public class Test2_Thread_runnable {
public static void main(String[] args) {
//方法一:继承Thread类
ShowTimeThread stt=new ShowTimeThread();
stt.setName("线程1");
stt.setPriority(1);//可以设置优先级(理论上) 1-10
stt.start();
//实现二:实现runnable接口 任务对象
ShowTimeThread2 showTimeThread2=new ShowTimeThread2();
Thread t=new Thread(showTimeThread2,"线程二");
t.setPriority(10);
t.start(); //t 启动。jvm就会自动回调它配置 task中的run()
//实现三:换成匿名内部类写法
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss");
Date d=null;
while ( true ){
d=new Date();
System.out.println(Thread.currentThread().getName()+"当前的时间为:"+simpleDateFormat.format(d));
try{
Thread.sleep(1000);
}catch ( InterruptedException e){
e.printStackTrace();
}
}
}
},"Thread3");
thread.start();
//写法4:函数式编程 ->lambda写法
Thread t3=new Thread(()->{
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss");
Date d=null;
while ( true ){
d=new Date();
System.out.println(Thread.currentThread().getName()+"当前的时间为:"+simpleDateFormat.format(d));
try{
Thread.sleep(1000);
}catch ( InterruptedException e){
e.printStackTrace();
}
}
},"线程四");
t3.start();
}
}
//显示时间 线程类
class ShowTimeThread extends Thread{
@Override
public void run() {
//耗时操作
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss");
Date d=null;
while ( true ){
d=new Date();
System.out.println(Thread.currentThread().getName()+"当前的时间为:"+simpleDateFormat.format(d));
try{
Thread.sleep(1000);
}catch ( InterruptedException e){
e.printStackTrace();
}
}
}
}
//方案二:写一个类(任务类)实现Runnable接口,重写run()
class ShowTimeThread2 implements Runnable{
@Override
public void run() {
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss");
Date d=null;
while ( true ){
d=new Date();
System.out.println(Thread.currentThread().getName()+"当前的时间为:"+simpleDateFormat.format(d));
try{
Thread.sleep(1000);
}catch ( InterruptedException e){
e.printStackTrace();
}
}
}
}
3.FutureTask
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Test3_callable {
public static void main(String[] args) {
//FutureTask对象
//方式一:内部类
// FutureTask<Integer> task=new FutureTask<>(new Callable< Integer >() {
// @Override
// public Integer call() throws Exception {
// for ( int i = 0; i <= 100; i++ ) {
// Thread.sleep(1000);
// count +=i;
// }
// return null;
// }
// });
//方式二:lambda表达式
FutureTask<Integer> task=new FutureTask<>(()->{
int count=0;
for ( int i = 0; i <= 100; i++ ) {
Thread.sleep(100);
count +=i;
}
return count;
});
//创建线程,与一个FutureTask任务绑定
Thread thread=new Thread(task); //Thread(Runnable)
//启动线程
thread.start();
try {
System.out.println("1 + 2 + 3 + ... +100="+task.get()); //等到两种情况跳出:1.任务执行出错 2.任务执行完成
//System.out.println("1 + 2 + 3 + ... +100="+task.get(1, TimeUnit.SECONDS));
}catch ( InterruptedException e1 ){
e1.printStackTrace();
}catch ( ExecutionException e ){
e.printStackTrace();
}
System.out.println("主线程中其它的代码..."); //因为调用了get(),这是阻塞式的方法。它要等到出结果之后,主线程才会继续
}
}
4.线程池
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 线程池
*/
public class Test4_pool {
public static void main(String[] args) {
/* 核心线程池的大小 */
int corePoolSize=2;
/* 核心线程池的最大线程数 */
int maxPoolSize=4;
/* 线程最大空闲时间 */
long keepAliveTime=10;
/* 时间单位 */
TimeUnit unit=TimeUnit.SECONDS; //enum枚举 常量
/* 阻塞队列 容量为2 最多允许放入2个 空闲任务*/
BlockingQueue<Runnable> workQueue=new ArrayBlockingQueue<>(2);
/* 线程创建工厂 */
ThreadFactory threadFactory=new NameThreadFactory();
/* 线程池拒绝策略 */
RejectedExecutionHandler handler=new MyIgnorePolicy();
ThreadPoolExecutor executor=null;
try {
/* 推荐的创建线程池的方式 */
/* 不推荐使用现成的API创建线程池 */
executor=new ThreadPoolExecutor(corePoolSize,maxPoolSize,keepAliveTime,unit,workQueue,threadFactory,handler);
/* 预启动所有的核心线程 提升效率 */
executor.prestartAllCoreThreads();
/* 任务数量 */
int count=10;
for ( int i = 0; i < count; i++ ) {
RunnableTask task=new RunnableTask(String.valueOf(i));
executor.submit(task); //提交任务到线程池 还有4个任务无法执行
}
}finally {
assert executor!=null; //断言,可开关 -ea -da
executor.shutdown();
}
}
/**
* 线程工厂
*/
static class NameThreadFactory implements ThreadFactory{
/* 线程id AtomicInteger 原子类 */
private final AtomicInteger threadId=new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
Thread t=new Thread(r,"线程一"+threadId.getAndIncrement()); // i++ => i I+1 => 赋值
System.out.println(t.getName()+" 已经被创建");
return t;
}
}
/**
* 线程池拒绝策略
*/
public static class MyIgnorePolicy implements RejectedExecutionHandler{
@Override //被拒绝的任务 线程池对象
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
doLog(r,executor);
}
private void doLog(Runnable runnable,ThreadPoolExecutor e){
//可做日志记录等
System.out.println("线程池:"+e.toString()+runnable.toString()+" 被拒绝执行");
}
}
/**
* 任务类
*/
static class RunnableTask implements Runnable{
private String name;
public RunnableTask(String name){
this.name=name;
}
@Override
public void run() {
try{
System.out.println(this.toString()+ " is running!");
//让任务执行慢点
Thread.sleep(3000);
}catch ( InterruptedException e ){
e.printStackTrace();
}
}
@Override
public String toString() {
return "RunnableTask [name="+name+"]";
}
}
}
线程池原理图:
线程生命周期:
相关的源代码:
// Public constructors and methods
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters and default thread factory and rejected execution handler.
* It may be more convenient to use one of the {@link Executors} factory
* methods instead of this general purpose constructor.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue} is null
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters and default rejected execution handler.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @param threadFactory the factory to use when the executor
* creates a new thread
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue}
* or {@code threadFactory} is null
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters and default thread factory.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @param handler the handler to use when execution is blocked
* because the thread bounds and queue capacities are reached
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue}
* or {@code handler} is null
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @param threadFactory the factory to use when the executor
* creates a new thread
* @param handler the handler to use when execution is blocked
* because the thread bounds and queue capacities are reached
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue}
* or {@code threadFactory} or {@code handler} is null
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
/**
* Executes the given task sometime in the future. The task
* may execute in a new thread or in an existing pooled thread.
*
* If the task cannot be submitted for execution, either because this
* executor has been shutdown or because its capacity has been reached,
* the task is handled by the current {@code RejectedExecutionHandler}.
*
* @param command the task to execute
* @throws RejectedExecutionException at discretion of
* {@code RejectedExecutionHandler}, if the task
* cannot be accepted for execution
* @throws NullPointerException if {@code command} is null
*/
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
5.中断:
interrupt()方法:其作用是中断此线程(此线程不一定是当前线程,而是指调用该方法的Thread实例所代表的线程),但实际上只是给线程设置一个中断标志,线程仍会继续运行。
interrupted()方法:作用是测试当前线程是否被中断(检查中断标志),返回一个boolean并清除中断状态,第二次再调用时中断状态已经被清除,将返回一个false。
public class Test5_Interrupt {
public static void main(String[] args) {
//通过interrupt()方法检测线程是否被中断
//Thread.currentThread() 主线程
System.out.println(Thread.currentThread().getName()+"线程是否中断:"+Thread.interrupted());
//设置线程中断
Thread.currentThread().interrupt();
//Thread.currentThread().stop();
//通过interrupt()方法检测线程是否被中断
System.out.println(Thread.currentThread().getName()+"线程是否被中断:"+Thread.interrupted()); //返回true后,恢复状态
//检测interrupt()是否会清除线程状态
System.out.println(Thread.currentThread().getName()+"线程是否被中断:"+Thread.interrupted());
}
}
interrupt()和interrupted()的源代码:
/**
* Interrupts this thread.
*
* <p> Unless the current thread is interrupting itself, which is
* always permitted, the {@link #checkAccess() checkAccess} method
* of this thread is invoked, which may cause a {@link
* SecurityException} to be thrown.
*
* <p> If this thread is blocked in an invocation of the {@link
* Object#wait() wait()}, {@link Object#wait(long) wait(long)}, or {@link
* Object#wait(long, int) wait(long, int)} methods of the {@link Object}
* class, or of the {@link #join()}, {@link #join(long)}, {@link
* #join(long, int)}, {@link #sleep(long)}, or {@link #sleep(long, int)},
* methods of this class, then its interrupt status will be cleared and it
* will receive an {@link InterruptedException}.
*
* <p> If this thread is blocked in an I/O operation upon an {@link
* java.nio.channels.InterruptibleChannel InterruptibleChannel}
* then the channel will be closed, the thread's interrupt
* status will be set, and the thread will receive a {@link
* java.nio.channels.ClosedByInterruptException}.
*
* <p> If this thread is blocked in a {@link java.nio.channels.Selector}
* then the thread's interrupt status will be set and it will return
* immediately from the selection operation, possibly with a non-zero
* value, just as if the selector's {@link
* java.nio.channels.Selector#wakeup wakeup} method were invoked.
*
* <p> If none of the previous conditions hold then this thread's interrupt
* status will be set. </p>
*
* <p> Interrupting a thread that is not alive need not have any effect.
*
* @throws SecurityException
* if the current thread cannot modify this thread
*
* @revised 6.0
* @spec JSR-51
*/
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // Just to set the interrupt flag
b.interrupt(this);
return;
}
}
interrupt0();
}
/**
* Determines if the currently running thread has permission to
* modify this thread.
* <p>
* If there is a security manager, its <code>checkAccess</code> method
* is called with this thread as its argument. This may result in
* throwing a <code>SecurityException</code>.
*
* @exception SecurityException if the current thread is not allowed to
* access this thread.
* @see SecurityManager#checkAccess(Thread)
*/
public final void checkAccess() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkAccess(this);
}
}
/**
* Tests whether the current thread has been interrupted. The
* <i>interrupted status</i> of the thread is cleared by this method. In
* other words, if this method were to be called twice in succession, the
* second call would return false (unless the current thread were
* interrupted again, after the first call had cleared its interrupted
* status and before the second call had examined it).
*
* <p>A thread interruption ignored because a thread was not alive
* at the time of the interrupt will be reflected by this method
* returning false.
*
* @return <code>true</code> if the current thread has been interrupted;
* <code>false</code> otherwise.
* @see #isInterrupted()
* @revised 6.0
*/
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
6.isInterrupted()
isInterrupted()方法:作用是只测试此线程是否被中断 ,不清除中断状态。
public class Test6_isInterrupted {
public static void main(String[] args) {
//当前线程
Thread thread=Thread.currentThread();
//检测当前线程是否被中断
System.out.println(thread.getName()+"线程是否被中断:"+thread.isInterrupted()); //false
thread.interrupt();
//检测当前线程是否被中断
System.out.println(thread.getName()+"线程是否被中断:"+thread.isInterrupted()); //true
//检测线程中断状态是否会被清除
System.out.println(thread.getName()+"线程是否被中断:"+Thread.interrupted());//true
try {
//线程休眠2s
Thread.sleep(20000); //本来要在main线程中20 s的,但因为interrupt()被调用了。所以sleep被打断
System.out.println(thread.getName()+"线程休眠未被中断...");
}catch ( Exception e ){
e.printStackTrace();
System.out.println(thread.getName()+"线程休眠被中断...");
//检测当前线程是否被中断
System.out.println(thread.getName()+"线程是否被中断:"+thread.isInterrupted()); //false
}
System.out.println(thread.getName()+"线程是否被中断:"+thread.isInterrupted());
}
}
isInterrupted()源代码:
/**
* Tests whether this thread has been interrupted. The <i>interrupted
* status</i> of the thread is unaffected by this method.
*
* <p>A thread interruption ignored because a thread was not alive
* at the time of the interrupt will be reflected by this method
* returning false.
*
* @return <code>true</code> if this thread has been interrupted;
* <code>false</code> otherwise.
* @see #interrupted()
* @revised 6.0
*/
public boolean isInterrupted() {
return isInterrupted(false);
}
/**
* Tests if some Thread has been interrupted. The interrupted state
* is reset or not based on the value of ClearInterrupted that is
* passed.
*/
private native boolean isInterrupted(boolean ClearInterrupted);
7.中断线程并抛出异常后重新抛出异常
/**
* 中断线程并抛出异常后重新抛出异常
*/
public class Test7_ReThrowInterruptException {
public static void main(String[] args) throws InterruptedException {
//当前线程
Thread thread=Thread.currentThread();
try {
//检测当前线程是否被中断
thread.interrupt();
//线程休眠3s
Thread.sleep(3000);
}catch ( InterruptedException e ){
System.out.println(thread.getName()+"抛出InterruptedException中断异常");
System.out.println(thread.getName()+"做一些清理工作");
throw e;
}
}
}
8.ReInterrupted
import java.text.SimpleDateFormat;
import java.util.Date;
public class Test8_ReInterrupted extends Thread{
public static void main(String[] args) throws InterruptedException {
//当前线程
String threadName=Thread.currentThread().getName();
Test8_ReInterrupted reInterrupted=new Test8_ReInterrupted();
System.out.println(printDate()+threadName+"线程启动");
//启动新线程
reInterrupted.start();
//主线程休眠3秒
Thread.sleep(3000);
System.out.println(printDate()+threadName+"设置子线程中断");
//对新线程设置线程中断
reInterrupted.interrupt();
//主线程休眠3秒
Thread.sleep(3000);
System.out.println(printDate()+threadName+"运行结束");
}
@Override
public void run() {
//当前线程
String threadName=Thread.currentThread().getName();
int i=0;
//for 循环等待线程中断 只要当前线程不是中断态,则继续,是中断,则退出当前线程
while ( !Thread.currentThread().isInterrupted() ){
System.out.println(printDate()+threadName+"线程正在执行第"+(++i)+"次");
try {
//执行3次
//线程阻塞,如果线程收到中断操作信号将抛出异常
Thread.sleep(1000);
}catch ( InterruptedException e ){
System.out.println(printDate()+threadName+"线程正在执行");
//检测线程是否被中断
System.out.println(printDate()+threadName+"的状态:"+this.isInterrupted());//false
//如果需要维护中断状态,则需要重新设置中断状态
//如果不需要。则不用调用。 如果调用interrupt()的话,则当前线程的状态变为中断,这个while循环退出,程序结束
Thread.currentThread().interrupt();//true
}
System.out.println(printDate()+threadName+"线程是否被中断:"+this.isInterrupted());
System.out.println(printDate()+threadName+"线程退出");
}
}
private static String printDate(){
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(new Date())+" ";
}
}