多线程
进程与线程
打个QQ,开了一个进程,再开个QQ,有开了一个进程,开了迅雷,又开了一个进程。
在QQ里头可以 传输 文字,语言,小广告,那么可以理解为传输文字,语言,小广告就是开了一个线程,线程可以理解为 “任务”。
一个进程可以管理多个线程,进程是爹妈,管理众多线程儿子。
进行本身不执行,只为线程的执行提供运行的环境,申请内容空间的。
什么是多线程
一个进程里面同时执行多个任务(线程、执行路径)。
一个进程里头只是包含一个线程。
开启多线程,是为了 “同时”执行多个任务,
也就是 “同时”执行多部分代码。
多线程弊端
弊端:线程切换耗费额外的资源,切换本身也是耗时间的。
好处:解决了多部分代码 同时 运行的问题
案例:验证JVM的启动
class User extends Object{
protected void finalize() throws Throwable {
System.out.println("我要被回收了");
}
}
public class Demo01 {
public static void main(String[] args) throws InterruptedException {
System.out.println("hello Thread--------");
for(int i = 0;i < 10;i++){
new User();
}
System.gc();//手动触发垃圾回收
Thread.sleep(1);
System.out.println("hello Thread========");
}
}
通过多次运行,发现输出结果的顺序有随机性,有多个“人”在跑
总结:
(1)JVM启动至少2个线程,一个主(main)线程,垃圾回收线程。
(2)线程之间的执行顺序是随机的,也就是那个线程占有CPU谁就执行。
怎么创建线程
方式一:继承Thread
代码如下
class MyThread extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"老王,被打死了");
//Thread.currentThread().getName()获取当前线程的名称
}
}
public class Demo02 {
public static void main(String[] args) throws InterruptedException {
MyThread myThread = new MyThread();
myThread.start();//启动一个线程
Thread.sleep(3);//线程休眠
System.out.println(Thread.currentThread().getName()+"老王复活了");
}
}
实现步骤
(1)创建一个类,继承Thread,
(2)子类重写run方法,原因:创建线程目的是为了跑任务,任务代码写在run方法里头
(3)创建子类对象,
(4)开启线程。调用start方法
Thread常用方法和属性
currentThread:获取当前线程
name:线程的名称
方式二:Runnable接口
代码实现
class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i <10 ; i++) {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"转世"+i);
}
}
}
public class Demo03 {
public static void main(String[] args) {
MyRunnable myRunnable1 = new MyRunnable();
MyRunnable myRunnable2 = new MyRunnable();
Thread thread1 = new Thread(myRunnable1, "小李");
Thread thread2 = new Thread(myRunnable2, "老王");
thread1.start();
thread2.start();
System.out.println(Thread.currentThread().getName()+"完了");
}
}
实现步骤
(1)创建一个类,时间runnable接口
(2)重写run方法,并且在run方法里面,写清楚你要干的活的。
(3)创建一个线程Thread(myRunnable,”线程的名字”)
(4)开启线程
总结
java是单继承,如果使用方式一创建线程,要求该类不能继承与其他父类,
往往我们使用 接口 方式扩展。
将线程任务 与 线程进行分离,对任务进行单独的封装。
一般情况下使用第二种方式
线程的调度
挂号之后病人处于新建状态,
排队候诊就是就绪状态;
当轮到医生给你看病的时候,就是运行状态,
当医生给你开验血单,在等待化验结果的时候,医生给其他病人看病,
在此之间处于阻塞状态,
当你拿到化验单医生再次为你服务,恢复为就绪状态。
线程状态:
新建状态
就绪状态
运行状态
阻塞状态
线程优先级
可以给线程设置优先级,优先级高的先跑,但是不意味着先跑完。
class Tach implements Runnable{
private String name;
public Tach(String name){
this.name = name;
}
@Override
public void run() {
for (int i = 0 ; i< 10;i++){
System.out.println("to"+name+i);
Thread.yield();//让步,只会让优先级比自己高得线程
}
System.out.println("bye....");
}
}
public class Demo04 {
public static void main(String[] args) {
Tach zx = new Tach("测试员");
Tach ls = new Tach("项目经理");
Thread t1 = new Thread(zx);
Thread t2 = new Thread(ls);
t1.setPriority(Thread.MAX_PRIORITY);//设置优先级
t2.setPriority(Thread.MIN_PRIORITY);
t1.start();
t2.start();
}
}
线程休眠sleep
设置休眠的时间,时间到了接着运行。阻塞线程。
Thread.sleep( 100 );
线程让步yield
yield()方法可以让当前正在执行的线程暂停,但它不会阻塞该线程,它只是将该线程从运行状态转入就绪状态。
只会给比自己 >=自己优先级的线程。
线程插队join
public class Demo05 {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
thread.start();
for(int i = 0;i < 5 ;i++){
System.out.println(Thread.currentThread().getName());
if(i==3){
thread.join();
}
}
}
}
线程安全(线程同步)
一个任务的代码实现超过2行,就有可能出现线程安全问题。
通过同步代码块的方式,保证 任务 的代码 只能一个线程跑,
当一个线程在运行同步代码块的时候,其他线程只能等待。
Synchronized:
保证一个方法或者一个代码块在同一时间只被一个线程访问
线程的死锁
2个人吃饭,只有2个筷子,每个个右手边各一只,
吃饭:A拿起右手边,等待B把左手边那个筷子放下,
B 等待A
原因:资源的竞争,
线程的协作
生产者和消费者
PV操作
wait:是当前线程进入阻塞状态,相当于P 操作 +
notify:唤醒此同步锁上阻塞的第一个线程,相当于V操作 -
notifyALL: 唤醒此同步锁上阻塞的所有线程