JAVA 基础知识整理-19 多线程

1. 进程,线程的区别
进程: 正在运行的程序,是系统进行资源分配的独立单位。
每一个进程都有它自己的内存空间和系统资源。

线程:是进程中的单个顺序控制流,是一条执行路径。
一个进程如果只有一条执行路径,则称为单线程程序。
一个进程如果有多条执行路径,则称为多线程。

2.并行和并发
并行:逻辑上的同时发生,指在一个时间内同时运行多个程序。
并发:物理上同时发生,指在某一个时间点同事运行多个程序。

3. JAVA 程序运行原理
java 命令会启动java虚拟机,启动JVM,等于启动了一个应用程序,也就是启动了一个进程。该进程会自动启动一个‘主线程’,然后这个主线程去调用某个类的main方法。jvm的启动至少启动两个线程,一个是主线程,一个是垃圾回收线程。
由于线程是依赖进程而存在的,所以我们应该先创建一个进程出来。而进程是由系统创建的,而Java不能直接调用系统功能,但是java可以去调用C/C++去调用系统功能创建进程。

4. 线程的生命周期
在这里插入图片描述

5.Thread类

线程是程序中执行的线程。 Java虚拟机允许应用程序同时执行多个执行线程。
每个线程都有优先权。 具有较高优先级的线程优先于优先级较低的线程执行。
每个线程可能也可能不会被标记为守护程序。
当在某个线程中运行的代码创建一个新的Thread对象时,新线程的优先级最初设置为等于创建线程的优先级,并且当且仅当创建线程是守护进程时才是守护线程。

构造方法 :

Constructor and Description

Thread()
分配一个新的 Thread对象。

Thread(Runnable target)
分配一个新的 Thread对象。

扫描二维码关注公众号,回复: 10847862 查看本文章

Thread(Runnable target, String name)
分配一个新的 Thread对象。

Thread(String name)
分配一个新的 Thread对象。

Thread(ThreadGroup group, Runnable target)
分配一个新的 Thread对象。

Thread(ThreadGroup group, Runnable target, String name)
分配一个新的 Thread对象,使其具有
target作为其运行对象,具有指定的 name作为其名称,属于
group引用的线程组。

Thread(ThreadGroup group, Runnable target, String name,
long stackSize)
分配一个新的 Thread对象,以便它具有
target作为其运行对象,将指定的 name正如其名,以及属于该线程组由称作
group ,并具有指定的 堆栈大小 。

Thread(ThreadGroup group, String name)
分配一个新的 Thread对象。

