多线程

多线程

程序:为了完成特定任务、用某种语言编写的一组指令的集合。
即指一段静态代码,静态对象。

进程:是程序的一次执行过程,或是正在运行的一个程序,是一个动态
的过程
:有它自身产生、存在和消亡的过程。 系统会为每个进程分配不同的内存区域

线程:进程可以进一步细化为线程,是一个程序内部的一条执行路径
线程有独立的栈和独立的程序计数器
一个进程里面的多个线程是共享内存单元/内存地址空间的 他们从同一堆中分配对象
访问相同的变量和对象 这就使得线程内通信更简便、高效。但多个线程操作
共享的系统资源可能会带来安全隐患。

java应用程序其实最少有三个线程,main主线程、gc垃圾回收线程、异常处理线程
当发生异常,会影响主线程。

并行与并发

并行:多个cpu同时执行多个任务
并发:一个cup同时执行多个任务 JUC并发包

如果是单核CPU 用单线程完成多个任务要比使用多线程快 因为用多线程单核CPU需要切换

什么时候需要多线程:

程序需要同时执行两个或者多个任务
程序需要实现一些需要等待的任务:用户输入、文件读写、网络操作、搜索等等
需要一些后台运行程序 例如java中的gc 垃圾回收线程

多线程优点:

1.提高应用程序响应,对图形化界面操作更有意义,可增强用户体验。
(比如 下载东西的同时听听音乐)
2.提高计算机CPU的利用率
3.改善程序结构,把负责的进程分成多个线程 独立进行 方便修改

多线程创建

创建一个继承于thread类的子类

class m extends thread
	重写thread类的run()--》将此线程	执行的操作声明在run()中
