JavaSE——Java多线程


多线程总结: 


本文总结自传智博客毕老师的java基础视频也包含了作者自己对异常的一些理解

1.进程和线程有什么区别?

a. 进程是指一个正在执行的程序,每一个进程都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元

b. 线程是进程中的一个独立的控制单元,线程在控制着进程的执行

b.1 一个进程中至少有一个进程

b.2 JVM启动的时候会有一个进程java.exe,该进程中至少有一个线程在负责Java程序的执行

b.3 b.2中的线程运行的代码存在于main方法中, 被线程被成为主线程

b.4 更细节说明是, JVM启动不止一个线程,还有garbage collector负责的线程

2. 如何创建一个线程?

a. 继承Thread类的方法

a.1 定义一个类继承Thread;

a.2 复写run()方法: 自定义的要运行代码存储在run方法中,线程运行执行run()中代码(这也是要复写run的原因)

a.3 在需要的地方创建一个线程, 调用start()方法,该方法可以 i) 启动线程; ii) 调用run()方法

class MyThread extends Thread{   // --> a.1
      public void run(){        //  --> a.2
            // 线程中要运行的代码
      }
}

class Demo{
      public static void main(String[] args){
           MyThread mt=new MyThread();  // --> a.3
           mt.start();                 // --> a.3
      }
}

b. 传入一个实现Runnable接口的类的方法

b.1 定义一个类继承Runnable接口

b.2 覆盖Runnable接口中的run()方法: 将线程要运行的代码存放在该run方法中

b.3 通过Thread类建立线程对象, 并将Runnble接口的子类对象作为实际参数传给Thread类的构造函数

b.4 调用start()方法开始线程,执行run()

class MyCode4Thread2Run implements Runnable{ // --> b.1
      public void run(){      // --> b.2
         // 线程中要运行的代码
      }
}

class Demo{
      public static void main(String[] args){
            Thread t=new Thread(new MyCode4Thread2Run()); // --> b.3
            t.start(); // --> b.4
      }
}

 

3. 两种创建方法哪个更好?

a. 考虑, 比如一个类需要继承另外一个类并且还要有再另外一个线程内执行, 此时用2.a就会出现单一继承的局限性

b. 考虑2.a的方式中run()是在Thread类中的, 每开一个线程,就多一个Thread类中的成员变量的copy,run()中操作的也是自己类中的成员的copy ; 2.b只要Thread内传入的是同一个Runnable子类对象,那么无论多少个Thread都只是操作一份Runnable子类对象内的成员变量; 

c. 所以, 如果用2.b的方式, 由于是接口实现(也正是接口的好处), 不会有单一继承的局限性, 对于多个同功能线程操作同一资源的情况更容易实现 (如果希望2.a可以操作同一资源,需要将成员变量定义为static)

d. 所以, 用Runnable子类实现的方法更好


4. 多个线程之间是如何独立并且"同时"执行的, 如何理解线程的随机性?

a. 多线程间并不是正在的并行执行, 而是CPU在每个时刻分配给多个线程中的一个线程执行权, CPU在一段时间内在多个线程见跳转执行

b. 由于CPU的执行速度很快, 每个分配执行权内线程的执行时间很短, 跳转频率很快, 造成了“同时”执行的假象(多核CPU有一定的并行性)

c. 线程的随机性表现在CPU执行权的分配是随机的, 多线程运行时, 可能会每次运行的结果都不同

5. 没有拿到运行权的线程在干什么? 线程一共有几种状态, 在多线程运行时各个线程是怎么在这几个状态见转变的?

a. 没有拿到执行权的线程处于一种临时状态,其具有运行资格但是,没有运行

b. 具体的状态下图可以清晰地说明, 运行--临时状态, 就是CPU执行权分配决定的, 运行--冻结, 是人为干预的, 应用于线程的协作





6. 线程执行权内可执行的最小的转换分区是什么, 是一段代码, 一行代码, 还是更小的划分?暂时无法回答

7. 如果线程执行的最小语句很小, 而多条线程在操作同一资源的时候不是会导致语句间无法像单线程时一样保持连续性?

a. 是的,而且这种情况经常存在, 如下面的程序, 两次加法有先后顺序,但是打印结果确漏掉了一次

// Thread-1,2 有两个线程,其run()函数如下
int sum=0;
public void run(){
     sum=sum+100;   // Step1: sum=0,1线程运行一次,挂起没打印sum=200; Step2: 2线程运行一次,挂起没打印
     System.out.println(sum +"after "+ t.getName() +" added");  // Step3: 1线程唤醒, 打印然后挂起 200 after Thread-1 added; 
      // Step4: 2线程唤醒, 打印然后挂起 200 after Thread-2 added;           
}

b. 为了避免这种情况的存在, 本质是要让回复线程中代码执行的连续性, 让一部分需要一个线程连续执行的代码在一起连续执行

c. Java给出了同步(synchronized)的方式解决这个问题(此机制被后来的lock基本取代)

8. Java同步(synchronized)的机制是什么,是如何保证代码连续性的?

synchronized(lock){   //
// 需要保证连续性的代码
}
public synchronized void method(){
// 方法内的代码可以保证在一个线程内连续地执行
}
public static synchronized void staticMethod(){
// 方法内的代码可以保证在一个线程内连续地执行
}

a. Java 同步代码块 + 同步方法 

