多线程

多线程

进程:是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位。本质上就是一块内存空间。
线程:就是进程中的程序执行的最小单元。

一个进程至少有一个线程在运行,当一个进程中出现多个线程时,就称这个应用程序是多线程应用程序,每个线程在栈区中都有自己的执行空间,自己的方法区、自己的变量。
jvm在启动的时,首先有一个主线程,负责程序的执行,调用的是main函数。主线程执行的代码都在main方法中。

当产生垃圾时,收垃圾的动作,是不需要主线程来完成,因为这样,会出现主线程中的代码执行会停止,会去运行垃圾回收器代码,效率较低,所以由单独一个线程来负责垃圾回收。(异常也是并行运行)
- - -
随机性的原理:因为cpu的快速切换造成,哪个线程获取到了cpu的执行权,哪个线程就执行。

  1. 关于运行线程代码,要知道的是:

    • 返回当前线程的名称:Thread.currentThread().getName()
    • 线程的名称是由:Thread-编号定义的。编号从0开始。(当然可以自己去命名)
  • 线程要运行的代码都统一存放在了run方法中。

  • 线程要运行必须要通过类中指定的方法开启。start方法。

3.线程状态:

它要经过新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5种状态。尤其是当线程启动以后,它不可能一直"霸占"着CPU独自运行,所以CPU需要在多条线程之间切换,于是线程状态也会多次在运行、阻塞之间切换

  1. 新建状态,当程序使用new关键字创建了一个线程之后,该线程就处于新建状态,此时仅由JVM为其分配内存,并初始化其成员变量的值

  2. 就绪状态,当线程对象调用了start()方法之后,该线程处于就绪状态。Java虚拟机会为其创建方法调用栈和程序计数器,等待调度运行,
  • notify 是随机唤醒。 notify全部释放
  1. 运行状态,如果处于就绪状态的线程获得了CPU,开始执行run()方法的线程执行体,则该线程处于运行状态

  2. 阻塞状态,当处于运行状态的线程失去所占用资源之后,便进入阻塞状态,有wait , sleep可以变成阻塞
  • wait 不会被被唤醒后是不会释放锁, sleep会释放锁
  1. 在线程的生命周期当中,线程的各种状态的转换过程!
    1272280-20171202135545683-1121282568.png
创建线程的第二种方式:实现一个接口Runnable

1,定义类实现Runnable接口。
2,覆盖接口中的run方法(用于封装线程要运行的代码)。
3,通过Thread类创建线程对象;
4,将实现了Runnable接口的子类对象作为实际参数传递给Thread类中的构造函数。
为什么要传递呢?因为要让线程对象明确要运行的run方法所属的对象。
5,调用Thread对象的start方法。开启线程,并运行Runnable接口子类中的run方法

为什么要有Runnable接口的出现?

1:通过继承Thread类的方式,可以完成多线程的建立。但是这种方式有一个局限性,如果一个类已经有了自己的父类,就不可以继承Thread类,因为java单继承的局限性。
  可是该类中的还有部分代码需要被多个线程同时执行。这时怎么办呢?
  只有对该类进行额外的功能扩展,java就提供了一个接口Runnable。这个接口中定义了run方法,其实run方法的定义就是为了存储多线程要运行的代码。
  所以,通常创建线程都用第二种方式。
因为实现Runnable接口可以避免单继承的局限性。

2:其实是将不同类中需要被多线程执行的代码进行抽取。将多线程要运行的代码的位置单独定义到接口中。为其他类进行功能扩展提供了前提。
  所以Thread类在描述线程时,内部定义的run方法,也来自于Runnable接口。
实现Runnable接口可以避免单继承的局限性。而且,继承Thread,是可以对Thread类中的方法,进行子类复写的。但是不需要做这个复写动作的话,只为定义线程代码存放位置,实现Runnable接口更方便一些。所以Runnable接口将线程要执行的任务封装成了对象。

wait(),notify(),notifyAll()用来操作线程为什么定义在Object类中,sleep 存在thread中?

这些方法存在于同步中;
使用这些方法必须标识同步所属的锁;
锁可以是任意对象,所以任意对象调用方法一定定义在Object类中。
而sleep休眠是需要设定固定时间而不是通过锁的方式。