public void run(){
for(int i=0;i<100;i++){
system.out.println(i);
}
在main方法中:
	创建thread类的子类对象
m m = new m();
	通过此对象调用start()//start方法是启动线程
m.start();
只用一次线程类,书写方法 用匿名子类:
new Thread(){
	public void run(){
	for(int i=0;i<100;i++){
	system.out.public("111111111");
	}
	}
}.start();
//上面就可以直接用一个线程来完成输入100次11111111

Thread常用方法
start();的作用:1.使得当前线程启动2.调用当前线程的run方法
不能直接调用run方法启动线程!
start方法对一个线程只能用一次

run() 通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在次方法中
setname(name)设置该线程名称

Thread.currentThread();返回执行当前代码的线程。在Thread子类中就是
this,通常用于主线程和Runnable实现类

getname()获取当前线程名字

setname()设置当前线程的名字

yield();释放cpu的执行权(释放后有可能被其他线程使用)

join():在线程a中调用线程b的join方法:b.join();,此时线程a进入阻塞状态直到线程b完全执行完成

stop():已过时,当执行此方法时,强制结束当前线程。

sleep(Long millitime):睡眠 参数单位为毫秒

isAlive();判断一下当前线程是否还存活

线程的调度
线程优先级
MAX_PRIORITY:10 最大优先级
MIN_PRIORITY:1 最小优先级
NORM_PRIORITY:5 默认优先级
涉及到的方法:
getPriority():返回线程优先值
setPriority(int newPriority):改变线程的优先级
M.setPriority(8);//设置分线程优先级为8
并不是a线程优先级高b线程优先级低就一定会先执行a线程优先级高的概率高一点

多线程买票问题:
//这样是多个线程每个线程卖100张
int i =100;
while(true){
	if(i>0){
		System.out.println(i);
	}
}
//这样是多个线程一共卖一百张 加上static关键字
static int i =100;
while(true){
	if(i>0){
		System.out.println(i);
	}
}

如果一个成员变量使用了static关键字,那么这个变量不再属于对象自己,
而是属于所在类,多个对象共享同一份数据

实现Runnable接口

1.创建一个实现了Runnable接口的类
MThread 实现 Runnable
2.实现类去实现Runnable中的抽象方法:run()
 run()
3.创建实现类的对象
MThread mThread = new MThread();
4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
Thread t1 =new Thread(mThread);
5.通过Thread类的对象去调用 start方法:启动线程 调用线程的run方法 调用了Runnable类型的target的run //target就是mThread
t1.start();
在启动一个线程,做同样的工作:
Thread t2 =new Thread(mThread);
t2.start();
多线程买票问题 用实现Runnable接口的方式:
//这样是多个线程每个线程卖100张    因为只需要创建一个MThread对象 创建多个Thread对象 所以是多个Thread对象共用MThread对象中的资源不需要加上static关键字
int i =100;
while(true){
	if(i>0){
		System.out.println(i);
	}
}

比较以上两种创建线程的方式(继承Thread/实现Runnable接口):
实现Runnable接口比较好

1.因为继承Thread之后 Java是单继承 不能再继承其他类了、
2.创建多个线程需要共享数据的时候 最好使用实现Runnable接口 因为实现Runnable接口更适合处理共享数据的情况

相同点:两种方式都需要重写run(),将线程要执行的逻辑声明再run()中

实现Callable接口

1.实现Callable接口需要返回值类型
public class Demo1 implements Callable 
2.重写call方法,需要抛出异常
public Object call() throws Exception 
3.创建目标对象
Demo1 demo = new Demo1();
Demo1 demo2 = new Demo1();
//创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(3);//nThreads参数 需要创建几个线程就写几个
//提交执行
Future result1 = ser.submit(demo);
Future result2 = ser.submit(demo2);
//获取结果
String q = (String) result1.get();
String q1 = (String) result2.get();
//关闭服务
ser.shutdownNow();  

Runnable和Callable的区别:
1、Callable规定的方法是call(),Runnable规定的方法是run().
2、Callable的任务执行后可返回值,而Runnable的任务是不能返回值得
3、call方法可以抛出异常,run方法不可以
4、运行Callable任务可以拿到一个Future对象,
表示异步计算的结果。它提供了检查计算是否完成的方法,
以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,
可取消任务的执行,还可获取执行结果。

Java中的线程分为两种:

守护线程/用户线程
用户线程例子:main 主线程
守护线程例子:gc() 垃圾回收线程
当用户线程结束的时候守护线程也会结束 要有守护线程必须得有用户线程
用户线程在通过start方法前调用 thread.SetDaemon(true)可以把有个用户线程变成守护线程
当jvm中只有守护线程的时候 jvm就会关闭退出

线程的生命周期

JDK中用Thread.State类定义了线程的几种状态
新建(new 出线程对象)
就绪(调用start方法/由阻塞传过来:sleep方法时间到、join结束、获取到同步锁、notify或notifyAll、resume())
运行(获取cpu的执行权)
阻塞(调用sleep(long time)、调用其他线程.join()、等待同步锁、wait()方法、suspend方法已经过时意思为挂起 与上方resume()方法成对出现)
死亡(run执行完/调用stop方法/出现Error或Exception且没处理)

线程的同步

多个线程执行的不确定性引起执行结果的不稳定
多个线程对账本的共享,会造成操作的不完整性,会破坏数据
多线程访问同一个对象,并且某些线程需要修改这个对象。就需要用到
线程同步,线程同步就是一种等待机制。多个需要同时访问此对象的线程进入
到这个 对象等待池!
给方法加synchronized修饰符可以实现同步:

private synchronized void buy(){}
方法可以直接加修饰符 如果是类需要用块级结构
int i;
synchronized(i/*可写参数*/){
private  void buy(i){}
}

ArrayList集合不安全

JUC里面的安全类型集合:CopyOhWriteArrayList(这个集合本身就是安全的不需要同步)

死锁

死锁产生的四个必要条件:
1.互斥条件:一个资源每次都只能被一个进程使用。
2.请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
3.不剥夺条件:进程已获得的资源,未使用完之前不能被强行剥夺。
4.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
idea Thread->死锁

Lock锁

Class A{
	private final ReentrantLock lock = new ReentrantLock(); //ReentrantLock 可重复锁
	public void m(){
		lock.lock();//加锁
		try {
			//保证线程安全的代码
		} finally{
			//lock.unlock();//解锁
			//如果同步代码有异常,要把unlock()写入finally语句块
		}
	}
}

Lock锁和synchronized功能一样
Lock是显式锁需要手动开启和关闭而synchronized是隐式锁出了作用域会自动释放
但是lock只能锁代码块 而synchronized可以锁代码块和方法

线程协作

对于消费者与生产者问题有两种方法:管程法、信号灯法(百度去查)
使用方法
this.wait();当前线程等待、挂起
this.notify()通知等待队列中的第一个线程,唤醒
this.notifyAll()通知的是等待队列中的所有线程;唤醒

线程池

实现Runnable接口
public class TestPool {
    public static void main(String[] args) {
    //1.创建服务,创建线程池,参数为线程池大小
        ExecutorService service = Executors.newFixedThreadPool(10);
    //执行
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());

    //2.关闭连接
        service.shutdown();
    }
}
class MyThread implements Runnable{

    @Override
    public void run() {
        for (int i=0;i<1;i++){
            System.out.println(Thread.currentThread().getName()+"____"+i);
        }
	}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值