一、 多线程概念和作用
线程指进程中的一个执行场景,也就是执行流程,那么进程和线程的区别是什么
1.每个进程是一个应用程序,都有独立的内存空间
2.同一个进程中的线程共享其进程中的内存和资源
(共享的内存是堆内存和方法内存,栈内存不共享,每个线程有自己的堆内存)
进程:进程对应一个应用程序
现在的计算机都是支持多进程的,在同一个操作系统中,可以同时启动多个进程
多进程的作用:
* 单进程只能做一件事 : 一边玩游戏,一边听音乐不是同时运行,而是进程之间的频繁调度,切换速度极高,感觉是同时进行。
* 多线程的作用不是提高执行速度,而是提高CPU的使用率。进程和进程之间的内存是独立的、
* 线程:是进程中的执行场景。一个进程可以启动多个线程。
* 多线程的作用:不是为了提高执行速度,而是为了提高应用程序的使用率
* java程序的运行原理
* java命令启动java虚拟机,启动JVM,等于启动一个应用程序,表明启动一个进程。该进程会自动启动一个“主线程”,然后主线程去调用某各类的main方法。
* 所以,main方法运行在主线程中。在此之前的所有程序都是单线程的。
二、线程的创建和启动
*Java虚拟机的主线程入口是main方法,用户可以自己创建线程,创建方式有两种
*1.继承Thread类
*2.实现Runnable接口(推荐使用Runnable)
*继承Thread类
*采用 Thread类创建线程,用户只需要继承 Thread,覆盖 Thread中的run方法,父类 Thread中的run方法没有抛出异常,那么子类也不角能抛出异常,最后采用start启动线程即可
实现Runnable接口
Thread对象本身就实现了 Runnable接口,但一般建议直接使用 Runnable接口来写多线程程序,因为接口会比类带来更多的好处
三、java语言中实现多线程第一种方式
1.继承java.lang.Thread
2.重写run方法
三个知识点 :定义线程 、创建线程、 启动线程
package com.steven.demo; import java.lang.Thread; public class ThreadTest { public static void main(String[] args) { Thread thread = new Student(); //启动线程 thread.start();//打印Run:0~9 //start方法执行完瞬间结束,告诉JVM再分配一个新的线程 给t线程 //是随机分配的,没有规律 //run不需要手动调用,系统程序启动之后会自动调用方法 //thread.run();//这是普通方法的调用,这样做程序只有一个线程,run方法结束之后,下边的程序才会执行 for (int i = 0; i < 5; i++) { System.out.println("main"+i); } //有了多线程之后,main方法结束只是主线程中没有方法栈帧了 但是其他线程或者其他栈中还有栈帧 main方法结束,程序可能还在运行 } } class Student extends Thread { //重写Run方法 public void run() { for (int i = 0; i < 10; i++) { System.out.println("Run:"+i); } } }
四、java语言中实现多线程第二种方式
1.写一个类实现
2.重写run方法
package com.steven.demo; import java.lang.Runnable; import java.lang.Thread; public class ThreadTest02 { public static void main(String[] args) { //创建线程: Thread thread = new Thread(new Teacher()); //启动线程 thread.start(); } } class Teacher implements Runnable { //重写Run方法 public void run() { for (int i = 0; i < 10; i++) { System.out.println("Run:"+i); } } }
五、掌握线程方法:
1.获取当前线程的对象 Thread.currentThread()
2.给线程起名t.setName()
3.获取线程的名字:t.getName()
package com.steven.demo; public class ThreadTest03 { public static void main(String[] args) { //获取当前线程的对象 main主线程 Thread thread = Thread.currentThread(); //获取当前线程的名字 System.out.println("当前名称的名称"+thread.getName());//当前名称的名称main Thread t1 = new Thread(new ArrayTest()); //给线程起名 t1.setName("Steven"); t1.start();//线程的名称Steven Thread thread2 = new Thread(new ArrayTest()); //给线程重命名 thread2.setName("珂珂"); thread2.start();//线程的名称珂珂 } } class ArrayTest implements Runnable { public void run() { Thread thread = Thread.currentThread();//获取当前线程的对象 System.out.println("线程的名称"+thread.getName()); } }
六、线程的优先级
优先级高的获取CPU时间片,相对多一些
最高:10
最小:1
默认:5
优先级1-10
优先级高的线程,会得到CPU的时间多一些,优先执行完成
public class ThreadTest04 { public static void main(String[] args) { System.out.println("最高"+Thread.MAX_PRIORITY); System.out.println("最小"+Thread.MIN_PRIORITY); System.out.println("默认"+Thread.NORM_PRIORITY); Thread t1 = new Thread(new KeKe()); t1.setName("t1"); Thread t2 = new Thread(new KeKe()); t2.setName("t2"); //获取线程的优先级 System.out.println("t1优先级"+t1.getPriority()); System.out.println("t2优先级"+t2.getPriority()); //设置优先级 t1.setPriority(5); t2.setPriority(6); //启动 t1.start(); t2.start();//线程虽然有优先级,但是随机分配的,打印结果不一致 } } class KeKe extends Thread { public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName()+"----------------"+i); } } }
七、线程休眠
①Thread.sleep()使当前正在执行的线程执行休眠操作(暂停执行) 单位:毫秒
sleep 静态方法 作用: 阻塞当前线程,腾出CPU,让给其他线程
public class ThreadTest05 { public static void main(String[] args) { Thread thread = new Array1(); thread.setName("thread1"); thread.start(); //获取当前线程的对象 main主线程 Thread t = Thread.currentThread(); //获取当前线程的名字 System.out.println("当前名称的名称"+t.getName());//当前名称的名称main //阻塞主线程 for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "-------------------:" + i); try { Thread.sleep(2000);//程序休眠2秒钟 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } class Array1 extends Thread { public void run() { System.out.println("线程正在启动=-===="); for (int i = 0 ; i < 5 ; i ++) { System.out.println(Thread.currentThread().getName() + "-------------------:" + i); try { Thread.sleep(2000);//程序休眠2秒钟 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
②Thread.yield()暂停当前正在执行的线程对象,并执行其他线程。
1.静态方法
2.作用:给同一个优先级的线程让位,但是让位时间不固定
3.和sleep方法相同,就是yield的时间不固定
他与sleep类似,只是不能由用户执行暂停时间,并且yield()只能让同优先级的线程有执行的机会
package com.steven.demo; public class ThreadTest07 { public static void main(String[] args) { //1.创建线程 Thread thread = new HThread(); thread.setName("线程07"); //2.启动线程 thread.start(); //3.主线程 for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() +"---------:"+i); } System.out.println("Steven====="); } } class HThread extends Thread { public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() +"---------:"+i); if (i % 2 == 0) { //暂定当前线程,执行其他线程 Thread.yield(); } } } }
③线程的基本操作
/* 线程的基本操作:创建,启动,休眠,异常处理 */ public class ThreadTest06 { public static void main(String[] args) { try { //1.创建线程 Thread thread = new MyThread(); thread.setName("线程"); //2.启动线程 thread.start(); //3.休眠 Thread.sleep(2000); System.out.println("Steven====="); }catch (InterruptedException e) { e.printStackTrace(); } } } class MyThread extends Thread { public void run() { for (int i = 0; i < 10; i++) { try { Thread.sleep(3000); } catch (Exception e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() +"---------:"+i); } } }
八、线程的合并(join)
public class ThreadTest08 { public static void main(String[] args) { try { //1.创建线程 Thread thread = new KThread(); thread.setName("线程07"); //2.启动线程 thread.start(); //3.合并线程 (线程07和main线程合并) thread.join();//输出只保证一个线程正在执行,依次执行,单线程的程序 (先执行线程07后执行main) //主线程 for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() +"---------:"+i); } //当前线程可以调用第一个线程的join方法,调用后当前线程会被阻塞不再执行,直到被调用的线程执行完毕,当前线程才会执行 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } class KThread extends Thread { public void run() { for (int i = 0; i < 10; i++) { try { Thread.sleep(2000); System.out.println(Thread.currentThread().getName() +"---------:"+i); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); }; } } }
九、线程同步案例
<1>模拟取款:(不使用同步机制)多线程同时对同一个账号进行取款操作
/* 模拟取款:不使用同步机制,多线程同时对同一个账号进行取款操作 thread 和 thread2 异步编程模型:thread线程执行的是thread , thread2线程执行的是thread2 ,两个线程之间谁也不等于谁 同步编程模型:thread线程和thread2线程执行,当thread线程必须等thread2的线程的执行结果之后,thread线程才能执行 ,这是同步编程 什么时候需要引入同步: 1.为了数据安全,尽管程序的使用率低,但是为了保证数据安全性,必须得加入线程同步机制, 线程同步机制 使程序变成了单线程(一个线程) 2.在什么条件下需要使用线程同步 <1>必须是多线程环境 <2>多线程环境在共享同一个数据时 <3>共享的数据涉及到修改操作 */ package com.steven.demo; public class ThreadTest09 { public static void main(String[] args) { //创建一个公共的账户 Account account = new Account("steven_kou",10000.0); //创建线程对同一个账户进行取款 Thread thread = new Thread(new Money(account)); thread.setName("steven"); Thread thread2 = new Thread(new Money(account)); thread2.setName("kou"); thread.start(); thread2.start(); } } //取款线程 class Money implements Runnable { //账户 Account account; Money (Account account){ this.account = account; } public void run() { account.withDraw(2000.0); System.out.println("取款2000.0$,余额为:"+account.getBalance());//取款2000.0$,余额为:8000.0 (输出两次) } } //银行账户 class Account { private String actno; private double balance;//账户余额 public Account() {} public Account(String actno,double balance) { this.actno = actno;//成员变量|局部变量 this.balance = balance; } public void setActno(String actno) { this.actno = actno; } public String getActno() { return actno; } public void setBalance(double balance) { this.balance = balance; } public double getBalance() { return balance; } //对外提供一个取款方法 public void withDraw(double money) { //对账户进行取款操作 double after = balance - money; //延迟操作 try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace();//InterruptedException 线程中断下抛出的异常 } this.setBalance(after);//更新账户余额,重新赋值 } }
<2>模拟取款(同步机制,同步锁synchronized)
①以下程序使用线程同步机制保证数据安全
package com.steven.demo; public class ThreadTest09 { public static void main(String[] args) { //创建一个公共的账户 Account account = new Account("steven_kou",10000.0); //创建线程对同一个账户进行取款 Thread thread = new Thread(new Money(account)); thread.setName("steven"); Thread thread2 = new Thread(new Money(account)); thread2.setName("kou"); thread.start(); thread2.start(); } } //取款线程 class Money implements Runnable { //账户 Account account; Money (Account account){ this.account = account; } public void run() { account.withDraw(2000.0); System.out.println("取款2000.0$,余额为:"+account.getBalance());//取款2000.0$,余额为:8000.0 (输出两次) } } //银行账户 class Account { private String actno; private double balance;//账户余额 public Account() {} public Account(String actno,double balance) { this.actno = actno;//成员变量|局部变量 this.balance = balance; } public void setActno(String actno) { this.actno = actno; } public String getActno() { return actno; } public void setBalance(double balance) { this.balance = balance; } public double getBalance() { return balance; } //对外提供一个取款方法 //TODO 取款 同步 public void withDraw(double money) { /* 需要把同步的代码,放到同步的语句块中 thread线程执行到此处,遇到了synchronized 关键字,就会去找this的对象锁 如果找到了this的对象锁,则进入同步语句块 执行程序 当同步语句块代码执行结束的时候,thread线程归还this的对象锁 在thread线程执行同步语句块的过程中,如果thread2线程也执行以下代码。遇到synchronized 关键字,所以去找this对象锁,但是该对象被thread线程持有, 只能等待thread线程使用完以后再解锁this对象锁 */ //同步锁 synchronized (this) { //对账户进行取款操作 double after = balance - money; //延迟操作 try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace();//InterruptedException 线程中断下抛出的异常 } //更新账户余额,重新赋值 this.setBalance(after); } } }
②synchronized关键字 添加到成员方法上,线程拿走的也是this对象锁
//对外提供一个取款方法 //TODO 取款 同步 //synchronized关键字 添加到成员方法上,线程拿走的也是this对象锁 public synchronized void withDraw(double money) { /* 需要把同步的代码,放到同步的语句块中 thread线程执行到此处,遇到了synchronized 关键字,就会去找this的对象锁 如果找到了this的对象锁,则进入同步语句块 执行程序 当同步语句块代码执行结束的时候,thread线程归还this的对象锁 在thread线程执行同步语句块的过程中,如果thread2线程也执行以下代码。遇到synchronized 关键字,所以去找this对象锁,但是该对象被thread线程持有, 只能等待thread线程使用完以后再解锁this对象锁 */ //同步锁 // synchronized (this) { //对账户进行取款操作 double after = balance - money; //延迟操作 try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace();//InterruptedException 线程中断下抛出的异常 } //更新账户余额,重新赋值 this.setBalance(after); // } }
三、守护线程
①定义一个用户线程
package com.steven.demo; //定义一个用户线程 public class UserThread01 { public static void main(String[] args) { Runnable runnable = new UserTest(); Thread thread = new Thread(runnable, "UserThread"); thread.start(); for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "==== " + i); } System.out.println("主线程结束---"); } } class UserTest implements Runnable { public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "==== " + i); } } }
②改为守护线程
package com.steven.demo; //守护线程 //其他所有的用户线程结束,则守护线程退出 //守护线程一般都是无限执行的 守护线程最后结束(先执行用户线程) //设置守护线程以后,当前主线程结束后,守护线程并没有把所有的数据输出就结束 也就是说 守护线程是为用户线程服务的,当用户线程全部结束,守护线程会自动化结束 public class UserThread02 { public static void main(String[] args) { Thread thread = new UserTest02(); thread.setName("thread"); //将thread这个用户线程 修改为守护线程 thread.setDaemon(true); thread.start(); //主线程 for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "==== " + i); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println("主线程结束---"); } } class UserTest02 extends Thread { public void run() { int i = 0; while (true) { i ++; System.out.println(Thread.currentThread().getName() +"====:"+i); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
四、定时器简单使用:
package com.steven.demo; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Timer; import java.util.TimerTask; /* Timer 定时器 */ public class TimerTest01 { public static void main(String[] args) { System.out.println("来了"); Timer timer = new Timer(); try { Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2018-10-07 19:26:02"); //安排在指定的时间执行指定的任务 timer.schedule(new MyTimer(), date,1000*60*60*24);//date执行后续任务的时间间隔 设置定时任务:在2018-10-07 19:26:02执行此任务, 24小时执行一次 //timer.schedule(new MyTimer(), date,1000*2); //如果设置每天执行一次:Date date = new SimpleDateFormat("HH:mm:ss").parse("19:26:02"); }catch (ParseException e) { e.printStackTrace(); } catch (NullPointerException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalStateException e) { e.printStackTrace(); } } } class MyTimer extends TimerTask { public void run() { System.out.println("=="+new Date());//==Sun Oct 07 19:26:02 CST 2018 } }