方法:
static int activeCount()
返回当前线程的thread group及其子组中活动线程数的估计。
void checkAccess()
确定当前正在运行的线程是否有权限修改此线程。
protected Object clone()
将CloneNotSupportedException作为线程抛出无法有意义地克隆。
static Thread currentThread()
返回对当前正在执行的线程对象的引用。
static void dumpStack()
将当前线程的堆栈跟踪打印到标准错误流。
static int enumerate(Thread[] tarray)
将当前线程的线程组及其子组中的每个活动线程复制到指定的数组中。
static Map<Thread,StackTraceElement[]> getAllStackTraces()
返回所有活动线程的堆栈跟踪图。
ClassLoader getContextClassLoader()
返回此Thread的上下文ClassLoader。
static Thread.UncaughtExceptionHandler getDefaultUncaughtExceptionHandler()
返回当线程由于未捕获异常突然终止而调用的默认处理程序。
long getId()
返回此线程的标识符。
String getName()
返回此线程的名称。
int getPriority()
返回此线程的优先级。
StackTraceElement[] getStackTrace()
返回表示此线程的堆栈转储的堆栈跟踪元素数组。
Thread.State getState()
返回此线程的状态。
ThreadGroup getThreadGroup()
返回此线程所属的线程组。
Thread.UncaughtExceptionHandler getUncaughtExceptionHandler()
返回由于未捕获的异常,此线程突然终止时调用的处理程序。
static boolean holdsLock(Object obj)
返回 true当且仅当当前线程在指定的对象上保持监视器锁。
void interrupt()
中断这个线程。
static boolean interrupted()
测试当前线程是否中断。
boolean isAlive()
测试这个线程是否活着。
boolean isDaemon()
测试这个线程是否是守护线程。
boolean isInterrupted()
测试这个线程是否被中断。
void join()
等待这个线程死亡。 等待这个线程结束再执行其他线程。
void join(long millis)
等待这个线程死亡最多 millis毫秒。
void join(long millis, int nanos)
等待最多 millis毫秒加上 nanos纳秒这个线程死亡。
void run()
如果这个线程使用单独的Runnable运行对象构造,则调用该Runnable对象的run方法; 否则,此方法不执行任何操作并返回。
void setContextClassLoader(ClassLoader cl)
设置此线程的上下文ClassLoader。
void setDaemon(boolean on)
将此线程标记为 daemon线程或用户线程。
static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)
设置当线程由于未捕获的异常突然终止而调用的默认处理程序,并且没有为该线程定义其他处理程序。
void setName(String name)
将此线程的名称更改为等于参数 name 。
void setPriority(int newPriority)
更改此线程的优先级。
void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)
设置当该线程由于未捕获的异常而突然终止时调用的处理程序。
注:线程默认优先级是5;线程优先级的范围1-10.
线程优先级高仅仅表示线程获取的CPU时间片的几率高,但是要在次数比较多,或者多次运行的时候才能看到效果。

static void sleep(long millis)
使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。
static void sleep(long millis, int nanos)
导致正在执行的线程以指定的毫秒数加上指定的纳秒数来暂停(临时停止执行),这取决于系统定时器和调度器的精度和准确性。
void start()
导致此线程开始执行; Java虚拟机调用此线程的run方法。
String toString()
返回此线程的字符串表示,包括线程的名称,优先级和线程组。
static void yield()
对调度程序的一个暗示,即当前线程愿意产生当前使用的处理器。

实现多线程的两种方式:
在这里插入图片描述

案例1

public class TheFirstThread extends Thread{
 private Scanner sc= new Scanner(System.in);
 public void playGame(){
  for(int i=0;i<100;i++){
  System.out.println(getName()+"请输入你最喜欢的人物姓名:");
  String s = sc.nextLine();
  System.out.println(getName()+"你最喜欢的人是"+s);
  }
 }
 public void run() {
  
  playGame();
 }
 
 
}
public class ThreadDemo {
 public static void main(String[] args) {
  TheFirstThread tft  = new TheFirstThread();
  TheFirstThread tft1  = new TheFirstThread();
  tft.start();
  tft1.start();
 }
}

案例2 实现Runnable接口模拟多窗口售票:

public class SaleTickets implements Runnable{
 private int sum = 100; 
 private Object  obj = new Object();
 @Override
 public void run() {
  saleTickets();
  }
public void saleTickets(){
  while(true){
   synchronized(obj){
    if(sum>0){
     try {
      Thread.currentThread();
      Thread.sleep(100);
     } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     }
    System.out.println(Thread.currentThread().getName()+"正在出售第"+sum--+"张票");  
    }
    
   }
   }
   }




public class ThreadDemo1 {
 public static void main(String[] args) {
  //某电影院目前正在上映贺岁大片,共有100张票,而他有3个售票窗口售票,请设计一个程序模拟该电影院售票。
  SaleTickets st1 = new SaleTickets();
  //SaleTickets st2 = new SaleTickets();
  Thread t1 = new Thread(st1,"售票员1号");
  Thread t2 = new Thread(st1,"售票员2号");
  Thread t3 = new Thread(st1,"售票员3号");
  t1.start();
  t2.start(); 
  t3.start(); 
 }
}

