行业网站模版,网架生产企业,网站建设应列入啥费用,培训网站建设学校目录前言核心概念线程创建继承Thread类实现Runnable接口上述两个方法小结实现Callable接口并发问题简介静态代理模式线程状态线程停止#xff08;stop#xff09;线程休眠#xff08;sleep#xff09;线程礼让#xff08;yield#xff09;线程强制执行#xff08;joinstop线程休眠sleep线程礼让yield线程强制执行join线程状态观测线程优先级守护线程后续内容见《Java学习笔记5-2》前言
Process与Thread
程序programm是指令和数据的有序集合其本身没有任何运行的含义是一个静态的概念。而进程process是执行程序的一次执行过程它是一个动态的概念。是系统资源分配的单位。通常在一个进程中可以包含若干个线程thread当然一个进程中至少有一个线程不然没有存在的意义。线程是CPU调度和执行的单位。
很多多线程是模拟出来的真正的多线程是指有多个CPU。
核心概念
线程就是独立的执行路径在程序运行时即使没有自己创建线程后台也会有多个线程如主线程gc线程main()称之为主线程为系统的入口用于执行整个程序在一个进程中如果开辟了多个线程线程的运行由调度器安排调度调度器是与操作系统紧密相关的先后顺序是不能人为干预的。对同一份资源操作时会存在资源抢夺的问题需要加入并发控制线程会带来额外的开销如cpu调度时间并发控制开销每个线程在自己的工作内存交互内存控制不当会造成数据不一致
线程创建
三种创建线程的方式
继承Thread类【重点】实现Runnable接口【重点】实现Callable接口
继承Thread类
创建线程方式1继承Thread类重写run()方法调用start开启线程。 public class Thread extends Object implements Runnable 构造方法 Thread(Runnable target) 分配一个新的 Thread对象。 Thread(Runnable target, String name) 分配一个新的 Thread对象具有指定的name作为其名称 常用方法 void start() 使此线程开始执行; JVM调用此线程的run方法。
简单例子
//创建线程方式1继承Thread类重写run()方法调用start开启线程
//总结注意线程开启不一定立即执行由CPU调度执行
public class ThreadTest1 extends Thread{Overridepublic void run() {//run方法线程的执行体for (int i 0; i 200; i) {System.out.println(我在学线程---i);}}public static void main(String[] args) {//main线程主线程//创建一个线程对象ThreadTest1 tt new ThreadTest1();//调用start()tt.start();for (int i 0; i 1000; i) {System.out.println(主我在看代码---i);}}
}网图下载例子
//练习Thread实现多线程同步下载图片
public class TestThread2 extends Thread{private String url;//网络图片地址private String name;//保存的文件名public TestThread2(String url,String name){this.url url;this.name name;}// 下载图片线程的执行体Overridepublic void run() {WebDownloader webDownloader new WebDownloader();webDownloader.downloader(url,name);System.out.println(下载了文件名为name的文件);}public static void main(String[] args) {TestThread2 t1 new TestThread2(https://www.kuangstudy.com/assert/course/c1/01.jpg,图片1.jpg);TestThread2 t2 new TestThread2(https://www.kuangstudy.com/assert/course/c1/02.jpg,图片2.jpg);TestThread2 t3 new TestThread2(https://www.kuangstudy.com/assert/course/c1/03.jpg,图片3.jpg);t1.start();t2.start();t3.start();}
}
//下载器
class WebDownloader{//下载方法public void downloader(String url,String name){try {FileUtils.copyURLToFile(new URL(url),new File(name));} catch (IOException e) {e.printStackTrace();System.out.println(IO异常Downloader出现问题);}}
}实现Runnable接口
创建线程方式2实现Runnable接口重写run方法执行线程需要丢入runnable接口实现类调用start方法。
public interface Runnable Runnable接口应由任何类实现其实例将由线程执行。 该类必须定义一个无参数的方法称为run 。
简单例子
//创建线程方式2实现Runnable接口重写run方法执行线程需要丢入runnable接口实现类调用start方法
public class ThreadTest3 implements Runnable{Overridepublic void run() {//run方法线程体for (int i 0; i 200; i) {System.out.println(我在学线程---i);}}public static void main(String[] args) {//创建Runnable接口的实现类对象ThreadTest3 tt new ThreadTest3();//创建线程对象通过线程对象来开启我们的线程代理new Thread(tt).start();for (int i 0; i 1000; i) {System.out.println(主我在看代码---i);}}
}上述两个方法小结
继承Thread类
子类继承Thread类具备多线程能力启动线程子类对象.start()不建议使用。为了避免面向对象单继承局限性
实现Runnable接口
实现接口Runnable具有多线程能力启动线程传入目标对象Thread对象.start()推荐使用。能避免单继承局限性灵活方便方便同一对象被多个线程使用。 例如
//一份资源
ThreadTest tt new ThreadTest();//多个代理
new Thread(tt,张三).start();
new Thread(tt,李四).start();
new Thread(tt,你好).start();代码例子龟兔赛跑作为总结
//模拟龟兔赛跑
public class Race implements Runnable{//胜利者private static String winner;Overridepublic void run() {for (int i 0; i 100; i) {//模拟兔子睡觉if (Thread.currentThread().getName().equals(兔子) i%100){try {Thread.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}//判断比赛是否结束boolean flag gameOver(i);if (flag){break;}System.out.println(Thread.currentThread().getName()跑了i步);}}//判断是否完成比赛private boolean gameOver(int steps){//判断是否有胜利者if (winner!null){//已经存在胜利者return true;}{if (steps100){winnerThread.currentThread().getName();System.out.println(winner iswinner);return true;}}return false;}public static void main(String[] args) {Race race new Race();new Thread(race,乌龟).start();new Thread(race,兔子).start();}
}
实现Callable接口
实现Callable接口需要返回值类型重写call方法需要抛出异常创建目标对象创建执行服务ExecutorService ser Executors.newFixedThreadPool(1);提交执行Future\Boolean\ result1 ser.submit(t1);获取结果boolean r1 result1.get();关闭服务ser.shutdownNow();
网图下载例子
//线程创建方式3实现Callable接口
//Callable的好处1.可以定义返回值2.可以抛出异常public class CallableTest implements CallableBoolean {private String url;//网络图片地址private String name;//保存的文件名public CallableTest(String url,String name){this.url url;this.name name;}// 下载图片线程的执行体Overridepublic Boolean call() {WebDownloader1 webDownloader new WebDownloader1();webDownloader.downloader(url,name);System.out.println(下载了文件名为name的文件);return true;}public static void main(String[] args) throws ExecutionException, InterruptedException {CallableTest ct1 new CallableTest(https://www.kuangstudy.com/assert/course/c1/01.jpg,c图片1.jpg);CallableTest ct2 new CallableTest(https://www.kuangstudy.com/assert/course/c1/02.jpg,c图片2.jpg);CallableTest ct3 new CallableTest(https://www.kuangstudy.com/assert/course/c1/03.jpg,c图片3.jpg);// 创建执行对象ExecutorService ser Executors.newFixedThreadPool(3);// 提交执行FutureBoolean r1 ser.submit(ct1);FutureBoolean r2 ser.submit(ct2);FutureBoolean r3 ser.submit(ct3);// 获取结果boolean rs1 r1.get();boolean rs2 r2.get();boolean rs3 r3.get();System.out.println(rs1);System.out.println(rs2);System.out.println(rs3);// 关闭服务ser.shutdownNow();}
}
// 下载器
class WebDownloader1{// 下载方法public void downloader(String url,String name){try {FileUtils.copyURLToFile(new URL(url),new File(name));} catch (IOException e) {e.printStackTrace();System.out.println(IO异常Downloader出现问题);}}
}
并发问题简介
// 多个线程同时操作同一个对象
// 买火车票的例子
public class ThreadTest4 implements Runnable{// 票数private int ticketNums 10;Overridepublic void run() {while (true){if (ticketNums0){break;}//模拟延时try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()拿到了第ticketNums--张票);}}public static void main(String[] args) {ThreadTest4 ticket new ThreadTest4();new Thread(ticket,小明).start();new Thread(ticket,老师).start();new Thread(ticket,黄牛党).start();}
}结果 小明拿到了第10张票 老师拿到了第8张票 黄牛党拿到了第9张票 黄牛党拿到了第7张票 小明拿到了第7张票 老师拿到了第7张票 黄牛党拿到了第6张票 … 可见发生了多个人取到同一张票
静态代理模式
静态代理模式总结
真实对象和代理对象都要实现同一个接口代理对象要代理真实角色
好处 1.代理对象可以做很多真实对象做不了的事情 2.真实对象专注做自己的事情 public class StaticProxy {public static void main(String[] args) {new Thread( ()- System.out.println(我爱你) ).start();// Thread也实现了Runnable接口所以代理了Runnable接口Thread调用了start方法Runnable接口只有run方法Thread调用了start方法时虚拟机调用了run方法// 相当于WeddingCompany代理了Marry调用了HappyMarry方法new WeddingCompany(new You()).HappyMarry();;}
}interface Marry{void HappyMarry();
}// 真实角色你。 你去结婚
class You implements Marry{Overridepublic void HappyMarry() {System.out.println(结婚了好开心);}
}// 代理角色婚庆公司。 帮助你办婚礼
class WeddingCompany implements Marry{// 代理谁--真实目标角色private Marry target;public WeddingCompany(Marry target) {this.target target;}Overridepublic void HappyMarry() {before();this.target.HappyMarry();//这是真实对象after();}private void before() {System.out.println(结婚之前布置现场);}private void after() {System.out.println(办完婚礼后付尾款);}
}线程状态
线程停止stop
建议线程正常停止自己停下来。即利用次数不建议死循环建议使用标志位作为终止当flagfalse则线程终止运行不要使用stop或者destroy等过时或不建议使用的方法
基本步骤
线程中定义线程体使用的标识线程体使用该标识对外提供方法改变标识
例子
public class TestStop implements Runnable{// 1.设置一个标志位private boolean flag true;Overridepublic void run() {int i 0;while (flag){System.out.println(run...Threadi);}}// 2.设置一个公开的方法停止线程转换标志位public void stop(){this.flag false;}public static void main(String[] args) {TestStop testStop new TestStop();new Thread(testStop).start();for (int i 0; i 1000; i) {System.out.println(maini);if (i900) { // 主线程到i900的时候才停止线程testStop.stop();System.out.println(线程停止了);}}}
}输出 … main900 run…Thread662 线程停止了 main901 … 线程休眠sleep
sleep(毫秒) 指定当前线程阻塞的毫秒数sleep存在异常InterruptedExceptionsleep时间达到后线程进入就绪状态sleep可以模拟网络延时倒计时等每一个对象都有一个锁sleep不会释放锁
例子1在前面的了解并发问题的买票例子中可以加入延时Thread.sleep(100);,从而模拟网络延迟这样可以放大问题的发生性。
例子2模拟倒计时。
public class Countdown {public static void main(String[] args) {try {countdown();} catch (InterruptedException e) {e.printStackTrace();}}//模拟倒计时public static void countdown() throws InterruptedException {int num 10;while (true){Thread.sleep(1000);System.out.println(num--);if (num0){break;}}}
}例子3打印当前系统时间 public class SystemTime {public static void main(String[] args) throws InterruptedException {//打印当前系统时间Date time new Date(System.currentTimeMillis());//获取当前系统时间while(true){try {Thread.sleep(1000);System.out.println(new SimpleDateFormat(HH:mm:ss).format(time));time new Date(System.currentTimeMillis());//更新当前时间} catch (InterruptedException e) {e.printStackTrace();}}}}
线程礼让yield
礼让线程让当前正在执行的线程暂停但不阻塞将线程从运行状态转为就绪状态注意让CPU重新调度礼让不一定成功看CPU心情
public class YieldTest {public static void main(String[] args) {MyYield myYield new MyYield();new Thread(myYield,A).start();new Thread(myYield,B).start();}
}class MyYield implements Runnable{Overridepublic void run() {System.out.println(Thread.currentThread().getName()线程开始执行);Thread.yield();//礼让System.out.println(Thread.currentThread().getName()线程停止执行);}
}输出礼让不成功 A线程开始执行 A线程停止执行 B线程开始执行 B线程停止执行 输出礼让成功 A线程开始执行 B线程开始执行 A线程停止执行 B线程停止执行 线程强制执行join
join合并线程待此线程执行完成后再执行其他线程其他线程阻塞可以想象成插队
public class JoinTest implements Runnable{Overridepublic void run() {for (int i 1; i 1000; i) {System.out.println(线程vip来了i);}}public static void main(String[] args) throws InterruptedException {Thread thread new Thread(new JoinTest());thread.start();for (int i 0; i 500; i) {if(i200){thread.join();//主线程跑到200的时候插队,插队者跑完1000才继续其他的线程包括主线程}System.out.println(maini);}}
}
线程状态观测 线程状态。 线程可以处于以下状态之一
NEW 尚未启动的线程处于此状态。RUNNABLE 在Java虚拟机中执行的线程处于此状态。BLOCKED 被阻塞等待监视器锁定的线程处于此状态。WAITING 正在等待另一个线程执行特定动作的线程处于此状态。TIMED_WAITING 正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。TERMINATED 已退出的线程处于此状态。
一个线程可以在给定时间点处于一个状态。 这些状态是不反映任何操作系统线程状态的虚拟机状态。
// 观察线程的状态
public class StateTest {public static void main(String[] args) throws InterruptedException {Thread thread new Thread(()-{for (int i 0; i 5; i) {// 线程启动后睡五秒钟try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(干活);});// 观察状态Thread.State state thread.getState();System.out.println(state);// 观察new了一个线程后的状态// 观察启动后thread.start();// 启动线程state thread.getState();System.out.println(state);// 观察线程run后的状态while(state!Thread.State.TERMINATED){ // 只要线程不终止就一直输出状态Thread.sleep(100);state thread.getState();// 更新线程状态System.out.println(state);// 输出状态}}
}
输出 NEW RUNNABLE TIMED_WAITING TIMED_WAITING … TIMED_WAITING 干活 TERMINATED 【注意】线程start后不一定立即执行而是处于RUNNABLE状态即就绪状态。线程休眠的5秒钟内一直是TIMED_WAITING状态睡醒之后就干活干完活就线程终止
线程优先级 Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程线程调度器按照优先级决定应该调度哪个线程来执行。 线程的优先级用数字表示范围从1~10数字越大优先级越高 Thread.MIN_PRIORITY 1; Thread.MAX_PRIORITY 10; Thread.NORM_PRIORITY 5; 【注意】优先级低只是意味着获得调度的概率低。并不是优先级低就不会被先调用这都是取决于CPU。 使用以下方式改变或者获取优先级 getPriority() setPriority(int x) 如果需要设置优先级先设定好优先级再start() // 测试线程的优先级
public class PriorityTest {public static void main(String[] args) {System.out.println(Thread.currentThread().getName()主线程优先级:Thread.currentThread().getPriority());MyPriority myPriority new MyPriority();Thread t1 new Thread(myPriority);Thread t2 new Thread(myPriority);Thread t3 new Thread(myPriority);Thread t4 new Thread(myPriority);// 先设置优先级 再启动t1.start();t2.setPriority(1);t2.start();t3.setPriority(4);t3.start();t4.setPriority(Thread.MAX_PRIORITY);// MAX_PRIORITY10t4.start();}
}class MyPriority implements Runnable{Overridepublic void run() {System.out.println(Thread.currentThread().getName()优先级:Thread.currentThread().getPriority());}
}守护线程
线程分为用户线程和守护线程虚拟机必须确保用户线程执行完毕虚拟机不用等待守护线程执行完毕守护线程如后台记录操作日志监控内存垃圾回收等
public class DaemonTest {public static void main(String[] args) {God god new God();You1 you new You1();Thread thread new Thread(god);thread.setDaemon(true); // 默认是false表示用户线程一般都是用户线程thread.start();// 上帝守护线程启动new Thread(you).start();// 你 用户线程启动}
}// 上帝
class God implements Runnable{Overridepublic void run() {while (true){System.out.println(上帝保佑你);}}
}// 你
class You1 implements Runnable{Overridepublic void run() {for (int i 0; i 36500; i) {System.out.println(你一生幸福快乐地活着);}System.out.println(goodbye world);}
}用户线程停止之后守护线程还在运行因为虚拟机停止需要一段时间。
后续内容见《Java学习笔记5-2》