第20天 多线程

多线程

进程 :正在内存中运行的程序
线程:线程是进程中的一个执行单元

一个进程中至少有一个线程

cpu一次只能处理一条指令,所谓同时是因为人反应不过来
分为多个时间片段,尽可能平均分配给每一个线程

多线程并不会提高运行时间,它只是分别每次只执行一个任务,在没有执行完就切换执行下一个

线程的创建 : 第1种方法:
1.定义一个类继承(extends)Thread
2.重写run方法
3.创建Thread子类对象,就是创建线程对象
4.调用start方法,开启线程执行run方法

Thread.currentThread()//返回正在被执行的线程信息 .getName() //获取正在被执行的线程信息的名字

缺点:1. Java是单继承,继承thread后就无法继承其他的
2. 当继承Thread 并重写run方法,在run方法中定义要执行的任务。这会导致线程与任务之间存在一个必然的耦合关系,不利于线程的

psvm{
Thread t1 = new MyThread1();//创建子类对象
Thread t2 = new MyThread2();
t1.start();//先调用start方法,而不是run方法。run方法是创建的类中的,这里是让线程跑起来
t2.start();

}


class MyThred1 extends Thread{
public void run(){
要并行执行的语句
}

}


class MyThred2 extends Thread{
public void run(){
要并行执行的语句
}

}

第2种方法:
实现Runnable接口重写run方法

  1. 定义类实现(implements)Runnable接口。或者实现Callable接口(如果线程执行完毕需要返回值时使用,多用于线程池)
  2. 实现接口中的run方法
  3. 创建Runnable接口的子类对象
  4. 将子类对象作为参数传递给创建的Thread类的构造方法
  5. 调用Thread类的start方法开启线程
//定义类实现Runnable接口
public class MyRunnable1 implements Runnable{
//实现接口中的run方法
public void run(){
要并行执行的语句
}
}
public class MyRunnable2 implements Runnable{
public void run(){
要并行执行的语句
}
}

public class   {
psvm{
//创建线程要执行的任务。创建Runnable接口的子类对象
Runnable r1 = new MyRunnable();//这是向上造型么?
Runnable r2 = new MyRunnable();
//创建线程。将子类对象作为参数传递给Thread的构造方法
//这种写法是将任务和线程分开,Thread可以干更多的事,不局限于一个任务
Thread t1 = new Thread(r1(只要是继承Runnable接口或者是它的对象都可以),"此处可以设置线程的名字");
Thread t2 = new Thread(r2);
//启动。调用Thread类的start()方法开启线程
t1.start();
t2.start();

}

}



线程安全

有多个线程在同时运行,而这些线程同时运行某段代码。程序每次运行的结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的一样,就是线程安全。
如果不一样,就是出现了线程安全隐患。
出现安全隐患的必要条件:

  1. 多线程
  2. 同时使用一个资源
  3. 写操作(添加,删除,修改)
    只需要破坏其中一个条件就可以解决线程安全隐患

同步代码块

在代码块声明一个锁(synchronized)

两个线程调用同一对象才有效果,他们看到的是同一对象。()内写的是同步监视对象,必须是一个引用类型

(new Object):对象一定不同,锁无论如何都没有效果

(“abc”):写abc在main中调用多个对象,此时已经不是多线程,但是锁还是发挥作用。不是一个合适的锁对象
(this):this是一个合适的锁对象,因为它在调用多个对象时,会失效。看到的是不同的类。

在静态方法中写括号中的同步监视对象时,括号内不能是this,因为静态中没有this,需要使用类对象,就是类名.Class



