线程中的一些方法
1.线程加入:
public final void join()
join()方法的作用:等待该线程中止,其他线程才能继续抢着执行。
2.线程礼让:
public static void yield()
yield()方法的作用:暂停当前正在执行的线程对象,并执行其他线程。
理论上会让线程之间的执行更加和谐,但是实际上做不到。
3.线程死亡:
public final void stop()
public vois interrupt()
stop()方法的作用:直接杀死线程,后面的代码就不会执行了。
interrupt()方法的作用:直接杀死线程,但是在线程死亡之前,还可以有遗言,后面的代码会执行。
下面的代码的执行结果可以清楚的看到这两个方法之间的区别:
(1)调用stop()方法杀死线程:
package com.edu_01;
import java.text.SimpleDateFormat;
import java.util.Date;
class MyThread extends Thread{
@Override
public void run() {
System.out.println("开始时间"+new SimpleDateFormat("HH:mm:ss").format(new Date()));
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
System.out.println("我被杀死了");
}
System.out.println("结束时间"+new SimpleDateFormat("HH:mm:ss").format(new Date()));
}
}
public class Test {
public static void main(String[] args) {
MyThread mt = new MyThread();
mt.start();
try {
Thread.sleep(3000);
//在线程休眠的时候杀死这个线程
//调用stop()方法将线程直接杀死,上面有一条横线表示已经过时,但是还是可以使用
mt.stop();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
运行结果:
(2)调用interrupt()方法杀死线程:
package com.edu_01;
import java.text.SimpleDateFormat;
import java.util.Date;
class MyThread extends Thread{
@Override
public void run() {
System.out.println("开始时间"+new SimpleDateFormat("HH:mm:ss").format(new Date()));
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
System.out.println("我被杀死了");
}
System.out.println("结束时间"+new SimpleDateFormat("HH:mm:ss").format(new Date()));
}
}
public class Test {
public static void main(String[] args) {
MyThread mt = new MyThread();
mt.start();
try {
Thread.sleep(3000);
//在线程休眠的时候杀死这个线程
//interrupt():直接杀死,在死前,还可以有遗言。
mt.interrupt();//线程被杀死之后会将后面的代码执行完毕再死去
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
运行结果:
4.线程休眠:
static void sleep(long mliils)
sleep()方法的作用:让线程睡一会。
sleep()和wait()方法的区别:
sleep():线程休眠,会自己醒来,线程休眠的过程中不会释放掉手里的锁。
wait():线程等待,不会自己醒来,需要被唤醒,等待的过程中会释放掉手里的锁。
线程的生命周期
1.新建
2.就绪
3.运行
4.阻塞
5.死亡
线程的生命周期图解:
线程间通信
1.线程间通信问题:多个线程对同一个资源进行操作,有进有出。
2.生产者消费者问题:
单生产单消费:生产和消费都是一个线程。
多生产多消费:生产和消费都是多个线程。
案例:以给学生设置和获取姓名和年龄为例,演示线程通信问题。
基于学生对象的线程间通信问题图解:
可以看出这是一个单生产单消费问题。
package com.edu_01;
//学生类
class Student {
String name;
int age;
}
//设置线程
class SetThread extends Thread{
private Student s;
private int x=0;
public SetThread(Student s){
this.s=s;
}
@Override
public void run() {
while (true) {
synchronized (s) {
if (x%2==0) {
s.name="张三";
s.age=22;
} else {
s.name="李四";
s.age=23;
}
x++;
}
}
}
}
//获取线程
class GetThread extends Thread{
private Student s;
public GetThread(Student s){
this.s=s;
}
@Override
public void run() {
while (true) {
synchronized (s) {
System.out.println(s.name+":"+s.age);
}
}
}
}
public class StudentDemo {
public static void main(String[] args) {
//创建学生对象
Student s = new Student();
//创建设置线程和获取线程并开启
SetThread st = new SetThread(s);
GetThread gt = new GetThread(s);
st.start();
gt.start();
}
}
运行结果:
可以看出上述运行结果不够和谐,我们可以加入等待唤醒机制,实现礼让效果:
package com.edu_01;
//学生类
class Student {
String name;
int age;
boolean flag;//用来判断对象中是否有数据
}
//设置线程
class SetThread implements Runnable{
private Student s;
private int x=0;
public SetThread(Student s){
this.s=s;
}
@Override
public void run() {
while (true) {
synchronized (s) {
//判断此时对象中有没有数据
if (s.flag) {
try {
s.wait();//设置线程等待,同时释放锁s
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (x%2==0) {
s.name="张三";
s.age=22;
} else {
s.name="李四";
s.age=33;
}
x++;
s.flag=true;//此时对象中有数据了
s.notify();//唤醒正在等待的线程,如果没有线程等待则没有任何效果
}
}
}
}
//获取线程
class GetThread implements Runnable{
private Student s;
public GetThread(Student s){
this.s=s;
}
@Override
public void run() {
while (true) {
synchronized (s) {
//判断对象中是否有数据
if (!s.flag) {
try {
s.wait();//获取线程等待,释放锁s
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(s.name+":"+s.age);
s.flag=false;//当获取线程从对象中获取数据后,我们默认对象中没有数据了,此时我们应该让设置线程继续给学生对象设置学生信息
s.notify();
}
}
}
}
public class StudentDemo {
public static void main(String[] args) {
//创建学生对象
Student s = new Student();
//创建设置线程和获取线程
SetThread st = new SetThread(s);
GetThread gt = new GetThread(s);
Thread t1 = new Thread(st);
Thread t2 = new Thread(gt);
//开启线程
t1.start();
t2.start();
}
}
运行结果:
可以看出打印的结果非常和谐。
线程组
1.线程组:Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。
默认情况下,所有线程都属于主线程组。
2.获取线程对应的线程组对象:public final ThreadGroup getThreadGroup()
3.给线程组设置分组:Threa(ThreadGroup,Runnable target)
需求1:创建线程获取对应的线程组对象,并获取名称。
需求2:创建线程组对象,给线程分配线程组。
package com.edu_01;
class MyThread extends Thread{
public void run() {
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
}
}
public class Test {
public static void main(String[] args) {
//需求1:创建线程获取对应的线程组对象,并获取名称
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();
//获取上面两个线程对应的线程组对象
ThreadGroup tg1 = mt1.getThreadGroup();
ThreadGroup tg2 = mt2.getThreadGroup();
//获取线程组对象的名称
System.out.println(tg1.getName());//main,默认情况下每一个线程都属于主线程组
System.out.println(tg2.getName());
System.out.println("------------------------");
//需求2:创建线程组对象,给线程分配线程组
ThreadGroup tg = new ThreadGroup("线程组");
//public Thread(ThreadGroup group,Runnable target)
Thread t1 = new Thread(tg,new MyRunnable());
Thread t2 = new Thread(tg,new MyRunnable());
//获取上面两个对象的线程组对象
ThreadGroup tg3 = t1.getThreadGroup();
ThreadGroup tg4 = t2.getThreadGroup();
//获取线程组对象的名称
System.out.println(tg3.getName());
System.out.println(tg4.getName());
}
}
运行结果:
线程池
1.线程池:可以理解为一个装线程的池子。
2.使用线程池的原因:程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以很好的提高性能,
尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。
3.线程池的特点:线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。
4.线程池的创建:利用Executors工厂类,调用public static ExecutorService newFixedThreadPool(int nThreads)
方法。
5.线程池的使用步骤:
(1)创建线程池对象:
ExecutorService pool = Executors.newFixedThreadPool(2);
(2)创建任务:
MyRunnable my = new MyRunnable();
(3)提交任务:
pool.submit(my);
pool.submit(my);
(4)关闭线程池:
pool.shutdown();
需求:实现Runnable接口实现线程池的使用。
package com.edu_01;
class MyThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPool {
public static void main(String[] args) {
//1.利用工厂类Executors创建线程池
ExecutorService pool = Executors.newFixedThreadPool(2);
//2.创建两个任务,打印0到99
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();
//3.提交任务给线程池
pool.submit(mt1);
pool.submit(mt2);
//4.关闭线程池
pool.shutdown();
}
}
需求:实现Callable接口实现线程池的使用。
package com.edu_01;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
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;
}
}
public class ThreadPool {
public static void main(String[] args) {
//1.创建线程池
ExecutorService pool = Executors.newFixedThreadPool(2);
//2.创建两个任务
MyCallable mc1 = new MyCallable();
MyCallable mc2 = new MyCallable();
//3.提交任务
pool.submit(mc1);
pool.submit(mc2);
//4.关闭线程池
pool.shutdown();
}
}
需求:实现Callable接口实现线程池的使用,实现多线程求和,1-10之和,1-100之和。
package com.edu_01;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
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;
}
}
public class Test {
public static void main(String[] args) throws Exception, Exception {
//1.创建线程池
ExecutorService pool = Executors.newFixedThreadPool(2);
//2.创建任务并传参
MyCallable mc1 = new MyCallable(1,10);
MyCallable mc2 = new MyCallable(1,100);
MyCallable mc3 = new MyCallable(1,1000);
//3.提交任务<T> Future<T> submit(Callable<T> task)
Future<Integer> res1 = pool.submit(mc1);
Future<Integer> res2 = pool.submit(mc2);
Future<Integer> res3 = pool.submit(mc3);
V get()如有必要,等待计算完成,然后获取其结果。
System.out.println(res1.get());
System.out.println(res2.get());
System.out.println(res3.get());
//4.关闭线程池
pool.shutdown();
}
}
定时器
1.Timer:
public Timer()//构造
public void schedule(TimerTask task, long delay)//延迟多久执行任务
public void schedule(TimerTask task,long delay,long period)//延迟多久执行任务,并以后每隔多久执行一次
public boolean cancel()//取消这个任务
对于schedule()方法:
* 参数1:要执行的任务
* 参数2:延迟多久执行
* 参数3:执行一次之后,每隔多久重复执行
*
2.TimerTask:
public abstract void run()//放的是所要执行的任务代码
schedule(TimerTask task, Date time) //安排在指定的时间执行指定的任务
需求:在10秒钟后,在控制台打印一句话,helloworld。
package com.edu_01;
import java.util.Timer;
import java.util.TimerTask;
public class TimerTest {
public static void main(String[] args) {
Timer t = new Timer();
t.schedule(new MyTimerTask(t),1000);
//public void cancel()终止此计时器
//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();
}
}
需求:延迟5秒钟,打印,我爱中国,以后每隔一秒打印一次。
package com.edu_01;
import java.util.Timer;
import java.util.TimerTask;
//public void schedule(TimerTask task,long delay,long period)
//延迟多久执行任务,并以后每隔多久执行一次
public class TimerTest2 {
public static void main(String[] args) {
Timer t = new Timer();
t.schedule(new MyTimerTask2(t),5000,1000);
}
}
class MyTimerTask2 extends TimerTask{
private Timer t;
public MyTimerTask2(Timer t){
this.t=t;
}
@Override
public void run() {
System.out.println("我爱中国");
}
}