概念:程序、进程、线程
线程:是一个程序内部的一条执行路径(如果把进程比作河,那么线程就相当于河流的分支)
cpu的核数:几核的cpu代表一个瞬时时间能处理任务数
主频:任务之间的切换速度
核数越多、主频越快越好
多线程的创建和使用
多线程:一个进程(一个程序运行时),可以分化为并行执行多个线程(多个子程序)
何时需要多线程?
1、程序需要同时执行两个或者多个任务
2、需要一些后台运行的程序多线程的创建和启动,通过java.lang.Threat实现
Thread类特性:
run():每个线程通过某个特定的Thread对象的run()方法(它的主体叫做线程体,想要运行的代码逻辑写在这个方法里)来完成操作
start():通过该Thread对象的start()方法(启动线程)来调用这个线程
创建线程:
方法一:继承Thread类实现多线程
CC.java
package jicheng; import java.lang.*; public class CC extends Thread { @Override public void run() { System.out.println("这里写的是多线程运行的代码"); for (int i = 1; i <= 8; i++) System.out.println(i); } }
B.java
package jicheng; import java.lang.*; public class B { public static void main(String[] args) { //new实例的时候用其父类去接受即可(对象的多态) Thread t0 = new CC(); //继承Threat类的线程 t0.start();//启动线程,运行run方法 // start执行好之后的main()方法的其他的代码与run方法的代码并行 System.out.println("*****"); //发现每次运行的结果不一样 // 输出的和run方法输出混合,且顺序不一定,但是保持自己的输出顺序 //这个就是多线程的异步,同步:代码严格按照从上到下执行 //异步:开启线程之后run方法运行的和主程序的代码是并行执行的,没有先后关系 } }
方法二:实现Runnable接口实现多线程
CC.java
package jicheng; import java.lang.*; public class CC implements Runnable{//通过实现Runnable接口方式实现多线程 @Override public void run() { System.out.println("这里写的是Runnable多线程运行的代码"); for (int i = 1; i <= 8; i++) System.out.println(i); } }
B.java
package jicheng; import java.lang.*; public class B { public static void main(String[] args) { Thread t1 = new Thread(new CC());//传入Runnable实例 t1.start(); //或者可以这样写,多个线程可以这样子写 // Thread t2 = new Thread(new CC(), "线程的名称"); } }
两种方法实现多线程的总结:
-
接口:接口的方法都没有方法体,叫做实现run方法(一般用这个)
-
继承:叫做重写run方法
实现方法的好处:
-
避免单继承的局限性;(因为继承的话,如果父类是Thread就不能是其他的了,实现接口不影响去继承其他父类)
-
多个线程可以共享同一个接口实现类的对象,非常适合多个相同线程来处理同一份资源。
多线程的优点:
-
提高应用程序的相应。对图形化界面更有意义,增强用户体验;
-
提高应用程序的CPU利用率;
-
改善程序结构(将长和复杂的进程分为多个线程,独立运行,利于理解和修改)。
线程的生命周期
线程的同步与死锁
一个用户有3000元,需要实现支付宝和微信同时取出2000元
package jicheng; import java.lang.*; public class B { public static void main(String[] args) { Acount a = new Acount();//定义账户对象 //多线程对象 User u_w = new User(a, 2000);//分别取出2000 User u_z = new User(a, 2000); //微信和支付宝的线程 Thread wechat = new Thread(u_w, "微信");//多线程对象和名称 Thread pay = new Thread(u_z, "支付宝"); wechat.start(); pay.start(); } } //多线程出现安全问题: //多线程调用这个方法会发现:线程共享资源的时候,一个线程在执行这个方法没有完毕时,另一个线程开始执行这个方法 //出现问题:钱变成负数 //解决:对于共享数据的操作,让一个线程执行完再让其他线程参与 //通过synchronized同步锁来完成,在方法上加上该关键字即可 //在普通方法上加同步锁,锁的是整个对象 class Acount { public static int money = 3000;//账户原有3000元,全局变量 public synchronized void drawing(int m) { String name = Thread.currentThread().getName(); if (money < m) { System.out.println(name + " 余额不足"); } else { System.out.println(name + " 原来:" + money); System.out.println(name + " 取走:" + m); money -= m; System.out.println(name + " 余额:" + money); } } } class User implements Runnable { Acount acount; int money; public User(Acount acount, int money) { this.acount = acount; this.money = money; } @Override public void run() { acount.drawing(money); } }
代码的注释看一下。
在这个代码中,可以明白同步锁的作用,
同步锁关键字:synchronized。
这个代码不关键字,则:
多线程出现安全问题:
多线程调用drawing(取出钱)这个方法会发现:线程共享资源的时候,一个线程在执行这个方法没有完毕时,另一个线程开始执行这个方法
出现问题:钱变成负数。
解决:对于共享数据的操作,让一个线程执行完再让其他线程参与。
通过synchronized同步锁来完成,在方法上加上该关键字即可。
注意:在普通方法上加同步锁,锁的是整个对象,不是某一个方法,不同的对象就是不同的锁;静态的方法加synchronized,对于所有的对象都是同一个锁。
除了上面代码内容,
同步锁还可以这样写:
synchronized(对象){ //需要被同步的代码 }
如果针对对象要加同步锁,就加在方法上;
针对某一段代码需要加同步锁,就在代码块上加上同步锁。
不加同步锁关键字输出:
加了同步锁关键字输出:
线程的通信
这三个方法只能用在有同步锁的方法或者代码块上