a.1 同步代码块的锁是指定的,可以是任意的一个对象; 同步方法的锁是JVM指定的,非静态是本类对象this; 静态是本类 .class类对象

a.2 同步的机制是, 当一个线程运行到这段代买时, 将同步代码块/同步方法的区域上锁, 并将钥匙拿走

a.3 lock上锁动作是对所有的包含此lock的同步代码块的,即一个线程进入了此代码块, 其他所有带这个锁的同步代码块也被锁上了(这也是同步可以在多个run方法中协同操作同一资源的好处, 但是嵌套同步代码后也是死锁产生的原因之一)

a.4 出代码块, 解锁, 把钥匙归还给代码块

b.  a.1-a.4 保证了, 一段代码在一段时间内是被一个线程连续执行的, 但是,这个“连续”只是代码上的连续,而不是时间上的连续

c.  synchronized无法干预CPU的执行权分配,只是执行权分配到其他线程时,其他线程毫无作用,其实相当于浪费执行权,这也是为什么说同步效率低的原因,线程越多越明显


9. 8.a.3中提到了死锁, 死锁是怎么产生的?

a. 死锁产生于锁的相互嵌套

b.  甲拿着A锁的钥匙想要进入锁着B锁的房间, 而此时乙拿着B锁的钥匙想要进入锁着A锁的房间

Thread 甲: synchronized(Lock A){synchronized(Lock B){}};
Thread 乙: synchronized(Lock B){synchronized(Lock A){}};


10. 多线程间的执行权是由CPU决定的,如果不能干预CPU做详细的执行权分配,但是能控制线程的执行顺序不是也很好吗?

a. 是的,而且很有必要,但是控制执行顺序不是对CPU干预,而也是曲线地通过将某些线程冻结-唤醒的机制完成的

b. Java提供了 监视器(锁).wait()/notify()的操作完成冻结-唤醒, 且,这个wait(),notify()是完全对应监视器(锁)的, 而非针对某个特定的线程

c. 这个wait()/notify()操作往往是有一个判断条件的, 比如, 某个线程得到了执行权, 判断一个条件, 条件满足, 此线程等待

d. c中可以看出, 只是某个线程刚巧获得了运行权,刚巧满足了判断条件, 此线程才被wait()的,所以 锁.wait() 操作的是满足条件的当前运行线程,但是不是特定的线程 

e. notify(); try()会有InterruptedException(); try-catch surrounded 

f. 在多个生产者,消费者,甚至多把锁的情况下, 选择 JDK 1.5升级后的同步解决方案 Lock-Condition类, synchronized对锁获取和释放有一些严格的定义


11. Lock-Condition的机制是什么,它比synchronized好用?

a. synchronized,实现自动解锁,其局限性就表现在, 不可以灵活地在安排解锁代码存在的位置 As---Bs----Be----Ae;  不可以 As----Bs----Ae----Be

b. Lock接口, RetrantLock子类: myLock.lock(); <---> 上锁    myLock.newCondition <--->标示生产一个绑定于此myLock的新的条件类, 冻结-唤醒都是Condition控制的

c. 上锁,一定要解相应的锁, 用try-finally-throws InterruptedException 完成

d. Condition接口: Condition代替了监视器, 可以更加灵活的,而且有一个更直观的名字去控制满足条件的线程的冻结-唤醒 condition.await()/ condition.single();

e. Lock-Condition可以实现synchronized的功能且比synchronized更灵活, 可以实现lock和lock的condition的多种不同的操作组合


12. 懒汉式的单例设计模式为什么会存在多线程的安全问题,如何解决?

class LazySingleton{
        private static LazySingleton ls=null;         // --> a 拆分到后面生产
        public static LazySingleton getSingleton(){
                 if(ls==null){                // --> c 为提高效率分析何时才需要同步, 只有当首次调用时,需要同步,加一次判断
                       synchronize{           // --> b 如果不加同步,下一步 if判断完可能多个线程同时挂起,ls被覆盖,但每次调用函数都要同步
                         if(ls=null)          // --> a 如果null,生产一个实例
                              ls=new LazySingleton();
                        }  
                 }
                 return ls;
        }
}

a. 懒汉式单例设计模式产生多线程安全问题的本质是, 将本来可以连续执行的一句执行的代码分成了两部分, 这种分开造成安全问题

b. 所以解决办法就是加同步

c. 因为此处同步代码只需要在一定的条件下执行, 为了提高效率, 还可以在满足需求的情况下做一些小改进



13. Thread类中的方法还没有见识过,有哪些?

a. interrupt(); 强制一个让一个处于冻结状态的线程恢复运行状态, 于notify的区别是, interrupted直接作用于线程, notify作用于监视器标记的线程池中第一次冻结的线程

b. setDaemon(); 将一个线程定义成守护线程, 如果全是守护线程, JVM结束; 如果要定义成守护线程,必须在start()前用此方法

c. join(); 在一个线程A中 B.join(); 一下, 此线程A即放弃执行, 让其余线程执行, 直到Join来的B线程执行完, A才继续执行; join()会有异常InterruptedException

d. getPriority()/setPriority; 获取/设置线程的优先级有关

e. yield(); 临时释放一下当前的执行线程的执行权

 

14. 多线程中安全问题产生有什么一般性的规律吗? 有足够经验了再总结

 

参考资料: 传智博客毕老师Java基础-多线程部分

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值