java学习(16)

来源:互联网 发布:半结构化数据 编辑:程序博客网 时间:2024/06/15 10:05

java学习(16)

这篇接着来写多线程的内容,包括一些线程的方法,线程组、线程池以及关于定时

器的介绍。

1.线程中部分方法

1.1 线程加入

public final void join():等待该线程中止,其他线程才能继续抢着执行

public class Mythread extends Thread{@Overridepublic void run() {for (int i = 0; i < 100; i++) {if(i%2==0){System.out.println(this.getName()+"="+i);}}}}
public static void main(String[] args) {//1.1 线程加入  //public final void join()  //等待该线程中止,其他线程才能继续抢着执行//创建三个线程Mythread mt1 = new Mythread();Mythread mt2 = new Mythread();Mythread mt3 = new Mythread();//给三个线程设置名字mt1.setName("mt1");mt2.setName("mt2");mt3.setName("mt3");mt1.start();try {mt1.join();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}//启动线程mt2.start();mt3.start();}

分析:使用了join()方法,只有线程mt1运行中止,线程mt2和mt3才能抢占CPU执行

1.2 线程礼让

public static void yield():暂停当前正在执行的线程对象,并执行其他线程

public class MyThread extends Thread{@Overridepublic void run() {for (int i = 0; i < 50; i++) {if(i%3==1){System.out.println(this.getName()+"***"+i);}Thread.yield();}}}
public static void main(String[] args) {//创建三个线程MyThread mt1 = new MyThread();MyThread mt2 = new MyThread();MyThread mt3 = new MyThread();//设置名字mt1.setName("mt1");mt2.setName("mt2");mt3.setName("mt3");//启动线程mt1.start();mt2.start();mt3.start();}
分析:线程礼让难以实现线程之间的和谐运行

1.3 线程死亡

public final void stop():直接杀死

public void interrupt():直接杀死,在死前,还可以有遗言

public static void main(String[] args) {//创建一个线程MyThread mt = new MyThread();//启动线程mt.start();try {Thread.sleep(3000);//public final void stop():直接杀死//mt.stop();//interrupt():直接杀死,在死前,还可以有遗言。mt.interrupt();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
注意:这里使用stop()方法的时候,代码上会有一天直线,表示该方法已经过时,但还可以使用。stop()和interrupt()方法的区别在于stop是立即结束该进程,不会有停留,interrupt立即结束进程,但是还可以执行一段代码。

1.4 线程休眠

static void sleep(long millis) 线程睡一会

分析:该方法已经多次使用,可以查看上述代码。


2.线程的生命周期

1)新建

2)就绪

3)运行
4)有可能阻塞
5)死亡


3.线程间通信(生产消费者问题):不同类型线程针对同一个资源的操作

案例详细解析:例如给学生设置和获取姓名和年龄

线程间通讯:

1)资源:Student

2)设置数据线程:SetThread

3)获取数据线程:GetThread

代码:

public class GetThread implements Runnable{private Student s;public GetThread(Student s){this.s = s;}@Overridepublic void run() {// TODO Auto-generated method stubwhile (true) {synchronized(s){System.out.println(s.name+":"+s.age);}}}}
public class SetThread implements Runnable{private Student s;int x = 0;public SetThread(Student s){this.s = s;}@Overridepublic void run() {while(true){synchronized(s){if(x%2==0){s.name = "student1";s.age = 12;}else{s.name = "student2";s.age = 21;}x++;}}}}
public class Student { String name; int age;}
public class Test {public static void main(String[] args) {//创建一个学生对象Student s = new Student();//创建设置线程和获取线程对象SetThread st = new SetThread(s);GetThread gt = new GetThread(s);//创建线程Thread thread1 = new Thread(st);Thread thread2 = new Thread(gt);//启动线程thread1.start();thread2.start();}}
分析:如上代码运行,

问题1:对于控制台出现的结果是:null:0

是由于设置和获取线程使用的学生资源不是同一个,可以把资源作为构造参数传递

问题2:相同的数据出现了多次以及数据安全问题

CPU的一点点时间片就足够我们的程序执行很多次,要加锁

问题3:加了锁以后,数据还是有问题

多个线程都要加锁;多个线程加的锁必须是同一把锁

改进代码:

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();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}System.out.println(s.name+":"+s.age);//读取数据之后置flag为false,默认取出后没有数据了s.flag = false;//唤醒等待的进程s.notify();}}}}
public class SetThread implements Runnable{private Student s;int x=0;public SetThread(Student s){this.s = s;}@Overridepublic void run() {while(true){synchronized (s) {if(s.flag){try {s.wait();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}if(x%2==0){s.name = "小明";s.age = 12;}else{s.name = "小红";s.age = 15;}x++;//设置数据之后置flag为trues.flag = true;//唤醒等待的进程s.notify();}}}}
public class Student {//成员变量String name;int age;//用来标识有没有数据boolean flag;}

public class Test {public static void main(String[] args) {//创建一个学生对象Student s = new Student();//创建设置线程和获取线程对象SetThread st = new SetThread(s);GetThread gt = new GetThread(s);//创建线程Thread thread1 = new Thread(st);Thread thread2 = new Thread(gt);//启动线程thread1.start();thread2.start();}}
分析:这里的改进代码在student类里,加了一个flag成员变量,用以表示对象是否已经有数据,在设置线程和获取线程里面,进行判断以此来确定线程的执行顺序。

运行:



4.线程组

线程组:Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。


默认情况下,所有的线程都属于主线程组。

public final ThreadGroup getThreadGroup():获取线程对应的线程组对象


线程设置分组

Thread(ThreadGroup group, Runnable target) 


代码:

public class MyThread extends Thread{@Overridepublic void run() {// TODO Auto-generated method stubsuper.run();}}
public static void main(String[] args) {//创建线程获取对应的线程组对象,并获取名称MyThread mt1 = new MyThread();MyThread mt2 = new MyThread();//获取上面两个线程对应的线程组对象ThreadGroup tg1 = mt1.getThreadGroup();ThreadGroup tg2 = mt2.getThreadGroup();//输出线程组名称System.out.println(tg1.getName());System.out.println(tg2.getName());}

运行:



分析:这里给出的是默认情况,线程是属于主线程组,打印出来的线程组名称是main


5.线程池

5.1为什么要使用线程池?

程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。


5.2线程池的特点:

线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。

JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池


5.3线程池的创建:

JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法

public static ExecutorService newFixedThreadPool(int nThreads)


5.4线程池的使用步骤:

1.创建线程池对象

ExecutorService pool = Executors.newFixedThreadPool(2);


2.创建Runnable实例

MyRunnable my = new MyRunnable();


3.提交Runnable实例

pool.submit(my);


4.关闭线程池

pool.shutdown();


代码示例:

public class MyRunnable implements Runnable{@Overridepublic void run() {for (int i = 0; i < 100; i++) {if(i%2==0){System.out.println(Thread.currentThread().getName()+"=="+i);}}}}
public static void main(String[] args) {//1.创建线程池对象//ExecutorService pool = Executors.newFixedThreadPool(2);ExecutorService pool = Executors.newFixedThreadPool(2);//2.创建Runnable实例//MyRunnable my = new MyRunnable();MyRunnable my1 = new MyRunnable();MyRunnable my2 = new MyRunnable();//3.提交Runnable实例//pool.submit(my);pool.submit(my1);pool.submit(my2);//4.关闭线程池//pool.shutdown();pool.shutdown();}
运行:



分析:这里给出的代码是实现了Runnable接口,如果有返回值则可以实现Callable接口来实现线程池。


6.定时器 Timer

public Timer()构造


TimerTask

public abstract void run()放的是所要执行的任务代码


public boolean cancel()取消这个任务


public void schedule(TimerTask task, long delay)延迟多久执行任务

public class TimerTest01 {public static void main(String[] args) {//创建一个定时器Timer t = new Timer();//public void schedule(TimerTask task, long delay)延迟多久执行任务t.schedule(new Task(), 2000);}}class Task extends TimerTask{@Overridepublic void run() {System.out.println("to be or not bo be");}}

分析:延迟指定的时间执行Task里run方法的代码


public void schedule(TimerTask task,long delay,long period)延迟多久执行任务,并以后每隔多久执行一次

public class TimerTest02 {public static void main(String[] args) {//创建一个定时器Timer t = new Timer();//public void schedule(TimerTask task,long delay,long period)//延迟多久执行任务,并以后每隔多久执行一次t.schedule(new Task2(), 2000,1000);}}class Task2 extends TimerTask{@Overridepublic void run() {System.out.println("to be or not bo be");}}
运行:



分析:

三个参数:

参数1:要执行的任务

数2:延迟多久执行

参数3:执行一次之后,每隔多久重复执行