引发线程不安全的原因:
1)有多线程环境
2)有共享数据
3)有多条语句操作共享数据
在实现案例2 时,1),2)两项是无法避免的,因此只能通过对3)的改进来解决线程不安全的问题。Java中提供了同步机制synchronized()。
格式synchronized(对象){
需要同步的代码。
}
注意:
1)多个线程必须使用同一个对象(可以是任意的对象,但必须是同一个)才能起到同步锁的功能(sychronized中的对象参数)

2)实现Runable接口这种多线程方式的好处:
a . 可以避免由于java单继承带来的局限性
b. 适合多个相同程序的代码去处理同一个资源情况,把线程同程序的代码,数据有效分离。

6. 同步的优点和缺点

同步的前提:
1)多个线程
2)多个线程使用同一个锁对象

同步的好处:同步的出现解决了多线程的安全问题。
同步的缺点:当线程相当多的时候,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。

同步方法的格式:


public class SaleTickets implements Runnable{
 private int sum = 100; 
 private Object  obj = new Object();
 @Override
 public void run() {
  //saleTickets();
  saleTicket();
 }

public synchronized void saleTicket(){
  while(true){
   //synchronized(obj){
    if(sum>0){
     try {
      Thread.currentThread();
      Thread.sleep(100);
     } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     }
    System.out.println(Thread.currentThread().getName()+"正在出售第"+sum--+"张票");  
    }
}
}

注:
1)同步方法中使用的对象是本类对象(this)
2)如果是同步静态方法,那么同步中使用的方法就是这个类的.class文件对象(比如:SaleTicket.class)
3)如果所对象是this,那么可以考虑使用同步方法,否则能使用同步代码块的尽量使用同步代码块。

7. java中提供的常用的线程安全的类
StringBuffer,Vector, Hashtable
比Vector 效率高且线程安全:
List list1 = Collections.synchronizedList(new ArrayList());

参见 Collections的提供的方法:
static List synchronizedList(List list)
返回由指定列表支持的同步(线程安全)列表。

8. JDK5的Lock接口的使用
Lock实现提供比使用synchronized方法和语句可以获得的更广泛的锁定操作。

方法:
void lock()
获得锁。
void unlock()
释放锁。

9.Lock接口的具体类ReentrantLock

一个可重入互斥Lock具有与使用synchronized方法和语句访问的隐式监视锁相同的基本行为和语义,但具有扩展功能。

构造方法 :
ReentrantLock()
创建一个 ReentrantLock的实例。
ReentrantLock(boolean fair)
根据给定的公平政策创建一个 ReentrantLock的实例。

方法:
int getHoldCount()
查询当前线程对此锁的暂停数量。
protected Thread getOwner()
返回当前拥有此锁的线程,如果不拥有,则返回 null 。
protected Collection getQueuedThreads()
返回包含可能正在等待获取此锁的线程的集合。
int getQueueLength()
返回等待获取此锁的线程数的估计。
protected Collection getWaitingThreads(Condition condition)
返回包含可能在与此锁相关联的给定条件下等待的线程的集合。
int getWaitQueueLength(Condition condition)
返回与此锁相关联的给定条件等待的线程数的估计。
boolean hasQueuedThread(Thread thread)
查询给定线程是否等待获取此锁。
boolean hasQueuedThreads()
查询是否有线程正在等待获取此锁。
boolean hasWaiters(Condition condition)
查询任何线程是否等待与此锁相关联的给定条件。
boolean isFair()
如果此锁的公平设置为true,则返回 true 。
boolean isHeldByCurrentThread()
查询此锁是否由当前线程持有。
boolean isLocked()
查询此锁是否由任何线程持有。
void lock()
获得锁。
void lockInterruptibly()
获取锁定,除非当前线程是 interrupted 。
Condition newCondition()
返回Condition用于这种用途实例Lock实例。
String toString()
返回一个标识此锁的字符串以及其锁定状态。
boolean tryLock()
只有在调用时它不被另一个线程占用才能获取锁。
boolean tryLock(long timeout, TimeUnit unit)
如果在给定的等待时间内没有被另一个线程 占用 ,并且当前线程尚未被 保留,则获取该锁( interrupted) 。
void unlock()
尝试释放此锁。
案例:

public class SaleTickets implements Runnable{
	private int sum  = 100;
	private Lock lock = new ReentrantLock();

	@Override
	public void run() {
		 saleTicket();
		
	}
	
	
	public void saleTicket(){
		while(true){
			lock.lock();
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			if(sum>0){
				
				System.out.println(Thread.currentThread().getName()+"正在出售第"+(sum--)+"张票");
			}
			lock.unlock();
		}
	}
public class SaleTicketDemo {
	public static void main(String[] args) {
		SaleTickets sts = new SaleTickets();
		Thread t1 = new Thread(sts,"1号窗口");
		Thread t2 = new Thread(sts,"2号窗口");
		Thread t3 = new Thread(sts,"3号窗口");
		t1.start();
		t2.start();
		t3.start();
	}
}

10. 死锁问题
是指两个或两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待的现象。
如果出现了同步嵌套,就容易产生死锁问题。

11. 线程的唤醒机制
Object类中提供了三个方法:
wait();等待
notify();唤醒单个线程
notifyAll();唤醒所有线程
卖买包子案例:

public class Bums {
	private String name;
	private int quanity;
	private Bums b;
	boolean flag;
	public Bums() {

	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
		//this.obj = obj;
	}
	public int getQuanity() {
		return quanity;
	}
	public void setQuanity(int quanity) {
		this.quanity = quanity;
	}
	
}
public class BumPrducer implements Runnable {
	private Bums b;
	private int quantity;
	private Lock lock = new ReentrantLock();
	public BumPrducer(Bums b) {
		this.b = b;
	}
	
	@Override
	public void run() {
		 Produce(b);
	}
	