new Thread(new Runnable(){  //匿名
public void run(){
System.out.println("runnable run");
}
})
{
public void run(){
System.out.println("subthread run");
}
}.start();  //结果:subthread run
Try {
Thread.sleep(10);
}catch(InterruptedException e){}// 当刻意让线程稍微停一下,模拟cpu切换情况
  1. 多线程的安全问题:
    多线程环境下, 数据资源的争抢。
  2. 原因: 在某个时刻多条语句被一个线程执行的时候, 还没执行完, 就被其他的线程执行了。
  3. 解决方案: 同步代码块:
synchronized(对象){
    需要被同步的代码
}

同步

好处:解决了线程安全的问题
弊端:相对降低了性能, 因为判断锁需要消耗资源, 产生了死锁。

  1. 定义同步是有前提的:
  2. 必须是有两个以上的线程, 才需要同步
  3. 多个线程必须保证使用的是同一把锁

  4. 同步的第二种表现形式: 将同步关键字定义再函数上面, 让函数具备了同步性
  5. 同步函数用的是个锁, 函数都有自己所属的对象this, 所以同步函数所使用的锁就是本对象锁。
  6. 同步函数被static修饰的时候, 这个时候锁就是所属的类,也可以说是字节码文件对象, 也就是类名.class
  7. 同步代码块和同步函数的区别: 同步代码块使用的锁可以是任何对象
  8. 同步函数使用的锁是this, 静态同步函数的锁是字节码文件对象。再一个类中只有一个同步, 可以使用同步函数

    延迟加载的单例模式:
class Single{
private static Single s = null;
private Single(){}
public static Single getInstance(){ //锁是谁?字节码文件对象;
if(s == null){
synchronized(Single.class){
if(s == null)
s = new Single();
}
}
return s;
}
}
  1. 同步死锁:将同步进行嵌套的时候就可以看见同步死锁
  2. 线程间的通信:思路:多个线程按照序列操作同一个资源,仓储模式
  • 将资源封装成对象
  • 将线程执行的任务(run)方法也封装成了对象
  1. 等待唤醒机制:
  • wait:将线程对象存储到线程池
  • notify:到线程池唤醒对象
  • notifyall: 唤醒线程池中所有的纤层
    注意点:

    • 这些方法都需要定义再同步中, 因为要标识所属的锁。
    • 者三个方法都定义再Object类中,因为三个方法都需要定义同步内,并标识同步锁, 而同步锁可以定义为任何对象, 所以这个对象最好的选择就是object
wait和sleep区别:

wait可以指定时间也可以不指定时间,会释放执行权
sleep必须指定时间,不会释放执行权

线程停止:

1.通过定义循环的结束标记

  1. 通过interrupt中断线程

关于优先级的问题:

  1. 设置优先级, 只会再大体上进行控制,就是说数量达到一定的程度的时候才可以。
  2. 而要真正的实现只有通过join和interrupt才可以终端线程, 而不是通过yield让步和设置优先级
  3. setDamon设置,将该线程标记为守护线程, 或者是用户线程
  4. toString可以返回线程的许多信息。 setProprity,getProprity.
Lock接口:

同步是隐示的锁操作,而Lock对象是显示的锁操作

< java.util.concurrent.locks > Condition接口:await()、signal()、signalAll();
--------------------------------------------------------
class BoundedBuffer {
   final Lock lock = new ReentrantLock();
   final Condition notFull  = lock.newCondition();
   final Condition notEmpty = lock.newCondition();
   final Object[] items = new Object[100];
   int putptr, takeptr, count;
   public void put(Object x) throws InterruptedException {
     lock.lock();
     try {
       while (count == items.length)
         notFull.await();
       items[putptr] = x;
       if (++putptr == items.length) putptr = 0;
       ++count;
       notEmpty.signal();
     }
finally {
       lock.unlock();
     }
   }
   public Object take() throws InterruptedException {
     lock.lock();
     try {
       while (count == 0)
         notEmpty.await();
       Object x = items[takeptr];
       if (++takeptr == items.length) takeptr = 0;
       --count;
       notFull.signal();
       return x;
     }
finally {
       lock.unlock();
     }
   }
}
API:

(Application Programming Interface,应用程序编程接口)就是让别人通过访问封装好的类, 而不用去理解底层直接调用即可。

转载于:https://www.cnblogs.com/jwlxtf/p/7932357.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值