本帖最后由 小石姐姐 于 2018-11-22 09:31 编辑  
 
多线程 
并发: (交替执行) 指两个或多个事件在"同一时间段内"发生[排队,轮流干] 
 
并行: (同时执行) 指两个或多个事件在"同一时刻"发生 (同时发生)[同时进行] 
 
进程: 一个应用程序在内存中的一次执行过程,一个程序至少包含一个进程 
 
线程: 是进程内的一个独立执行单元 (一条代码执行路径),一个程序运行后至少有一个进程, 一个进程中可以包含多个线程 
多线程的好处: 
效率高 
多个线程之间互不影响 
线程的调度 
1,分时调度:平分CPU时间,轮流占用cpu 
2.抢占式调度:优先让优先级高的线程使用CPU; 如果优先级相同, 则随机选择一个线程执行 
主线程 
我们以前编写的代码, 也在一条线程中执行, 该线程叫作"main线程", 也称为"主线程" 
 
单线程(程序只有一个主线程,main线程)的执行特点:同一个线程内的代码, 从上往下依次执行 
创建多线程的方式 
创建多线程程序的第一种方式: 继承Thread类 
实现多线程的第一种方式: 
1. 定义类, 继承 Thread 类 
2. 重写 run() 方法, run方法内部是线程要执行的任务 
3. 创建Thread子类的对象, 调用 start() 方法启动线程 
java.lang.Thread类: 表示线程. 实现了Runnable接口 
          void start(): 启动线程, 即让线程开始执行run()方法中的代码 
 
注意: 
必须调用 start() 方法来开启线程, 不能直接调用 run() 方法, 调用 run() 会变成单线程 
同一个线程对象, 不能多次调用 start() 方法 
Java是抢占式调度, 不同线程的代码, 执行顺序是随机的 
多线程原理1: 线程执行的随机性 
CPU执行哪个线程是随机的, 不能人为干预 
Java线程调度是抢占式的, 多个线程互相抢夺CPU的执行权 
CPU高速随机切换 (本质) 
线程抢夺CPU资源 
多线程创建方式: 
第一种方式: 继承Thread类 
定义类继承Thread 
重写run()方法, 要执行的任务 
创建子类的对象, 调用start()方法启动线程 
多线程原理2: 多线程的内存 
多线程情况下, 每个线程都有各自的栈内存 
每个线程各自的方法调用, 进的是各自线程的栈 
"栈"是每个线程各自的, "堆"是所有线程共用的 
线程常用方法:Thread常用方法: getName(), currentThread() 
java.lang.Thread类: 表示线程. 实现了Runnable接口 
// 构造方法 
  Thread(): 创建Thead对象 
                  Thread(String threadName): 创建Thead对象并指定线程名 
                  Thread(Runnable target): 通过Runnable对象创建Thread对象 
                  Thread(Runnable target, String threadName): 通过Runnable对象创建对象并指定线程名 
// 成员方法 
void run(): 用于让子类重写, 表示该线程要执行的任务.不能直接调用 
  void start(): 启动线程, 即让线程开始执行run()方法中的代码 
  String getName(): 获取线程的名称 
  void setName(String name): 设置线程名称 
// 静态方法  
                  static Thread currentThread(): 返回对当前正在执行的线程对象的引用 
                  static void sleep(long millis): 让所在线程睡眠指定的毫秒 //出现异常,需要try..catch处理 
java.lang.Thread类: 表示线程. 实现了Runnable接口 
// 构造方法 
  Thread(String threadName): 创建Thead对象并指定线程名 
          // 成员方法 
                  void setName(String name): 设置线程名称 
创建多线程程序的方式2: 实现Runnable接口 
创建线程的第2种方式: 
1. 定义类, 实现Runnable接口 
2. 重写 run() 方法, 要执行的代码(任务) 
3. 创建Runnable实现类对象 (任务对象) 
4. 创建Thread类对象, 在构造方法中传入Runnable实现类对象 (将任务和线程绑定) 
5. 通过Thread对象调用 start() 方法启动线程 
java.lang.Thread类: 表示线程. 实现了Runnable接口 
// 构造方法 
                  Thread Thread(Runnable target): 通过Runnable对象创建Thread对象 
                  Thread Thread(Runnable target, String threadName): 通过Runnable对象创建对象并指定线程名 
Thread和Runnable的区别 
实现Runnable的好处: 
1. 避免单继承的局限性 
2. 增强了程序的扩展性, 降低了程序的耦合性(解耦) 
线程是Thread, 任务是Runnable实现类对象. 相当于将线程和任务分离 
 