	public void Produce(Bums b){
		while(true){
			synchronized(b){
				if(b.flag){
					try {
						b.wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
				b.setName("肉包子");
				b.setQuanity(2);
				this.quantity+=2;
				System.out.println(Thread.currentThread().getName()+"已生产肉包子"+quantity+"个");
				b.flag = true;
				b.notify();
			}
			
			
			
		}
		
		
	}
	
}
public class BumsBuyer implements Runnable{
	private Bums b;
	private int quantity;
	private Lock lock = new ReentrantLock();
	//private boolean flag;
	public BumsBuyer(Bums b) {
		this.b = b;
	}

	@Override
	public void run() {
		 Buy();
		
	}
	
	public void Buy(){
		while(true){
			synchronized(b){
				if(!b.flag){
					try {
						b.wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}					
				}
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				this.quantity+=b.getQuanity();
				System.out.println(Thread.currentThread().getName()+"已购买"+b.getName()+"共"+quantity+"个");
				b.flag = false;
				b.notify();
			}		
		}	
	}
	
}
public class SaleBuyDemo {
	public static void main(String[] args) {
		Bums b = new Bums();
		Object obj = new Object();
		BumPrducer bp = new BumPrducer(b);
		BumsBuyer bb = new BumsBuyer(b);
		Thread t1 = new Thread(bp, "卖家老王");
		Thread t2 = new Thread(bb, "买家老李");
		t1.start();
		t2.start();
	}
}

Student 案例:

public class Student {
	private String name;
	private int age;
	private boolean flag;

	public synchronized void Get(){		
		if(!this.flag){
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		System.out.println(this.name+"----"+this.age);
		this.flag =false;
		this.notify();
	}
	
	public synchronized void Set(String name,int age){
			if(this.flag){
				try {
					this.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			this.name=name;
			this.age=age;
			this.flag = true;
			this.notify();
			
	}
}
public class GetThread implements Runnable{
	private Student s;
	public GetThread(Student s){
		this.s = s;
	}

	@Override
	public void run() {
		get();
		
	}
	
	public void get(){
		while(true){
			s.Get();
		}
	}

}
public class SetThread implements Runnable {
	private Student s;
	private int x;
	public SetThread(Student s){
		this.s = s;
	}
	@Override
	public void run() {
		set();
		
	}

	public void set(){
		while(true){
			if(this.x%2==0){
				s.Set("Kathy", 35);
			}else{
				s.Set("John", 30);
			}
			this.x++;
		}
			
	}
}
public class TestDemo {
	public static void main(String[] args) {
		Student s = new Student();
		GetThread gt = new GetThread(s);
		SetThread st = new SetThread(s);
		Thread t1 = new Thread(gt);
		Thread t2 = new Thread(st);
		t1.start();
		t2.start();
	}
}

12. 线程池
线程启动一个新线程成本是比较高的,因为它涉及到要与操作系统交互。而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。
线程池中的每一个线程代码结束后,并不会死亡,而是再次回到线程池中变成空闲状态,等待下一个对象来使用。

13.Executors 线程池工厂类
static Callable callable(PrivilegedAction<?> action)
返回一个Callable对象,当被调用时,它运行给定的特权动作并返回其结果。
static Callable callable(PrivilegedExceptionAction<?> action)
返回一个Callable对象,该对象在被调用时运行给定的特权异常操作并返回其结果。
static Callable callable(Runnable task)
返回一个Callable对象,当被调用时,它运行给定的任务并返回null 。
static Callable callable(Runnable task, T result)
返回一个Callable对象,当被调用时,它运行给定的任务并返回给定的结果。
static ThreadFactory defaultThreadFactory()
返回用于创建新线程的默认线程工厂。
static ExecutorService newCachedThreadPool()
创建一个根据需要创建新线程的线程池,但在可用时将重新使用以前构造的线程。
static ExecutorService newCachedThreadPool(ThreadFactory threadFactory)
创建一个根据需要创建新线程的线程池,但在可用时将重新使用以前构造的线程,并在需要时使用提供的ThreadFactory创建新线程。
static ExecutorService newFixedThreadPool(int nThreads)
创建一个线程池,该线程池重用固定数量的从共享无界队列中运行的线程。
static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory)
创建一个线程池,重用固定数量的线程,从共享无界队列中运行,使用提供的ThreadFactory在需要时创建新线程。
static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
创建一个线程池,可以调度命令在给定的延迟之后运行,或定期执行。
static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory)
创建一个线程池,可以调度命令在给定的延迟之后运行,或定期执行。
static ExecutorService newSingleThreadExecutor()
创建一个使用从无界队列运行的单个工作线程的执行程序。
static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory)
创建一个使用单个工作线程运行无界队列的执行程序,并在需要时使用提供的ThreadFactory创建一个新线程。
static ScheduledExecutorService newSingleThreadScheduledExecutor()
创建一个单线程执行器,可以调度命令在给定的延迟之后运行,或定期执行。
static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory)
创建一个单线程执行器,可以调度命令在给定的延迟之后运行,或定期执行。
static ExecutorService newWorkStealingPool()
创建使用所有 available processors作为其目标并行级别的工作窃取线程池。
static ExecutorService newWorkStealingPool(int parallelism)
创建一个维护足够的线程以支持给定的并行级别的线程池,并且可以使用多个队列来减少争用。
static Callable privilegedCallable(Callable callable)
返回一个Callable对象,当被调用时,将在当前访问控制上下文中执行给定的callable 。
static Callable privilegedCallableUsingCurrentClassLoader(Callable callable)
返回一个Callable对象,当被调用时,将在当前访问控制上下文中执行给定的callable ,当前上下文类加载器作为上下文类加载器。
static ThreadFactory privilegedThreadFactory()
返回一个用于创建与当前线程具有相同权限的新线程的线程工厂。
static ExecutorService unconfigurableExecutorService(ExecutorService executor)
返回一个将所有定义的ExecutorService方法委托给给定执行程序的对象,但不能以其他方式使用转换方式访问。
static ScheduledExecutorService unconfigurableScheduledExecutorService(ScheduledExecutorService executor)
返回一个将所有定义的ScheduledExecutorService方法委托给给定执行程序的对象,但不能以其他方式使用转换方式访问。

14. ExecutorService

一个ExecutorService可以关闭,这将导致它拒绝新的任务。 提供了两种不同的方法来关闭ExecutorService 。 shutdown()方法将允许先前提交的任务在终止之前执行,而shutdownNow()方法可以防止等待任务启动并尝试停止当前正在执行的任务。 一旦终止,执行者没有任务正在执行,没有任务正在等待执行,并且不能提交新的任务。 应关闭未使用的ExecutorService以允许资源的回收。
方法:
boolean awaitTermination(long timeout, TimeUnit unit)
阻止所有任务在关闭请求完成后执行,或发生超时,或当前线程中断,以先到者为准。
List<Future> invokeAll(Collection<? extends Callable> tasks)
执行给定的任务,返回持有他们的状态和结果的所有完成的期货列表。
List<Future> invokeAll(Collection<? extends Callable> tasks, long timeout, TimeUnit unit)
执行给定的任务,返回在所有完成或超时到期时持有其状态和结果的期货列表,以先发生者为准。
T invokeAny(Collection<? extends Callable> tasks)
执行给定的任务,返回一个成功完成的结果(即没有抛出异常),如果有的话。
T invokeAny(Collection<? extends Callable> tasks, long timeout, TimeUnit unit)
执行给定的任务,返回一个已经成功完成的结果(即,不抛出异常),如果有的话在给定的超时之前过去。
boolean isShutdown()
如果此执行者已关闭,则返回 true 。
boolean isTerminated()
如果所有任务在关闭后完成,则返回 true 。
void shutdown()
启动有序关闭,其中先前提交的任务将被执行,但不会接受任何新任务。

List shutdownNow()
尝试停止所有主动执行的任务,停止等待任务的处理,并返回正在等待执行的任务列表。
Future submit(Callable task)
提交值返回任务以执行,并返回代表任务待处理结果的Future。

Future<?> submit(Runnable task)
提交一个可运行的任务执行,并返回一个表示该任务的未来。

Future submit(Runnable task, T result)
提交一个可运行的任务执行,并返回一个表示该任务的未来。

案例1:用Runnable实现线程池

public class ThreadPool implements Runnable {
	private int x = 100; 
	@Override
	public void run() {
		while(true){
			if(x>0){
				System.out.println(Thread.currentThread().getName()+":"+x--);
			}
		}
		
	}

}
public class PoolDemo {
	public static void main(String[] args) {
		ExecutorService es = Executors.newFixedThreadPool(3);
		es.submit(new ThreadPool());
		es.submit(new ThreadPool());
		es.submit(new ThreadPool());
	}
}

案例2:用Callable实现线程池(用Callable实现的线程是带返回值的)

public class CallablePool implements Callable<Integer>{
	private int x;
	public CallablePool(int x){
		this.x=x;
	}
	@Override
	public Integer call() throws Exception {
		int x = getSum();
		return x;
	}
	private int getSum() {
		int sum=0;
		for(int i = 0; i<this.x;i++){
			sum+=x;
		}
		return sum;
		
	}

}

public class CallablePoolDemo {
	public static void main(String[] args) throws Exception, ExecutionException {
		ExecutorService es = Executors.newFixedThreadPool(3);
		Future<Integer> f1 = es.submit(new CallablePool(3));
		Future<Integer> f2 = es.submit(new CallablePool(2));
		System.out.println(f1.get());
		System.out.println(f2.get());
		es.shutdown();
	}
}

15.匿名内部类的方式实现多线程
new Thread(){代码。。。。}.start()
New Thread(new Runnable(){代码。。。}).start()

public class AnanmysThread {
	public static void main(String[] args) {
		new Thread(){
			public void run(){
				for(int x = 0;x<10;x++){
					System.out.println(x);
				}
			}			
		}.start();
		
		new Thread(
			new Runnable(){
				public void run(){
					for(int x = 0;x<10;x++){
						System.out.println(x);
					}
				}			
		}).start();
	}
}

16. 定时器
定时器是一个应用十分广泛的工具,可用于调度多个定时任务,以后台的方式执行,在JAVA中,可以通过Timer和TimerTask类来实现定义调度功能。
构造方法 :
Timer()
创建一个新的计时器。
Timer(boolean isDaemon)
创建一个新的定时器,其相关线程可以指定为 run as a daemon 。
Timer(String name)
创建一个新的定时器,其相关线程具有指定的名称。
Timer(String name, boolean isDaemon)
创建一个新的定时器,其相关线程具有指定的名称,可以指定为 run as a daemon 。
方法:
void cancel()
终止此计时器,丢弃任何当前计划的任务。
int purge()
从该计时器的任务队列中删除所有取消的任务。
void schedule(TimerTask task, Date time)
在指定的时间安排指定的任务执行。

void schedule(TimerTask task, Date firstTime, long period)
从指定 的时间开始 ,对指定的任务执行重复的 固定延迟执行 。
void schedule(TimerTask task, long delay)
在指定的延迟之后安排指定的任务执行。

void schedule(TimerTask task, long delay, long period)
在指定 的延迟之后开始 ,重新执行 固定延迟执行的指定任务。

void scheduleAtFixedRate(TimerTask task, Date firstTime, long period)
从指定的时间 开始 ,对指定的任务执行重复的 固定速率执行 。
void scheduleAtFixedRate(TimerTask task, long delay, long period)
在指定的延迟之后 开始 ,重新执行 固定速率的指定任务。
案例1:

public class TimerDemo extends TimerTask{
	private Timer t;
	public TimerDemo(Timer t){
		this.t = t;
	}
	public static void main(String[] args) throws ParseException {
		String s = "2020-4-7 23:25:00";
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		Date d = sdf.parse(s);
		Timer t = new Timer();
		TimerTask tt = new TimerDemo(t);
		
		t.schedule(tt, d);
		
	}

