java学习笔记(三十二)java线程2
来源:互联网 发布:全国乡镇经纬度数据库 编辑:程序博客网 时间:2024/05/21 06:52
一:线程中的一些方法
1.1 线程加入
public final void join()
等待该线程中止,其他线程才能继续抢着执行
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(); //接着让mt1这个线程设置为加入线程,其他线程就没有抢占cpu执行权的权利了,只能等待该线程执行完毕之后,才能开始抢占 try { mt1.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } mt2.start(); mt3.start(); }}
1.2 线程礼让
public static void yield():暂停当前正在执行的线程对象,并执行其他线程。
作用:让线程间的执行更和谐一些,但是实际上做不到。
1.3 线程死亡
public final void stop():直接杀死
public void interrupt():直接杀死,在死前,还可以有遗言。
public class MyThread extends Thread{ @Override public void run() { //打印一下开始执行的时间 System.out.println("开始时间:"+new SimpleDateFormat("HH:mm:ss").format(new Date())); //休眠10秒钟 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(); } }}/*结果:开始时间:11:20:21我被杀死了结束时间:11:20:24*/
1.4 线程休眠
static void sleep(long millis) 线程睡一会
二:线程的生命周期
1.新建
2.就绪
3.运行
4.有可能阻塞
5.死亡
三:线程间通信(生产消费者问题):不同类型线程针对同一个资源的操作
通过一个案例来说明问题:
以给学生设置和获取姓名和年龄为例,演示线程通信问题
线程间通讯:
资源:Student
设置数据线程:SetThread
获取数据线程:GetThread
测试类:StudentDemo
Student.java
public class Student { String name; int age;}
SetThread.java
public class SetThread implements Runnable{ private Student s; private int x=0; public SetThread(Student s){ this.s = s; } @Override public void run() { //给学生对象设置姓名和年龄 //Student s = new Student(); while (true) { synchronized (s) { if (x%2==0) { s.name = "刘嘉玲"; s.age = 50; }else { s.name = "陈冠希"; s.age = 35; } x++; } } }}
GetThread.java
public class GetThread implements Runnable{ private Student s; public GetThread(Student s){ this.s = s; } @Override public void run() { //获取线程,获取学生对象的姓名和年龄 //Student s = new Student(); while (true) { synchronized (s) { System.out.println(s.name+"--"+s.age); } } }}
StudentDemo.java
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(); }}
采用上述方式会出现的问题:
相同的数据出现了多次:
因为CPU的一点点时间片就足够我们的程序执行很多次
怎么解决这个问题呢?
将上述代码使用等待唤醒机制改进,实现礼让效果
在Student类中加一个boolean类型的成员变量Flag用来控制
Student.java
public class Student { String name; int age; boolean flag;//在这里可以作为对象的一个标记,如果是false说明该对象没有数据,如果是true说明该对象有数据}
修改GetThread.java和SetThread.java
GetThread.java
public class GetThread implements Runnable{
private Student s;
public GetThread(Student s){ this.s = s;}@Overridepublic 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);//刘嘉玲--50 //冠希--35 //刘嘉玲--50 //当获取线程从学生对象中获取了数据之后,我们就默认他已经没有数据了,此时我们应该 //继续让设置线程继续给学生对象设置信息 s.flag = false; s.notify(); } }}
}
SetThread.java
public 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 = 50; }else { s.name = "冠希"; s.age = 35; } x++;//x=1 //此时对象有数据了 s.flag = true; s.notify();//如果有等待的线程就唤醒,如果没有等待的线程,则没有任何效果 }//在此时释放锁对象s } }}
来实现礼让效果
结果:
四.线程池
为什么要使用线程池?
程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以很好的提高性能,
尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。
线程池的特点:
线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。
在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池
线程池如何创建?
JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法
public static ExecutorService newFixedThreadPool(int nThreads)
线程池的使用步骤:
1.创建线程池对象
ExecutorService pool = Executors.newFixedThreadPool(2);
2.创建Runnable实例
MyRunnable my = new MyRunnable();
3.提交Runnable实例
pool.submit(my);
pool.submit(my);
4.关闭线程池
pool.shutdown();
练习:实现Runnable接口实现线程池的使用
MyRunnbale.java
public class MyRunnbale implements Runnable { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName()+"---"+i); } }}
ThreadPool.java
public class ThreadPool { public static void main(String[] args) { //线程池如何创建? //1.调用工厂类Executors //的public static ExecutorService newFixedThreadPool(int nThreads),返回一个线程池对象 ExecutorService pool = Executors.newFixedThreadPool(2); //2.提交给线程池两个任务,都是打印0-99 //创建任务 MyRunnbale my1 = new MyRunnbale(); MyRunnbale my2 = new MyRunnbale(); //3.提交任务 pool.submit(my1); pool.submit(my2); //关闭线程池 //void shutdown() pool.shutdown(); }}
结果:
练习2:实现Callable接口实现线程池的使用,实现多线程求和,1-10之和,1-100之和
MyCallable.java
public class MyCallable implements Callable<Integer>{//这里面的泛型就是call()方法返回值的类型 private int start; private int end; public MyCallable(int start,int end){ this.start = start; this.end = end; } @Override public Integer call() throws Exception { //在这里求取start--end之间的和 int sum = 0; for (int i = start; i < end+1; i++) { sum+=i; } return sum; }}
Test.java
public class Test { public static void main(String[] args) throws Exception{ //1-10之和,1-100之和 //1.创建线程池 ExecutorService pool = Executors.newFixedThreadPool(2); //2.创建任务对象,创建任务对象的同时,将参数进行传递 MyCallable my1 = new MyCallable(1,10); MyCallable my2 = new MyCallable(1, 100); MyCallable my3 = new MyCallable(1, 1000); //3.提交任务<T> Future<T> submit(Callable<T> task) Future<Integer> res = pool.submit(my1); Future<Integer> res2 = pool.submit(my2); Future<Integer> res3 = pool.submit(my3); //V get()如有必要,等待计算完成,然后获取其结果。 System.out.println(res.get()); System.out.println(res2.get()); System.out.println(res3.get()); //4.关闭线程池 pool.shutdown(); }}
结果:
五.定时器Timer
成员方法:
public Timer()构造 public void schedule(TimerTask task, long delay)延迟多久执行任务 public void schedule(TimerTask task,long delay,long period)延迟多久执行任务,并以后每隔多久执行一次 public boolean cancel()取消这个任务 TimerTask public abstract void run()放的是所要执行的任务代码
练习:定时删除文件
public class Test { public static void main(String[] args) throws Exception { /** * 定时删除文件(需要在13:10:00 删除D://a.txt文件) * 所用方法:schedule(TimerTask task, Date time) 安排在指定的时间执行指定的任务 * 时间点:13:10:00 * 任务:删除D://a.txt文件 */ //创建定时器 Timer t = new Timer(); String time = "2017-05-24 13:10:00"; Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(time); //所用方法:schedule(TimerTask task, Date time) 安排在指定的时间执行指定的任务 t.schedule(new MyTimerTask(), date); }}class MyTimerTask extends TimerTask{ @Override public void run() { //任务:删除D://a.txt文件 File file = new File("D://a.txt"); file.delete(); } }
- java学习笔记(三十二)java线程2
- JAVA学习笔记(三十二)
- Spring学习笔记(三十二):在java后台alert()消息
- JAVA学习总结三十二
- JAVA学习笔记(三十二)- 字符流 FileReader & FileWriter
- java 从零开始,学习笔记之基础入门<DOM解析>(三十二)
- java学习笔记(线程)
- java学习笔记(三十)java线程
- java学习笔记之多线程(2)之线程安全
- java线程学习笔记
- java线程学习笔记
- java 线程 学习笔记
- java线程学习笔记
- java线程学习笔记
- Java线程学习笔记
- java线程学习笔记
- JAVA学习笔记 -- 线程
- JAVA学习笔记 -- 线程
- 我的第一个爬虫
- Vagrant was unable to mount VirtualBox shared folders
- 使用maven项目启动项目时项目持续处于启动状态
- 基于163的邮件发送
- Android性能优化
- java学习笔记(三十二)java线程2
- OpenJudge 6377:生日相同 2.0——题解
- POI中设置Excel单元格格式样式(居中,字体,边框,背景色、列宽、合并单元格等)
- 公钥与私钥的理解
- Java并发编程实战--事实不可变对象
- 小布老师OracleDBA视频合集 下载
- Docker在不同平台上的安装
- 蓝桥杯 最大子阵 dp (类最大连续子段和)
- 并发编程的艺术-数据库连接池简单实现