串行 和并行 :串行顾名思义就是 一串 ,有序的执行,类似道路的 单条通道,车子只能有序的一辆一辆行驶。
并行就类似于 多条通道的道路,有很多车可以同时并行出发行驶。
1多线程的概述:
进程:当前正在运行的程序,一个应用程序在内存中的执行区域(一块内存空间)。
线程:进程中的一个执行控制单元,执行路径(做的每一件事情可以看做一个线程)
一个进程可以有一个线程,也可以有多个线程(多线程)
单线程:安全性高,但效率低
多线程:安全性低,效率高(同时进行多个线程)如迅雷同时下载多个电影
2多线程(创建多个线程实例,并启动多个线程)的实现方式:
Thread类
写一个类继承Thread的子类,重写Thread的run方法(被测试类运行的方法),
测试类,创建线程实例:(和普通类一样new)子类名 变量名 = new 子类名();
启动线程:变量名.start();
String getName();获取线程的名字
Void setName(String name);修改线程的名字为name
多线程:cpu在执行顺序上不确定,在多个线程之间随机执行
3主方法是单线程的:
主方法在运行 的时候是单线程运行的
4多线程的实现方式:
Runnable接口
写一个子类实现Runnable接口,并重写public void run方法。
实现Runnable接口,不能直接用[getName 的静态方法]getName()方法;
必须先创建和获取线程Thread对象,用Thread对象调用getName方法。
//Static Thread currentThread() 返回当前线程对象:Thread t = Thread.currentThread();
T.getName();
测试类:创建线程实例:MyRunnable mr = new MyRunnable();
将子类创建的对象 传给Thread : Thread t = new Thread(mr);
//用 线程Thread对象调用getName方法。t.setName()
启动线程: t.start();
5多线程模拟火车站售票出现问题:
Static void sleep(long millis):让当前线程暂停一会,时间单位是毫秒
6分析火车站售票出现问题原因:
是因为不同的线程在进入if之后 Thread.Sleep方法让它休息一会,并没有卖出票,那下一个线程也会通过if判断,所以会出现-1 -2的情况
7使用同步代码块解决多线程案例中出现的问题:
问题出现的原因:1要有多个线程2要被多个线程所共享的数据3多个线程并发的访问共享的数据
同步:安全性高,效率低
不同步:效率高,但是安全性低
Synchronized:同步(修饰符),可以修饰代码块和方法,被修饰的代码段和方法一旦被某个线程访问,则直接锁住,其他的线程将无法访问
同步代码块:
Synchronized(锁对象){
需要锁住运行的代码
}
注意:锁对象要被所有的线程锁共享。
8同步方法:
Synchronized :使用关键字synchronized修饰的方法,一旦被一个线程访问,则整个方法全部被锁住,其他的线程无法再访问
Synchronized:注意
非静态同步方法的锁对象是this
静态的同步方法的锁对象是当前类的字节码对象(文件)
Get.class();获取字节码文件对象 或者类名.class();
9线程的声明周期:
生命周期:一个对象的从生到死的过程
新建 就绪 运行 死亡
创建线程对象 ——》具备了执行条件——》具备了执行条件——》线程对象变成了垃圾
没有具备执行权力 具备了执行权力
等待方法:等待wait();让当前的线程对象等待
唤醒notify();让等待的线程继续执行(是让线程回到就绪步骤)
继承thread 的方式:
代码:
public class test {
public static void main(String args[]) {
// 实例化继承thread 的类
myThread myThread = new myThread();
//
myThread.start();
// 执行
System.out.println("主线程执行位置1");
myThread.run();
System.out.println("主线程执行位置2");
myThread.run();
System.out.println("主线程执行位置3");
}
}
class myThread extends Thread {
// 一个内部类继承thread类
// 重写run方法 将需要并发执行的逻辑 写到run方法中
@Override
public void run() {
for (int i = 0; i < 9; i++) {
System.out.println("线程中的i值"+i);
}
}
}
运行结果:
一般开发过程中,涉及几十万的大量数据导入 会使用异步多线程,后台对参数校验完成后 即可启动多线程,然后返回给前台结果,并提示 ‘数据正在导入中’稍后查询等 信息。
也适用于 一些日志信息的就 适用异步线程,这样即使异步线程日志记录出现bug,也不影响主业务逻辑的正常执行。
start() 方法是完全异步的,run()方法在 主线程中 是有顺序的执行的。
下面写两个start() 和两个run() 方法 更能体现出来不同方法执行的顺序。
runnable 接口方式:
因为thread 类也是实现的 runnable 接口所以,下面截图 使用runnable方式启动线程后,调用start() 和run() 与 使用thread
类得到的结果 一样,start()方法是完全异步的,run()方法和主线程顺序是一致的。
线程的休眠:sleep();单位是毫秒;下面代码运行结果 根据获取系统时间可以看出 每一秒执行一次。
public class test {
public static void main(String args[]) {
Runnable runnable = new Runnable() {
@Override
public void run() {
for (int i = 0;i<10;i++){
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
String date = df.format(new Date());
System.out.println(date+"线程中的i值"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Thread run1 = new Thread(runnable);
run1.start();
}
}
线程的优先级:
public class test {
public static void main(String args[]) {
Runnable runnable = new Runnable() {
@Override
public void run() {
for (int i = 0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"线程中的i值"+i);
}
}
};
Thread run1 = new Thread(runnable,"第一个线程:");// 给第一个线程 赋一个名字
Thread run2 = new Thread(runnable,"第二个线程:");// 给第二个线程 赋一个名字
// 设置线程的优先级,【1,10】 ,设置线程的优先级后只是提高线程获取到cpu的概率,并不是百分之百优先执行。
run1.setPriority(1);
run2.setPriority(10);
run1.start();
run2.start();
}
}
线程的礼让:当前线程由运行状态回到就绪状态,让出当前线程占用的cpu资源;
示例运行结果可以看出,线程二 运行到i= 5的时候 进行了礼让,然后线程一获取到了cpu 进行了打印。
同步代码段:
public static void main(String args[]) {
Runnable runnable = new Runnable() {
@Override
public void run() {
for (int i = 0;i<10;i++){
synchronized (""){
// 在执行同步代码段前,会先判断 是否由线程进入,如果有,
// 后面的线程就会在外面等待内部线程执行完同步代码段的代码后才会 进行继续执行
System.out.println(Thread.currentThread().getName()+i);
}
}
}
};
Thread run1 = new Thread(runnable,"第一个线程:");
Thread run2 = new Thread(runnable,"第二个线程:");
// 设置线程的优先级,【1,10】 ,设置线程的优先级后只是提高线程获取到cpu的概率,并不是百分之百优先执行。
run1.setPriority(1);
run2.setPriority(10);
run1.start();
run2.start();
}
类锁 :
synchronized (test.class){
// 在执行同步代码段前,会先判断 是否由线程进入,如果有,
// 后面的线程就会在外面等待内部线程执行完同步代码段的代码后才会 进行继续执行
System.out.println(Thread.currentThread().getName()+i);
}
同步方法:使用synchronized 修饰方法;
public synchronized static void main(String args[]) {}
使用锁对象进行加锁:ReentrantLock
ReentrantLock lock = new ReentrantLock();
lock.lock();// 加锁
System.out.println(Thread.currentThread().getName()+i);
lock.unlock();// 进行解锁
死锁: 就是线程a 持有 b锁 ,需要执行a锁,而 b持有a锁,需要执行b锁,会导致 死锁;
同步锁对象可以 适当的使用 对象.wait() 释放当前在的锁,让出cup资源,是当前的线程进入等待队列
然后等其他锁进入 锁对象的代码并执行完成后 再对象.notify() 或对象.notifyAll() 唤醒队列中的线程;