	@Override
	public void run() {
		System.out.println("时间到了哦!该睡觉了!");
		t.cancel();
	}
}

案例2:定时删除文件夹

public class DeleteWithTimer extends TimerTask {
	private Timer t;
	public DeleteWithTimer(Timer t){
		this.t = t;
	}
	public static void main(String[] args) throws ParseException {
		Timer t = new Timer();
		//schedule(TimerTask task, Date time) 
		TimerTask tt = new DeleteWithTimer(t);
		String time = "2020-04-08 9:02:00";
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		Date d = sdf.parse(time);
		t.schedule(tt, d);
	}	

	public void deleteFolder(File f) throws ParseException{				
		File[] flist = f.listFiles();
			for(File file: flist){
				if(file.isDirectory()){
					deleteFolder(file);
				}else{
					file.delete();
				}
				file.delete();
			}
			f.delete();			
		}

	@Override
	public void run() {
		String filename = "F:\\2020AW";
		File f = new File(filename);
		try {
			deleteFolder(f);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		System.out.println("文件删除成功");
		t.cancel();
	}

17. 多线程总结
创建多线程的常用的两种方法:
继承Thread
实现Runnable接口

同步的两种方式:
同步方法
同步代码块

run()和start()的区别:
run()封装了被线程执行的代码,直接调用仅仅是普通的方法调用。
start() 启动线程,并由JVM自动调用run()方法。

sleep()和wait()的区别:
sleep():必须指定时间;不释放锁。
wait():可以不指定时间;释放锁。

wait(),notify(),notifyAll()定义在Object类的原因:
因为这些方法的调用依赖于锁对象,而同步代码块的锁对象是任意锁。
而Object代表任意的对象。

线程的生命周期:
新建—就绪—运行—死亡
新建—就绪—运行—阻塞—就绪—运行–(阻塞—就绪—运行)—死亡

发布了55 篇原创文章 · 获赞 0 · 访问量 2068

猜你喜欢

转载自blog.csdn.net/KathyLJQ/article/details/105266665