一:线程中的一些方法(线程中存在的现象)
1.1 线程加入
public final void join()
等待该线程运行完毕,然后剩下的线程再一起抢占CPU执行权
package com.edu.exercise_01;
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(getName()+" "+i);
}
}
}
package com.edu.exercise_01;
/**
* 1.1 线程加入
public final void join()
等待该线程中止,其他线程才能继续抢着执行
*/
public class Test {
public static void main(String[] args) throws Exception {
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();
MyThread mt3 = new MyThread();
mt1.setName("张三");
mt2.setName("李四");
mt3.setName("王五");
mt1.start();
mt1.join();
mt2.start();
mt3.start();
}
}
//运行结果:
//张三 0
//张三 1
//张三 2
//张三 3
//张三 4
//李四 0
//李四 1
//李四 2
//王五 0
//李四 3
//王五 1
//李四 4
//王五 2
//王五 3
//王五 4
1.2 线程礼让
public static void yield():暂停当前正在执行的线程对象,并执行其他线程。
作用:让线程间的执行更和谐一些,但是实际上做不到。可以通过后面讲解的知识来实现。
package com.edu.exercise_02;
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(getName()+" "+i);
//实现线程礼让
//public static void yield():暂停当前正在执行的线程对象,并执行其他线程。
//作用:让线程间的执行更和谐一些,但是实际上做不到
Thread.yield();
}
}
}
package com.edu.exercise_02;
public class Test {
public static void main(String[] args) {
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();
MyThread mt3 = new MyThread();
mt1.setName("小张");
mt2.setName("小李");
mt3.setName("小王");
mt1.start();
mt2.start();
mt3.start();
}
}
1.3 线程死亡
public final void stop():直接杀死被清理掉
public void interrupt():直接杀死,被清理之前前,会将剩下的代码执行完毕。
package com.edu.exercise_03;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MyThread extends Thread{
@Override
public void run() {
//获取开始时间
System.out.println("StartTime:"+new SimpleDateFormat("HH:mm:ss").format(new Date()));
//休眠10秒
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
System.out.println("被终止了");
}
//获取结束时间
System.out.println("EndTime:"+new SimpleDateFormat("HH:mm:ss").format(new Date()));
}
}
package com.edu.exercise_03;
/**
* 1.3 线程死亡
public final void stop():直接杀死(被清理)
public void interrupt():直接杀死,在被清理之前会将剩下的代码执行完毕,然后才被清理
*
*/
public class Test {
public static void main(String[] args) {
//创建线程对象
MyThread mt = new MyThread();
//开启线程对象
mt.start();
//主线程休眠3秒,睡眠的过程中将mt线程杀死
try {
Thread.sleep(3000);
mt.interrupt();//首先输出StartTime:08:54:40
//3秒后输出:
/** 被终止了
EndTime:08:54:43
*/
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
/*
//主线程休眠3秒,睡眠的过程中将mt线程杀死
try {
Thread.sleep(3000);
mt.stop();//首先输出StartTime:08:57:13
//3秒后输出结果只有StartTime:08:48:50,并且终止线程
//stop划了一条横线表示该方法已经过时,但是还可以使用
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
*/
}
}
1.4 线程休眠
static void sleep(long millis) 线程睡眠
二:线程的生命周期(画图讲解),面试题
1.新建
2.就绪
3.运行
4.有可能阻塞
5.死亡
三:线程组
3.1线程组:Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。
注意:默认情况下,所有的线程都属于主线程组。
3.2线程组使用的一般步骤:
① a. 如果是给线程分组、命名,则首先创建MyRunnable类实现Runnable接口,重写run()方法;
b. 如果是仅仅获取线程名称等,可以创建MyThread类继承Thread类,重写run()方法。
②创建线程组,并给线程组取名字
ThreadGroup tg = new ThreadGroup(“我的线程组”);
③给线程设置分组
a. MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(tg, mr);
或者:
b. Thread t2 = new Thread(tg, new MyRunnable());
④获取线程组对象
ThreadGroup tg1 = t1.getThreadGroup();
⑤获取线程组对象的名称
String name = tg1.getName()
案例1:创建线程获取对应的线程组对象,并获取名称
package com.edu.exercise_04;
public class MyExtends extends Thread{
@Override
public void run() {
}
}
package com.edu.exercise_04;
/** 案例1:创建线程获取对应的线程组对象,并获取名称
*
* 1.默认情况下,所有的线程都属于主线程组。
2. public final ThreadGroup getThreadGroup():获取线程对应的线程组对象
3. 线程组对象掉用getName(),获取线程组对象的名称,返回值是String
*
*/
public class TestExtends {
public static void main(String[] args) {
//创建2个线程
MyExtends mt1 = new MyExtends();
MyExtends mt2 = new MyExtends();
//获取线程的线程组对象
ThreadGroup tg1 = mt1.getThreadGroup();
ThreadGroup tg2 = mt2.getThreadGroup();
//获取线程组对象的名称
System.out.println(tg1.getName());//main
System.out.println(tg2.getName());//main
}
}
案例2:创建线程组对象,给线程分配线程组
package com.edu.exercise_04;
public class MyRunnable implements Runnable{
@Override
public void run() {
}
}
package com.edu.exercise_04;
/** 案例2:创建线程组对象,给线程分配线程组
*
* 构造一个新线程组 public ThreadGroup(String name)
* 给线程设置分组 Thread(ThreadGroup group, Runnable target),注意需要的是实现Runnable接口的实现类对象
*/
public class TestRunnable {
public static void main(String[] args) {
//创建实现类对象
MyRunnable mr = new MyRunnable();
//构造一个新线程组 public ThreadGroup(String name)
ThreadGroup tg = new ThreadGroup("我的线程组");
//给线程设置分组 Thread(ThreadGroup group, Runnable target)
Thread t1 = new Thread(tg, mr);
Thread t2 = new Thread(tg, new MyRunnable());
//获取线程名称
System.out.println(t1.getThreadGroup().getName());//我的线程组
System.out.println(t2.getThreadGroup().getName());//我的线程组
}
}
四:线程池
4.1为什么要使用线程池?
程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。
线程池的特点:线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池
线程池如何创建?JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法public static ExecutorService newFixedThreadPool(int nThreads)
4.2线程池的使用步骤:
①创建线程池对象
ExecutorService pool = Executors.newFixedThreadPool(2);
②创建Runnable实例或者Callable实例
(创建Runnable实例,重写run()方法,没有返回值;或者创建Callable<>实例重写call()方法,有返回值,返回值类型必须是引用类型)
MyRunnable my = new MyRunnable();
③提交Runnable实例
pool.submit(my);
④关闭线程池
pool.shutdown();
案例1:实现Runnable接口实现线程池的使用
package com.edu.exercise_05;
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+"---"+i);
}
}
}
package com.edu.exercise_05;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MrThreadPool {
public static void main(String[] args) {
//案例1:实现Runnable接口实现线程池的使用
//1.创建线程池对象
//ExecutorService pool = Executors.newFixedThreadPool(3);
ExecutorService mrpool = Executors.newFixedThreadPool(2);
//2.提交给线程池两个任务,都是打印0-99
//创建Runnable实例,
//MyRunnable my = new MyRunnable();
MyRunnable mr1 = new MyRunnable();
MyRunnable mr2 = new MyRunnable();
//3.提交任务
mrpool.submit(mr1);//一旦提交任务,就会自动开启线程,并执行run()方法中的代码,
//每个线程任务执行完毕,线程会自动关闭,但是并没有被终止,
//而是再次再次回到线程池中成为空闲状态,等待下一个对象来使用。
mrpool.submit(mr2);
//关闭线程池
//void shutdown()
mrpool.shutdown();
}
}
案例2:实现Callable接口实现线程池的使用
package com.edu.exercise_05;
//Callable接口和Runnable接口很相似,不同的是前者有返回值,后者没有返回值
import java.util.concurrent.Callable;
public class MyCallable implements Callable{
@Override
public Object call() throws Exception {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+"---"+i);
}
return null;
}
}
package com.edu.exercise_05;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class McThreadPool {
public static void main(String[] args) {
//案例2:实现Callable接口实现线程池的使用
//1.创建线程池
ExecutorService mcpool = Executors.newFixedThreadPool(2);
//2.创建任务
MyCallable mc1 = new MyCallable();
MyCallable mc2 = new MyCallable();
//3.提交任务
mcpool.submit(mc1);
mcpool.submit(mc2);
//4.关闭线程池
mcpool.shutdown();
}
}
案例3:实现Callable接口实现线程池的使用,实现多线程求和,1-10之和,1-100之和
package com.edu.exercise_06;
//实现Callable接口实现线程池的使用,实现多线程求和,1-10之和,1-100之和
import java.util.concurrent.Callable;
/**
* 要想在主函数中直接给MyCallable类初始化,把开始值和结束值传进来,就应该想到利用构造方法传参
*
*/
public class MyCallable implements Callable<Integer>{
private int start;
private int end;
public MyCallable(int start , int end){
this.start = start;
this.end = end;
}
@Override
public Integer call() throws Exception {
//求和
int sum = 0;
for (int i = start; i < end + 1; i++) {
sum+=i;
}
return sum;
}
}
测试类:
package com.edu.exercise_06;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Test {
public static void main(String[] args) throws Exception {
//1.创建线程池对象
ExecutorService pool = Executors.newFixedThreadPool(2);
//2.创建任务
MyCallable mc1 = new MyCallable(1, 10);
MyCallable mc2 = new MyCallable(1, 100);
//3.添加任务
Future<Integer> res1 = pool.submit(mc1);
Future<Integer> res2 = pool.submit(mc2);
//V get()如有必要,等待计算完成,然后获取其结果。
System.out.println(res1.get());
System.out.println(res2.get());
//4.关闭线程池
pool.shutdown();
}
}
五:定时器
①Timer类
public Timer()构造
public void schedule(TimerTask task, long delay)延迟多久执行任务
public void schedule(TimerTask task,long delay,long period)延迟多久执行任务,并以后每隔多久执行一次
public void schedule(TimerTask task, Date time) 安排在指定的时间执行指定的任务
public boolean cancel()取消这个任务
②TimerTask抽象类,用于创建一个计时任务
public abstract void run()放的是所要执行的任务代码
案例1:演示以上方法的使用
package com.edu.exercise_07;
import java.util.Timer;
import java.util.TimerTask;
//需求:在5秒钟后,在控制台打印一句话,helloworld
public class TimerTest {
public static void main(String[] args) {
Timer t = new Timer();
//public void schedule(TimerTask task, long delay)延迟多久执行任务
//TimerTask是抽象类
t.schedule(new MyTimerTask(t), 5000);
//t.cancel();//如果在这里关闭的话,我们还没等任务执行完毕呢,计时器已经被关闭了
}
}
class MyTimerTask extends TimerTask{
private Timer t;
public MyTimerTask(Timer t){
this.t = t;
}
@Override
public void run() {
System.out.println("helloworld");
t.cancel();//等待任务结束,将计时任务取消
}
}
案例2:定时删除文件
package com.edu.exercise_08;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class Test {
public static void main(String[] args) throws Exception {
/**
* 案例:定时删除文件(需要在指定时间删除D://a.txt文件)
* 所用方法:schedule(TimerTask task, Date time) 安排在指定的时间执行指定的任务
* 时间点:16:30:00
* 任务:删除D://a.txt文件
*/
//1.创建定时器
Timer t = new Timer();
//2.指定的时间解析为日起对象
String time = "2017-05-21 16:31:00";
Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(time);
//3.所用方法:schedule(TimerTask task, Date time) 安排在指定的时间执行指定的任务
t.schedule(new MyTimerTask(t), date);
}
}
class MyTimerTask extends TimerTask{
//构造传参
private Timer t;
public MyTimerTask(Timer t) {
this.t = t;
}
@Override
public void run() {
//任务:删除D://a.txt文件
File file = new File("D://a.txt");
file.delete();
t.cancel();
}
}