什么是进程?
进程就是正在运行的应用程序
什么是线程?
线程是程序执行的一条路径, 一个进程中可以包含多条线程
多线程并发执行可以提高程序的效率, 可以同时完成多项工作
一个进程至少包含一个线程,线程控制着进程
什么是执行路径
一个应用程序从执行到结束的整个过程称为执行路径
多线程并行和并发的区别(了解)
并行就是两个任务同时运行,就是甲任务进行的同时,乙任务也在进行。(需要多核CPU)
并发是指两个任务都请求运行,而处理器只能按受一个任务,就把这两个任务安排轮流进行,由于时间间隔较短,使人感觉两个任务都在运行。
例1:一共启动两个线程:一个线程用来执行main方法里面的代码(主线程),另一个用来进行垃圾回收
创建线程的几种方式:
① 继承Thread
好处是:可以直接使用Thread类中的方法,代码简单
弊端是:如果已经有了父类,就不能用这种方法
②实现Runnable接口
好处是:即使自己定义的线程类有了父类也没关系,因为有了父类也可以实现接口,而且接口是可以多实现的
弊端是:不能直接使用Thread中的方法需要先获取到线程对象后,才能得到Thread的方法,代码复杂
①class A extends Thread{
重写run方法
}
A a =new A();
a.start();
②class A implements Runnable{
重写run方法
}
A a = new A();
new Thread(a).start();
③匿名内部类方式
Runnable runnable = new Runnable(){
重写run方法
}
new Thread(runnable).start();
④Thread t1 = new Thread(){
重写run方法
};
t1.start;
⑤new Thread(new Runnable(){
重写run方法
}).start();
⑥new Thread(){
重写run方法
}.start();
线程的结果为什么交叉?
跟java线程调度方式有关:java的方式:抢占式
java虚拟机让当前线程暂时放弃cpu,转到就绪状态,使其他线程获得机会
注意:main方法的结束代表主线程的结束
run方法的结束代表着自己创建的线程结束
1.在任何一个地方都可以去创建线程
2.线程 只能通过start();
3.无论在哪创建的线程都是独立的,与主线程平行
获取线程名字: 通过getName()方法获取线程对象的名字
设置线程名字: 通过setName()方法获取线程对象的名字
匿名内部线程设置名字:
获取当前线程:Thread.currentThread();
可以通过该方式获取实现Runnable接口的线程的名字:Thread.currentThread().getName();例:
线程休眠:Thread.sleep();括号内为线程休眠时间 毫秒
插入线程:
插入线程: Thread t1 = new Thread();
t1.join();在括号写数字就是插队多长时间
守护线程 : t2.setDaemon(true);//将t2设置为守护线程 当非守护线程挂了,那么守护线程就结束
礼让线程(只是了解):
yield让出cpu
设置线程的优先级(只是了解):
setPriority()设置线程的优先级:数字越大优先级越高,优先级高只能保证有更多的机会去抢占cpu。优先级最大是10,最小是1。
同步代码块:
线程安全的代码块就是同步代码块
线程安全就是无论什么时候去访问结果都是正确的
1.什么情况下需要同步
当多线程并发, 有多段代码同时执行时, 我们希望某一段代码执行的过程中CPU不要切换到其他线程工作. 这时就需要同步.
如果两段代码是同步的, 那么同一时间只能执行一段, 在一段代码没执行结束之前, 不会执行另外一段代码.
2.同步代码块
使用synchronized关键字加上一个锁对象来定义一段代码, 这就叫同步代码块
多个同步代码块如果使用相同的锁对象, 那么他们就是同步的
3.同步方法
使用synchronized关键字修饰一个方法, 该方法中所有的代码都是同步的
非静态同步函数的锁是:this
静态的同步函数的锁是:字节码对象
例:同步代码块
例:同步方法
死锁(必须掌握) : 在程序里面,有两个锁,A线程锁住第一个,B线程锁住了第二个,这时如果A再试图锁第二个,失败,因为B已经锁住了,A只能等待.就在这时B试图锁第一个,结果失败,因为A已经锁住了,B只好等待.就这样大家一直等下去,谁都不放,天荒地老...
多线程同步的时候, 如果同步代码嵌套, 使用相同锁, 就有可能出现死锁
例:
线程通信:
1.什么时候需要通信
多个线程并发执行时, 在默认情况下CPU是随机切换线程的如果我们希望他们有规律的执行, 就可以使用通信, 例如每个线程执行一次打印
2.怎么通信:
如果希望线程等待, 就调用wait()
如果希望唤醒等待的线程, 就调用notify();
这两个方法必须在同步代码中执行, 并且使用同步锁对象来调用
如果多个线程之间通信, 需要使用notifyAll()通知所有线程
线程的五种状态:
1、新建(new):线程对象被创建后就进入了新建状态。如:Thread thread = new Thread();
2、就绪状态(Runnable):也被称为“可执行状态”。线程对象被创建后,其他线程调用了该对象的start()方法,从而启动该线程。如:thread.start(); 处于就绪状态的线程随时可能被CPU调度执行。
3、运行状态(Running):线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态。
4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权限,暂时停止运行。直到线程进入就绪状态,才有机会进入运行状态。
阻塞的三种情况:
1)等待阻塞:通过调用线程的wait()方法,让线程等待某工作的完成。
2)同步阻塞:线程在获取synchronized同步锁失败(因为锁被其他线程占用),它会进入同步阻塞状态。
3)其他阻塞:通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超 时、join()等待线程终止或超时、或者I/O处理完毕时,线程重新转入就绪状态。
5、死亡状态(Dead):线程执行完了或因异常退出了run()方法,该线程结束生命周期。