多线程知识点
1.多线程
例如:一个短视频,一个线程复制管理视频,一个线程负责管理声音,一个线程负责管理弹幕
进程:Process,程序一旦开始运行就是是一个进程
线程:Thread,一个程序运行后,里面就包含了多个线程
真正的多线程是指有多个cpu,即多核。如果是模拟的多线程,即只有一个cpu,在同一时间点,cpu只能执行一个代码,因为切换的很快,所以就有同时执行的错误
2.线程的创建
1.继承Thread类
线程不一定立即执行,听从cpu的安排,交替执行
//实现步骤
1.继承Thread
2.重些run方法
3.对象. start()启动线程
public class StartThread extends Thread
{
@Override
public void run()
{
}
}
StartThread a=new StartThread();
a.start()
2.实现Runnable接口
接口实现类对象能被多个线程使用
1.实现接口
2.编写线程方法
3.开启线程,通过实现Runnable接口来创建线程,启动线程时需要通过Thread对象。
PrimeRun p = new PrimeRun(143);
new Thread(p).start();
并发问题:
3.实现Callable接口
3.静态代理
使用步骤
1.需要代理对象、实际对象,以及一个抽象接口
2.代理对象、实际对象都要实现接口
3.代理对象中创建构造方法,方法参数为接口类对象
4.在代理对象中定义抽象接口类对象来接收构造方法的参数对象
5.在代理对象中接口的实现方法中,用接收到的变量接口对象调用实现的接口方法
逻辑,通过代理对象的有参构造,将实际对象以参数的形式传递到代理类中,然后在代理类接口的实现方法中,调用实际类中的实现方法
4.线程停止
1.设立外部标志位,定义一个公开的方法,通过改变标志位来结束进程
5.线程休眠
应用
1.模拟网络延时
通过延时可以发现多线程中出现的问题,如数据同步性
2.倒计时
每个对象都有一把锁,sleep不回释放锁
6.线程礼让
Thread.yield()
正在进行的线程,由运行态转为就绪态,然后由cpu在就绪态中选择另一个线程,礼让不一定百分百成功
7.线程强制执行
线程插队,强制执行插队的线程
8.观测线程的状态
Thread.getState()
线程的状态
1.创建
2.就绪
3.执行
4.阻塞
5.结束,线程一旦结束,就不能再被使用
9.线程的优先级
thread.setPriority()
10.守护线程
监控用户线程,用户线程结束,守护线程也结束
thread.setDaemon()
11.线程同步
并发:同一个对象被多个线程同时操作,如:秒杀,抢票
解决线程的不安全性:队列+锁,解决数据安全性
synchronized
//修饰代码块
synchronized(this){} //修饰当前类
synchronized(类.class){}//修饰其他类
//修饰方法
1.修饰静态方法
2.修饰非静态方法
案例代码:
class Thick implements Runnable
{
private int num=10;
Boolean flag=true;
@Override
public void run()
{
try {
buy();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public void buy() throws InterruptedException {
while (flag)
{
Thread.sleep(200);
if (num>0)
synchronized (this)
{
System.out.println(Thread.currentThread().getName()+"买到了第"+num--+"票");
}
else
{
flag=false;
}
}
}
public class CarThick {
public static void main(String[] args) {
Thick thick=new Thick();
new Thread(thick,"张三").start();
new Thread(thick,"李四").start();
new Thread(thick,"王二狗").start();
}
}
12.死锁
13.Lock锁
和synchronized
锁的作用一样,在线程同步的情况下,保证数据的原子性
ReentrantLock//可重入锁
private final ReentrantLock lock=new ReentrantLock();
lock.lock();//加锁,在数据发生改变的地方,加锁
lock.uplock;//解锁,完成数据改动后,把锁给下一个线程
14.生产者与消费者-线程通信
1.生产者
2.消费者
3.线程池
4.消费品
管程法
//线程池
class SynContainer{
// 需要一个容器大小
Bug[] bugs = new Bug[10];
// 容器计算器
int count = 0;
// 生产者放入bug
public synchronized void push(Bug bug){
// 如果容器满了,就需要等待消灭bug
if(count==bugs.length){
// 通知消灭bug者消灭,生产bug等待
try {
//导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法。
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 如果没有满,我们就需要继续生产bug
bugs[count] = bug;
count++;
// 可以通知消灭者消灭了
this.notifyAll();
// notifyAll唤醒正在等待对象监视器的所有线程。
}
// 消灭者消灭bug
public synchronized Bug pop(){
// 判断能否消灭
if (count == 0){
// 等待生产者生产,消灭者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 如果可以消灭
count--;
Bug bug = bugs[count];
// 消灭完了,通知生产者生产
this.notifyAll();
try {
System.out.println("通知生产者");
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return bug;
}
//生产者
class Productor extends Thread{
SynContainer container;
public Productor(SynContainer container){
this.container = container;
}
// 生产
@Override
public void run() {
for (int i = 0; i <=100; i++) {
container.push(new Bug(i));
System.out.println("生产了"+i+"个bug");
}
}
}
//消费者
class Consumer extends Thread{
SynContainer container;
public Consumer(SynContainer container){
this.container = container;
}
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
System.out.println("消灭了-->"+container.pop().id+"个bug");
}
}
}
//消费品
class Bug{
int id;// bug编号
public Bug(int id){
this.id = id;
}
}
//测试
public class ProviderAndConsumer
{
public static void main(String[] args) {
SynContainer container = new SynContainer();
new Productor(container).start();
new Consumer(container).start();
}
}
15.线程池
避免重复的创建销毁线程,减少资源浪费,类似于数据库连接池