进程:正在进行中的程序
线程:就是进程中一个负责程序执行的控制单元(执行路径。)
一个进程中可以有多条执行路径,称为多线程。(比如,360软件,可同时杀毒,体检,清理垃圾等等,每一个功能相当于一条执行路径,同时执行,执行多条路径,也就是多线程了)。
也就是说,当我们想让多部分代码同时执行的时候,就是多线程了。每一个线程都有自己运行的内容,这个内容可以称为线程要执行的任务。
好处与弊端:
好处:解决了多部分同时运行的问题,充分利用CPU资源,简化编程模型,带来良好的用户体验。
弊端:线程太多回到效率的降低。
其实应用程序的执行都是cpu在做着快速的切换完成的,这个切换时随机的。
类 Thread:
主线程:
main()方法即为主线程入口
产生其他子线程的线程
必须最后完成执行,因为它执行各种关闭动作
Thread td = Thread.currentThread(); System.out.println("当前线程是:"+td.getName()); td.setName("萌萌"); System.out.println("当前线程是:"+td.getName());
运行结果:
当前线程是:main 当前线程是:萌萌
在java中实现多线程的两种方式:
继承Thread类,实现Runnable接口。
class MyThread implements Runnable class MyRunnable implements Runnable
Thread类构造方法:
Thread类常用方法:
start(); | 启动线程 |
getId(); | 获得线程ID |
getName(); | 获得线程名字 |
getPriority(); | 获得优先权 |
isAlive(); | 判断线程是否活动 |
isDaemon(); | 判断是否守护线程 |
getState(); | 获得线程状态 |
sleep(long mill); | 休眠线程 |
join(); | 等待线程结束 |
yield(); | 放弃cpu使用权利 |
interrupt(); | 中断线程 |
currentThread(); | 获得正在执行的线程对象 |
Runnable接口:
Runnable接口方法:
run()方法和start()方法的区别:
Tread类中start()方法是开始线程的方法。start()方法会用特殊的方法自动调用run()方法。run()方法是Tread的具体实现。
继承了Thread类后就通过重写run()方法来说明线程的行为,调用start()方法来开始线程。
小示例:这里使用了JUNIT测试类,java里自带的,省略了main方法
public class Demo { @Test public void test1(){ MyThread t1 = new MyThread(); Thread td = new Thread(t1); td.start(); } } class MyThread implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { String name = Thread.currentThread().getName(); System.out.println(name+":"+i); } System.out.println("结束~"); } }
输出结果:这个输出结果每次都不一样,多线程是随机的。
Thread-0:0 Thread-0:1 Thread-0:2 Thread-0:3 Thread-0:4 Thread-0:5 Thread-0:6 Thread-0:7 Thread-0:8 Thread-0:9 Thread-0:10 Thread-0:11 Thread-0:12 Thread-0:13 Thread-0:14 Thread-0:15 Thread-0:16 Thread-0:17 Thread-0:18 Thread-0:19 Thread-0:20 Thread-0:21 Thread-0:22 Thread-0:23 Thread-0:24 Thread-0:25 Thread-0:26 Thread-0:27 Thread-0:28 Thread-0:29 Thread-0:30 Thread-0:31 Thread-0:32 Thread-0:33 Thread-0:34 Thread-0:35 Thread-0:36 Thread-0:37 Thread-0:38 Thread-0:39 Thread-0:40 Thread-0:41 Thread-0:42 Thread-0:43
小修改一下:
public class Demo { @Test public void test1(){ MyThread t1 = new MyThread(); Thread td1 = new Thread(t1,"线程1"); Thread td2= new Thread(t1,"线程2"); td1.start(); td2.start(); } } class MyThread implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { String name = Thread.currentThread().getName(); System.out.println(name+":"+i); } System.out.println("结束~"); } }
部分输出结果:随机的
线程1:45 线程1:46 线程1:47 线程2:0 线程1:48 线程2:1 线程1:49 线程2:2 线程1:50 线程2:3 线程1:51 线程2:4 线程1:52 线程2:5 线程1:53 线程2:6 线程1:54 线程2:7 线程1:55 线程2:8 线程1:56
线程的状态(五个):创建、就绪、阻塞、运行、死亡。
线程调度:线程带的优先级1-10表示,1表示优先级最低,10表示优先级最高,默认是5。这些优先级对象一个Thread类的公用静态变量。
public static final int NORM_PRIORITY = 5; public static final int MAX_PRIORITY = 10; public static final int MIN_PRIORITY = 1;
优先级示例:
public class Demo { @Test public void test1(){ MyThread t1 = new MyThread(); Thread td1 = new Thread(t1,"线程1"); Thread td2= new Thread(t1,"线程2"); System.out.println(td1.getPriority());//获取优先级 System.out.println(td2.getPriority()); td1.start(); td2.start(); } } class MyThread implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { String name = Thread.currentThread().getName(); System.out.println(name+":"+i); } System.out.println("结束~"); } }
输出结果,优先级默认都是5:
5 5 线程1:0 线程1:1 线程1:2 线程1:3 线程1:4 线程1:5 线程1:6 线程1:7 线程1:8 线程1:9 线程1:10 线程2:0 线程1:11 线程2:1 线程1:12 线程2:2 线程1:13 线程2:3 线程1:14 线程2:4 线程1:15 线程2:5 线程1:16 线程2:6 线程1:17 线程2:7 线程1:18 线程2:8 线程1:19 线程2:9 线程1:20 线程2:10 线程1:21 线程2:11 线程1:22 线程2:12 线程1:23 线程2:13 线程1:24 线程2:14 线程1:25 线程2:15 线程1:26 线程2:16 线程1:27 线程2:17 线程1:28 线程2:18 线程2:19 线程1:29 线程2:20 线程1:30 线程2:21 线程1:31 线程2:22 线程1:32 线程2:23 线程1:33
小修改一下:
public class Demo { @Test public void test1(){ MyThread t1 = new MyThread(); Thread td1 = new Thread(t1,"线程1"); Thread td2= new Thread(t1,"线程2"); td1.setPriority(10);//指定优先级 td2.setPriority(1); td1.start(); td2.start(); } } class MyThread implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { String name = Thread.currentThread().getName(); System.out.println(name+":"+i); } System.out.println("结束~"); } }
输出结果,优先级10和1的区别:
线程1:0 线程1:1 线程1:2 线程1:3 线程1:4 线程1:5 线程1:6 线程1:7 线程1:8 线程1:9 线程1:10 线程1:11 线程1:12 线程1:13 线程1:14 线程1:15 线程1:16 线程1:17 线程1:18 线程1:19 线程1:20 线程1:21 线程1:22 线程1:23 线程1:24 线程1:25 线程1:26 线程1:27 线程1:28 线程1:29 线程1:30 线程1:31 线程1:32 线程1:33 线程1:34 线程1:35 线程1:36 线程1:37 线程1:38 线程1:39 线程1:40 线程1:41 线程1:42 线程1:43 线程1:44 线程1:45 线程1:46 线程1:47 线程1:48 线程1:49 线程1:50 线程1:51 线程1:52 线程1:53 线程1:54 线程1:55 线程1:56 线程1:57 线程1:58 线程1:59 线程1:60 线程1:61 线程1:62 线程1:63 线程1:64 线程1:65 线程1:66 线程1:67 线程1:68 线程1:69 线程1:70 线程1:71 线程1:72 线程1:73 线程1:74 线程1:75 线程1:76 线程1:77 线程1:78 线程1:79 线程1:80 线程1:81 线程1:82 线程1:83 线程1:84 线程1:85 线程1:86 线程1:87 线程1:88 线程1:89 线程1:90 线程1:91 线程1:92 线程1:93 线程1:94 线程1:95 线程1:96 线程1:97 线程1:98 线程1:99 结束~ 线程2:0 线程2:1 线程2:2 线程2:3
线程的休眠,sleep():
sleep() 的作用是让当前线程休眠,即当前线程会从“运行状态”进入到“休眠(阻塞)状态”。
sleep()会指定休眠时间,线程休眠的时间会大于/等于该休眠时间;
在线程重新被唤醒时,它会由“阻塞状态”变成“就绪状态”,从而等待cpu的调度执行。
sleep()与wait()的比较:
wait()的作用是让当前线程由“运行状态”进入“等待(阻塞)状态”的同时,也会释放同步锁。
而sleep()的作用是也是让当前线程由“运行状态”进入到“休眠(阻塞)状态”。
但是,wait()会释放对象的同步锁,而sleep()则不会释放锁。
wait()可以指定时间也可以不指定时间。sleep()必须指定时间。
在同步中,对CPU的执行权和锁的处理不同。
wait:释放执行权,释放锁。
sleep:释放执行权,不释放锁。
线程的强制运行,join():
与sleep()方法一样,调用join()方法需要处理InterruptedException异常。
线程的礼让,yield():
yield()方法可暂停当前线程执行,允许其他具有相同优先级的线程获得运行机会,该线程仍处于就绪状态,不转为阻塞状态,此时,系统选择其他相同或更高优先级线程执行,若无其他相同或更高优先级线程,则该线程继续执行。
最后示例演示:
public class Demo { @Test public void test1() throws InterruptedException{ PaShan ps = new PaShan(); Thread nqr = new Thread(ps,"年轻人"); Thread old = new Thread(ps,"老年人"); nqr.start(); old.start(); Thread.currentThread().join();//暂停主线程 } } class PaShan implements Runnable{ @Override public void run() { //得到当前线程的名称 String name = Thread.currentThread().getName(); for (int i = 1; i <= 10; i++) { System.out.println(name+"爬完"+(i*100)+"米"); //加一个判断 if(name.equals("年轻人")){try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); }} else{try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }} } System.out.println(name+"爬到山顶了"); } }
动态直观输出结果:
线程同步的实现:
当两个或多个线程需要访问同一资源时,需要以某种顺序来确保该资源某一时刻只能被一个线程使用,这就称为线程同步。
采用线程同步来控制线程的执行有两种方式,即同步代码方法和同步代码块。这两种方式都使用synchronized关键字实现。
1.同步方法
使用synchronized修饰的方法控制对类成员变量的访问。每个类实例对应一把锁,方法一旦执行,就独占该锁,知道从该方法返回时才将锁释放,此后,被阻塞的线程方能获得该锁,重新进入可执行状态。
示例:
创建Site类:
public class Site implements Runnable { public int count = 10;//剩余票数 public int num = 0;//买到第几张票票 public boolean flag = false; @Override public void run() { while(!flag){ sale(); } } public synchronized void sale(){ if(count<=0){ flag = true; return; } num++; count--; try { Thread.sleep(500);//模拟网络延迟 } catch (InterruptedException e) { e.printStackTrace(); } //第二步:显示信息 System.out.println(Thread.currentThread().getName()+"抢到第"+num+"张票,剩余"+count+"张票!"); } }
创建测试类:
public class MyTest { public static void main(String[] args) { Site site = new Site(); Thread p1 = new Thread(site,"普通人"); Thread p2 = new Thread(site,"代理"); Thread p3 = new Thread(site,"黄牛"); p1.start(); p2.start(); p3.start(); } }
输出结果:
普通人抢到第1张票,剩余9张票! 普通人抢到第2张票,剩余8张票! 普通人抢到第3张票,剩余7张票! 黄牛抢到第4张票,剩余6张票! 代理抢到第5张票,剩余5张票! 黄牛抢到第6张票,剩余4张票! 黄牛抢到第7张票,剩余3张票! 普通人抢到第8张票,剩余2张票! 普通人抢到第9张票,剩余1张票! 普通人抢到第10张票,剩余0张票!
2.同步代码块
代码块即使用{}括起来的一段代码,使用synchronized关键字修饰的代码块,称为同步代码块。
实际上实现多线程的方式还有一种:
实现Callable接口,重写call方法。Callable接口与Runnable接口的功能类似,但提供了比Runnable更强大的功能。有以下三点
(1)Callable可以在人物结束后提供一个返回值,Runnable没有提供这个功能。
(2)Callable中的call方法可以抛出异常,而Runnable的run方法不能抛出异常。
(3)运行Callable可以拿到一个Future对象,表示异步计算的结果,提供了检查计算是否完成的方法。
需要注意的是,无论用那种方式实现了多线程,调用start方法并不意味着立即执行多线程代码,而是使得线程变为可运行状态。