耦合性: 相互之间的关系的紧密程度 
耦合性高: 相互之间的关系非常紧密 
耦合性低: 相互之间的关系不太紧密 
我们追求 "低耦合" 
匿名内部类方式创建线程 
// 简化代码, 直接在Thread的构造方法中, 传递匿名内部类 
[Java] 纯文本查看 复制代码 public class Test {
     public static void main(String[] args) {
   new Thread(new Runnable() {
   @Override
  public void run() {
     for (int i = 0; i < 100; i++) {
   System.out.println(Thread.currentThread().getName() + "---" + i);
      }
  }
  }).start();
}
} 
线程安全问题(多线程访问了共享的数据就会产生线程安全问题) 
线程安全问题的原因 
问题发生场景: 
多个线程操作共享资源 
问题发生原因: 
JVM是抢占式调度, CPU在每个线程之间切换是随机的, 代码执行到什么位置是不确定的 
在操作共享资源时, 由于一个线程还没有执行完, 另一个线程就来操作, 就会出现问题 
如何解决: 
在操作共享资源时, 让线程一个一个来执行, 不要并发操作共享变量, 就可以解决问题 
解决线程安全问题方式1: 同步代码块         
同步代码块: 使用 synchronized 关键字修饰的代码块, 并传入一个当作锁的对象 
格式: 
  synchronized (锁对象) { 
// 操作共享数据的代码 
 } 
注意: 
锁对象可以是"任意类型的一个对象" 
锁对象必须是"被多个线程共享的唯一的"对象 
锁对象的作用: 只让一个线程在同步代码块中执行 
同步技术解决线程安全问题的原理 
锁对象, 也称为"同步锁", "对象锁", "对象监视器" 
同步代码块锁唯一;必须包含所有可能会产生线程安全问题的代码块 
同步方法锁对象就是实现类的对象,所以要保证实现类对象唯一 
同步的原理: 
 线程进入同步代码块前, 会"争夺锁对象", "只有一个线程"会抢到锁对象 
 进入同步代码块的线程, 会"持有锁对象", 并执行同步代码块中的代码 
此时同步代码块外的线程, 处于"阻塞"状态, 只能等待 
当同步代码块内的线程执行完代码块, 会离开同步代码块, 并"归还锁对象"给同步代码块 
等在同步代码块外的其他线程就可以继续争夺锁对象 
解决线程安全问题方式2: 同步方法 
同步方法: 使用 synchronized 关键字修饰的方法, 具有默认的锁对象 
非静态同步方法的锁对象: this 
// 非静态同步方法 
public synchronized void method(){ 
// 可能会产生线程安全问题的代码 
} 
静态同步方法 
静态同步方法的锁对象: 当前类的字节码对象 Class对象 
静态同步方法: 
public static synchronized void method(){ 
// 可能会产生线程安全问题的代码 
} 
附注:获取一个类的字节码对象的3种方式: 
1. 对象名.getClass()      new RunnableImpl().getClass() 
2. 类名.class         RunnableImpl.class 
3. Class.forName("类的全名");  Class.forName("com.itheima.test05.RunnableImpl"); 
字节码对象的特点: 
同一个类, 他的字节码对象只有"唯一的一个" 
解决线程安全问题方式3: Lock锁 
java.util.concurrent.locks.Lock接口: JDK 5 新增的Lock接口 
// 成员方法 
void lock(): 获取锁 
void unlock(): 释放锁 
java.util.concurrent.locks.ReentrantLock类: Lock的实现类 
使用方式: 
[Java] 纯文本查看 复制代码 public class RunnableImpl implements Runnable {
  // 成员变量创建锁对象, 该锁对象也要所有线程共享唯一一个
  Lock lock = new ReentrantLock();  // 成员变量
                  @Override
  public void run() {
            // 加锁
    lock.lock();
    try {
      // 操作共享变量的代码...
   } finally {
      // 在finally中保证释放锁
      lock.unlock();
   }
}
} 
线程间的通信 
线程状态概述 
Object类中关于线程的方法: 
java.lang.Object类: 
// 成员方法 (<<注意>>: 只能通过"锁对象"调用) 
void notify(): 随机唤醒在同一个锁对象上的某一个处于等待状态的线程 
void notifyAll(): 唤醒所有在同一个锁对象上处于等待状态的线程 
void wait(): 让当前线程处于"无限等待"状态 
void wait(long timeout): 让当前线程处于"计时等待"状态, 时间到或被唤醒后结束此状态 
void wait(long timeout, int nanos): 让当前线程处于计时等待状态, 时间到或被唤醒后结束此状态 
"注意!! 以上方法只能通过锁对象调用" 
wait() 和 sleep() 的区别: 
1. wait会释放锁, 恢复时需要重新获取锁; sleep不会释放锁 
2. wait可以被notify/notifyAll唤醒; sleep不会 
3. wait要用锁对象调用; sleep要用Thread类名调用 
线程的六种状态 
                     新建   执行  阻塞   休眠    无限等待    死亡状态 
C:/Users/Administrator/AppData/Local/YNote/data/qq0160B4AE0B97BA596F0CCCA205A4CECF/200dda6e70d04a6ab894d96a85703c71/clipboard.png 
 
 
 
 
 
 
 |   
        
 
    
    
    
     
 
 |