```java
锁对象是共有的
synchronized(锁对象){
可能产生线程安全问题的代码
}
public static synchronized void consumer(){}


在同步代码块中,在同一时刻只能有一个线程在执行,加入锁之后相当于给门上锁,我正在里边的代码时不允许其他人访问,我结束执行了才可以。

同步代码块中的锁对象可以是任意对象,但是多个线程时,要使用同一个锁对象才能保证线程安全。否则容易出死锁

同步方法

在普通方法声明上添加synchronized,就是同步方法。

成员方法所属于对象

同步方法中所的对象不能改,只能是this

权限修饰符 synchronized 方法的返回值类型 方法名(){
可能会产生问题的代码;代码执行完之后才会解锁
}

同步方法可以保证在同一时刻方法中只有一条线程

静态同步方法

在 静态方法的声明上添加synchronized,就是静态同步方法

静态方法所属类,全局只有一份。

当一个静态方法使用synchronized后,那么该方法一定具有同步效果。不论new多少个都需要排队做

静态方法上指定的同步监视器对象为当前类的类对象(.Class)
类对象:Class的实例

权限修饰符 static  synchronized 方法的返回值类型 方法名(){
    可能会产生线程安全问题的代码;
}

同步和异步

同步:一段代码在同一时刻只允许一个线程执行
异步:一段代码在同一时刻允许多个线程执行

同步一定是线程安全的
线程安全的不一定同步

异步不一定线程不安全
线程不安全一定是异步的

ArrayList 是异步线程不安全的
LinkedList是异步线程不安全的
HashMap是异步线程不安全的
Hashtable是同步线程安全的

ConcurrentHashMap 是一个异步线程安全的,采用分桶锁的机制

死锁

由于锁的嵌套导致锁之间相互锁死的现象

synchronized(s){//抢到s线程之后,等待释放p线程
synchronized(p){
}
}//大括号结束释放s线程
synchronized(p){//抢到p线程之后,等待释放s线程
synchronized(s){
}
}//大括号结束释放p线程


等待唤醒机制

类.wait() :让线程进入等待池等待
类.notify():从等待池中唤醒线程

等待唤醒机制必须结合锁来用

使用的锁对象和唤醒的锁对象应该是同一对象

wait和sleep的区别

wait()方法是Object类的方法,在同步代码块或同步方法中调用,如果不指定休眠时间,调用之后会释放对象的锁,使当前线程进入等待状态,直到其他对象的notify()或notifyAll()方法来唤醒它。唤醒它之后代码向下执行
sleep()方法是Thread类的静态方法,可以在任何地方调用,它会使得当前线程暂停执行指定的时间,不会释放任何锁
当一个线程调用sleep方法在睡眠阻塞中,调用interrupt()方法会中断该线程的睡眠阻塞。sleep方法会抛出中断异常

等待唤醒机制

生产者与消费者

wait() 执行后从线程池唤醒一个。
执行完语句后也唤醒一个

若只是p.notify(); 。唤醒两个c,线程都进入等待

外边有两个p,等待池里有两个c沉睡(此时两个c都在p.wait()处,已经判断完毕),p执行结束后为false之后随机唤醒,若唤醒其中一个c,c抢到之后执行完把结果改为true,改为true之后要唤醒一个,若唤醒另一个c,另一个c抢到线程之后不再判断,从唤醒处向下执行,c还是true,还会执行一次c。

线程池的使用

提前创建多个线程,放入一个集合中。需要时取出一个线程为它服务,不需要时再放回

//创建一个线程池
ExecutorService executorService = Executors.newFixedThreadPool(10);
//向线程池中布置任务
executorService.submit(new 一个类中继承Callable接口的call方法)//submit是执行
executorService.execute(new 一个类中继承Run接口的run方法)。执行线程池
//关闭线程池
executorService.shutdown();


使用线程池执行方法

第四种创建线程的方法

单例设计模式

单例模式可以保证某一个类创建出的对象在整个项目中只有一个
分为懒汉式和饿汉式

饿汉式

将构造方法设置为private,禁止外面的类调用。
在内部设置静态的私有的变量,这样外面可以获取。
设置公开的方法返回静态静态的私有的变量

守护线程

守护线用于守护别的线程。只有所有线程都结束了,那么守护线程也随之结束
如果一个线程不是守护线程,那么就是被守护线程。所有的被守护线程结束,守护线程才随之结束

main是主线程,main方法执行完就会结束,所以不一定是最后结束的线程。如果主线程不结束,进程就不会杀死守护线程

GC(垃圾回收机制)就是一个守护线程。当程序结束后,GC也会结束

守护线程只能在start() 执行前启动

Thread thread = new Thread();
thread.setDaemon(true);//守护线程
thread.start();

设置线程优先级

优先级越高,越有可能执行

优先级分为1-10
Thread.MAX_PRIORITY 为10
Thread.NORM_PRIORYTY 为5
Thread.MIN_PRIORITY 为1

Thread t1 = new Thread();
t1.setPriority(1);//设置线程优先级

线程状态

Thread.State state = thread.getState();
sout(state)
获取线程的状态
NEW 尚未启动的线程
RUNNABLE 正在jvm中执行的线程
BLOCKED 阻塞的线程
WAITING 无限期等待另一个线程要执行某一特定操作的线程
TIMED_WAITING 等待另一线程执行取决于等待时间的操作,否则我等完就要执行
TERMINATED已经结束的线程

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值