目录
1、线程创建的方式一:继承Thread类(Thread类也是实现了Runnable接口的)
···程序是指令和数据的有序集合,没有执行的含义,是一个静态的概念。
···而进程则是程序的一次执行过程,进程是系统分配资源的基本单位。
···通常一个进程可以分为多个线程,线程是CPU调度的基本单位。
(1)第一步:继承Thread类
(2)第二步:重写run()方法;run()方法的函数体即为线程体。
(3)第三步:调用start()方法;通过调用start()方法来使线程跑起来。
//线程创建的方式一:继承Thread类//第一步:继承Thread类public class testThread extends Thread{ //第二步:重写run()方法 @Override public void run() { //线程体 for(int i=1;i<=100;i++) { System.out.println("我是小线程----"+i); } } public static void main(String[] args){ testThread t=new testThread(); //第三步调用start()方法 t.start(); //主函数即主线程 for(int i=1;i<=200;i++) { System.out.println("我是主线程----"+i); } }}
可以看到主线程和我们创建的线程是同时执行的。
总结:线程开启不一定立即执行,由CPU进行调度执行!
第一步:定义一个线程类实现Runnable接口。
第二步:重写run()方法,方法内部为线程执行体。
第三步:创建Thread的对象,并将Runnable接口的实现对象作为参数传入Thread的构造函数中,最后再调用start()方法进行线程的开启。
//线程创建的方式二:实现Runnable接口//第一步:implements Runnable接口public class testThread implements Runnable{ //第二步:重写run()方法 @Override public void run() { //线程体 for(int i=1;i<=100;i++) { System.out.println("我是小线程----"+i); } } public static void main(String[] args){ //第三步:创建Thread类,并将Runnable接口的实现类作为参数传给Thread,最后调用start()方法启动线程 testThread t=new testThread(); Thread tt=new Thread(t); tt.start(); //主函数即主线程 for(int i=1;i<=200;i++) { System.out.println("我是主线程----"+i); } }}
由运行结果可知,主线程和小线程也是同时执行的。
总结:继承Thread方法也是通过实现Runnable接口实现的,但由于java的单继承机制,推荐使用继承Runnable接口的方法。
第一步:创建Callable接口的实现类,并通过泛型定义返回值
第二步:重写call()方法,call()方法内部即为线程执行体
第三步:创建执行服务,使用ExecutorService ser =Executors.newFixedThreadPool(线程数量);
第四步:提交执行,例如:Future<Boolean> r1=ser.submit(线程名);
第五步:读取结果,例如:Boolean rs1=r1.get();
第六步:关闭服务,例如:ser.shutdownNow();
import java.util.concurrent.*; //线程创建的方式三:实现Callable接口//第一步:implements Callable接口public class testThread implements Callable<Boolean> { //第二步:重写call()方法 @Override public Boolean call() { //线程体 for(int i=1;i<=100;i++) { System.out.println("我是小线程----"+i); } return true; } public static void main(String[] args){ testThread t1=new testThread(); //第三步:创建执行服务 ExecutorService ser = Executors.newFixedThreadPool(1); //第四步:提交执行 Future<Boolean> r1=ser.submit(t1); //主函数即主线程 for(int i=1;i<=200;i++) { System.out.println("我是主线程----"+i); } //第五步:获取结果 try { Boolean rs1=r1.get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); System.out.println("get方法出错!!!"); } //第六步:关闭服务 ser.shutdownNow(); }}
Callable的好处:
(1)可以定义返回值
(2)可以抛出异常
不推荐适应jdk自带的stop()和destory()方法,因为已经弃用,推荐使线程自己停下来,建议使用标志位来进行终止变量,当flag=false时终止线程运行。
//线程停止public class testThread implements Runnable { //设置一个标志位来进行线程的停止 private boolean flag=true; @Override public void run() { int i=0; while(flag) { System.out.println("子线程已经运行"+i+"%"); i++; } System.out.println("子线程已经停止了!"); } //手动写一个改变线程运行标志位的方法stop() public void stop(){ this.flag=false; } public static void main(String[] args){ testThread t1=new testThread(); Thread thread=new Thread(t1); thread.start(); for(int i=0;i<=100;i++) { System.out.println("主线程----"+i+"%"); if(i==98) t1.stop(); } } }
*每个对象都有一把锁,sleep不会释放锁!
sleep(休眠时间),当线程调用sleep()方法,线程就会进入阻塞状态,线程的sleep()方法常用于计时。
import java.text.SimpleDateFormat;import java.util.Date; //线程休眠//主线程调用sleep()来动态实现显示当前时间public class testThread { private static Date date; public static void main(String[] args){ while (true){ date=new Date(System.currentTimeMillis()); //获取当前时间 String time=new SimpleDateFormat("hh:mm:ss").format(date); System.out.println(time); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }}
线程调用yield()方法后,线程不会回到阻塞状态,但会回到就绪状态,线程礼让不一定成功,主要看CPU调度。
//线程礼让public class testThread { public static void main(String[] args){ myThread m1=new myThread(); myThread m2=new myThread(); Thread t1=new Thread(m1,"线程1"); Thread t2=new Thread(m2,"线程2"); t1.start(); t2.start(); //不礼让的输出结果为: // 线程1开始执行! // 线程1执行完毕! // 线程2开始执行! // 线程2执行完毕! }} class myThread implements Runnable{ @Override public void run(){ System.out.println(Thread.currentThread().getName()+"开始执行!"); Thread.yield(); System.out.println(Thread.currentThread().getName()+"执行完毕!"); } }
线程调用join方法即可强制执行,待该线程执行完毕后再执行其他线程,可以想象为插队,尽量少用,因为容易引起线程阻塞。
//线程强制执行public class testThread { public static void main(String[] args) throws InterruptedException { //通过Thread对Runnable接口的实现类进行代理 myThread mt1=new myThread(); Thread thread=new Thread(mt1); thread.start(); //定义主线程 for(int i=0;i<20;i++) { if(i==12) thread.join(); System.out.println("主线程+"+i); } }} //实现Runnable接口class myThread implements Runnable{ //重写run()方法 @Override public void run(){ for(int i=0;i<10;i++) { System.out.println("子线程执行"+i*10+"%"); } } }
通过调用线程的getState()方法即可获取线程的五种状态。
//线程状态观测public class testThread { public static void main(String[] args) throws InterruptedException { //通过Thread对Runnable接口的实现类进行代理 myThread mt1=new myThread(); Thread thread=new Thread(mt1); System.out.println(thread.getState()); //NEW thread.start(); Thread.State state=thread.getState(); System.out.println(state); //RUN while (state!= Thread.State.TERMINATED){ Thread.sleep(100); state=thread.getState(); System.out.println(state); //WAITING } //TERMINATED }} //实现Runnable接口class myThread implements Runnable{ //重写run()方法 @Override public void run(){ for(int i=0;i<5;i++){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }
java线程的调度时通过线程调度器来按照线程的优先级来决定哪个线程优先执行,线程的优先级用数字进行表示,范围从1-10,数字越大优先级就越高。但不是优先级高的就一定会被优先执行,可能会出现性能倒置的情况,具体先执行哪个线程有CPU决定。java线程的优先级可以通过getPriority()和setPriority()方法来进行控制。
//线程优先级public class testThread { public static void main(String[] args) throws InterruptedException { //通过Thread对Runnable接口的实现类进行代理 myThread mt=new myThread(); Thread t1=new Thread(mt); Thread t2=new Thread(mt); Thread t3=new Thread(mt); Thread t4=new Thread(mt); //设置优先级 t1.setPriority(4); t2.setPriority(9); t3.setPriority(6); t4.setPriority(1); t1.start(); t2.start(); t3.start(); t4.start(); }} //实现Runnable接口class myThread implements Runnable{ //重写run()方法 @Override public void run(){ System.out.println(Thread.currentThread().getName()+"--->优先级"+Thread.currentThread().getPriority()); } }
线程分为用户线程和守护线程,虚拟机必须确保用户线程执行完毕,但不用等待守护线程执行完毕。setDaemon(true or false)方法来设置守护线程,true为守护线程,false为用户线程,一般的线程都是用户线程。
1、使用同步方法,即在方法前加关键字synchronized关键字,例如:
public synchronized void method(int args){}
若将一个大的方法声明为synchronized将会影响效率。
2、使用同步块:synchronized(Obj){}
Obj称之为同步监视器:
Obj可以使任何对象,但是推荐使用共享资源作为同步监视器
同步方法中无需指定同步监视器,因为同步方法的监视器就是this,就是这个对象本身
同步监视器的执行过程:
(1)第一个线程访问,锁定同步监视器,执行其中代码。
(2)第二个线程访问,发现同步监视器被锁定,无法访问。
(3)第一个线程访问完毕,解锁同步监视器
(4)第二个线程访问,发现同步监视器没有锁,然后锁定并访问。
1、管程法
2、信号灯法
思路:提前创建好多个线程,放入线程池,使用时直接获取,使用完返回池中,可以避免频繁创建和销毁,实现重复利用,类似生活中的交通工具。
好处:
1、提高了响应速度(减少了创建新线程的时间)
2、降低资源消耗(重复利用线程池中的线程,不需要每次都创建)
3、便于线程管理:
corePoolSize:核心池的大小。
maxmumPoolSize:最大线程数
keepAliveTime:线程没有任务时最多保持多长时间后会终止
使用:
线程池相关API:ExecutorService和Executors
ExecutorService:真正的线程池接口,常见子类ThreadPoolExecutor:
1、创建线程池:ExecutorService s = Executors.newFixedThreadPool(线程数)
2:执行线程:void execute(Runnable command):执行任务、命令,没有返回值。
3、关闭线程池:shutdown():关闭连接
原网址: 访问
创建于: 2022-10-30 16:20:51
目录: default
标签: 无
未标明原创文章均为采集,版权归作者所有,转载无需和我联系,请注明原出处,南摩阿彌陀佛,知识,不只知道,要得到
最新评论