zoukankan      html  css  js  c++  java
  • javasE--基础部分--线程

    .线程的相关概念 

    .线程的创建和启动★

    .线程的停止

    .线程的常用方法

    .线程的生命周期★ 

    .线程的同步

    .线程的通信

    .线程的创建方式三

    .线程的创建方式四(线程池

    .线程的相关概念 

    程序:是完成特定任务、用某种语言编写的一组指令的集合

    进程:是程序的一次执行过程,或是正在运行的一个程序 

    线程:进程可进一步细化为线程,线程是进程中的一个细小的单元

    单线程:一个程序,在“同一时刻”只能执行一个线程

    多线程:一个程序,在“同一时刻”可以同时执行多个线程

    双核

    多核

    高并发

    高频率

     

    二、线程的创建和启动

    1、之前的所有代码都是单线程

    2、线程的创建方式(两种)一共是四种

      2.1 a 新建一个类

            b 继承一个类Thread

            c 重写run方法(实际运行的代码)

      启动线程的方式:

      a。 创建一个线程对象

      Thread1 t1=new Thread1();

      b。调用当前线程的start方法

      t1.start();//启动线程的正确方式(实际运行的依然是run方法)

      start() 方法有两个作用:①启动当前线程 ② 调用当前线程

      问题1:我们不能通过直接调用run方法的方式启动线程

       ti.run() //线程只能运行main线程,t1 线程不能执行

      问题2:在启动一个线程,遍历100以内的偶数,不可以还让已经start的线程去执行,会报错IllegalThreadStateException 

     

        public synchronized void start() {
            /**
             * This method is not invoked for the main method thread or "system"
             * group threads created/set up by the VM. Any new functionality added
             * to this method in the future may have to also be added to the VM.
             *
             * A zero status value corresponds to state "NEW".
             */
            if (threadStatus != 0)
                throw new IllegalThreadStateException();
    }

    当start 方法启动时,会判断当前线程状态是不是0 也就是New 状态

    如果想调用新的线程,直接再新建一个T2线程

    2.2 

    a 新建一个类 

    b实现一个接口Runable

    c实现接口中的抽象方法 run方法

    启动线程的方式

    a 创建线程对象

    b通过Thread类进行包装

    c调用Start方法进行启动

    t22.start();//实际运行的依然是t2的run 方法

    3、 练习

    1 创 建两个子线程,让其中一个输出1-100之间的偶数,另一个输出1-100之间的奇数。

    package Demo;
    
    //创建两个子线程,让其中一个输出1-100的偶数,另一个输出1-100之间的奇数
    
    public class Demo3 {
    
    public static void main(String[] args) {
    
    Thread1 t1=new Thread1();
    
    t1.start();
    
    Thread2 t2=new Thread2();
    
    Thread t22=new Thread(t2);
    
    t22.start();
    
     
    
    }
    
    }
    
    class Thread1 extends Thread{
    
     
    
    @Override
    
    public void run() {
    
    // TODO Auto-generated method stub
    
    for (int i = 0; i < 101; i++) {
    
    if (i%2==0) {
    
    System.out.println("偶数"+i);
    
    }
    
    }
    
    super.run();
    
    }
    
     
    
    }
    
    class Thread2 implements Runnable{
    
     
    
    @Override
    
    public void run() {
    
    // TODO Auto-generated method stub
    
    for (int i = 0; i < 101; i++) {
    
    if (i%2!=0) {
    
    System.out.println("奇数:"+i);
    
    }
    
    }
    
    } 
    
    }

    三、线程常见方法

    1. 如何停止一个线程

    a  stop(); 停止一个线程   过时了  不建议使用

    b 可以采用逻辑的方式

    .线程的常用方法

    1、currentThread();获得当前线程的对象   静态方法

    2、Thread[Thread-0,5,main]   [当前线程的名字,当前线程的优先级,当前线程由哪个线程创建的]

    3、getName();获得线程的名字

    4、setName(String name);设置当前线程的名字

    5、getPriority();获得当前线程的优先级      线程的优先级默认和创建他的线程优先级一致

        /**
         * The minimum priority that a thread can have.
         */
        public static final int MIN_PRIORITY = 1;
    
       /**
         * The default priority that is assigned to a thread.
         */
        public static final int NORM_PRIORITY = 5;
    
        /**
         * The maximum priority that a thread can have.
         */
        public static final int MAX_PRIORITY = 10;

    6、setPriority(2);设置当前线程的优先级  范围:1-10  默认优先级是5

      说明:高优先级的线程要抢占低优先级CPU执行权,但是只是从概率上讲高优先级高概率被执行,并不意味着一定先执行  。 

    7、sleep(long time);睡眠  设置时间   单位:毫秒   需要抛出一个异常  InterruptedException e

    8、interrupt();中断(不能中断线程)   (只是改变了当前线程中一个值的状态)

    在中断睡眠或者等待的线程时会抛出一个异常

    9、yield()释放当前CPU的执行权,有可能当前CPU又分配到当前线程

    10、join():在线程a中,调用线程bde join方法,线程a进入阻塞状态,直到线程b完全执行完成,线程a 才继续执行 

    线程分类

     * 守护线程 (负责守护)   当用户线程执行完毕,自己线程无论是否执行完毕都自动结束!

    如何设置守护线程  t1.setDaemon(true);//设置为守护线程     需要在启动之前设置

      记住守护线程的特点:

      java一个最经典的守护线程是  :垃圾回收机制的线程 

      用户线程 (功能)

    2. 练习

    编写程序:在main方法中创建一个线程。线程每隔一定时间(200ms以内的随机时间)

    2.定义一个接口用来实现两个对象的比较。

    interface CompareObject{

    public int compareTo(Object o);   

    //若返回值是 0 , 代表相等; 若为正数,代表当前对象大;负数代表当前对象小

    }

    定义一个Circle类。

    定义一个ComparableCircle类,继承Circle类并且实现CompareObject接口。

    在ComparableCircle类中给出接口中方法compareTo的实现体,用来比较两个圆的面积大小。

    定义一个测试类InterfaceTest,创建两个ComparableCircle对象,

    调用compareTo方法比较两个类的半径大小。 产生一个0-100之间的随机整数,打印后将该整数放到集合中;

      共产生100个整数,全部产生后,睡眠5秒,然后将集合内容打印输出;

    package Demo;
    
     
    
    import java.util.ArrayList;
    
    import java.util.Iterator;
    
    import java.util.List;
    
     
    
    /*在main方法中创建一个线程。线程每隔一定时间(200ms以内的随机时间)
    
    *         产生一个0-100之间的随机整数,打印后将该整数放到集合中;
    
          共产生100个整数,全部产生后,睡眠5秒,然后将集合内容打印输出;*/
    
    public class Demo4 {
    
    public static void main(String[] args) {
    
    Thread3 t1=new Thread3();
    
    t1.start();
    
    }
    
    }
    
    class Thread3 extends Thread{
    
    List<Integer> list =new ArrayList<Integer>();
    
    @Override
    
    public void run() {
    
    super.run();
    
    // TODO Auto-generated method stub
    
    for (int i = 0; i <100; i++) {
    
    try {
    
    Thread.sleep((long) (Math.random()*200+1));
    
     
    
    } catch (Exception e) {
    
    // TODO: handle exception
    
    }
    
    int a = (int)(Math.random()*101);
    
    System.out.println(a);
    
    list.add(a);
    
     
    
    }
    
    try {
    
    Thread.sleep(5000);
    
    } catch (InterruptedException e) {
    
    // TODO Auto-generated catch block
    
    e.printStackTrace();
    
    }
    
    for (Integer integer : list) {
    
    System.out.print(integer);
    
    }

    .线程的同步

    1、线程的同步

    多个窗口买票

    1. 互斥锁

    a. 为一段代码块上锁

    语法:

    synchronized(锁对象【同步监视器】){

    //锁对象是任意对象(this或String对象) 多个线程必须公用同一把锁  

    需要锁住的代码;关键代码;

    }

    b. 为一段代码块上锁

    语法:public synchronized void method(){

    //如何让循环结束

    }

    关于同步方法的总结:

    ① 同步方法仍然设计到同步监视器 ,只是不需要我们显式的声明

    ② 非静态的同步方法,同步监视器是:this

      静态的同步方法,同步监视器是:当前类

    C.

    1.创建一个对象

    ReentrantLock lock=new ReentrantLock();

     

    2.在需要被锁住的上一行执行lock方法

    lock.lock();

    3.在需要被锁住的最后一行下一行执行unlock方法(必须要执行:finally中)

    Lock.unlock();  (必须被执行:finally里中)

    4. 案例

    try {
    
    lock.lock();
    
    if(num<=0){
    
    System.out.println("卖完了");
    
    break;
    
    }
    
    try {
    
    Thread.sleep(400);
    
    } catch (InterruptedException e) {
    
    e.printStackTrace();
    
    }
    
    System.out.println(Thread.currentThread().getName()+"卖出了一张票:剩余:"+--num);
    
    } finally {
    
    lock.unlock();//一定要保证能运行 finally中
    
    }

       synchronized 与lock 的异同?

      相同点:二者都可以解决线程安全问题

      不同点:synchronized 机制在执行完相应的同步代码以后,自动的释放同步监视器

         lock 需要手动的启动同步(lock()),同时结束同步也需要手动的实现(unlock())

    2、死锁的问题

    1、产生原因:

    就是锁与锁之间的相互等待

    一个线程一直占用这个资源,另一个线程一直在等待他结束

    此类用于演示死锁 (避免)

    1. 产生的原因:

    就是锁与锁之间相互等待

    2. 案例见下面代码 

    public class Demo2 {
    
    public static void main(String[] args) {
    
      Thread1 t1=new Thread1();//新生线程
    
      t1.start();//就绪(可运行)线程
    
     
    
      Thread2 t2=new Thread2();
    
      t2.start();
    
    }
    
    }
    
    class Thread1 extends Thread{
    
    @Override
    
    public void run() {
    
      System.out.println("这是Thread1线程");
    
      synchronized ("java") {
    
      try {
    
        Thread.sleep(500);
    
      } catch (InterruptedException e) {
    
          e.printStackTrace();
    
      }
    
      System.out.println("Thread1刚刚睡醒");
    
      synchronized ("html5") {
    
        System.out.println("Thread1内部锁的代码");
    
      }
    
      }
    
    }
    
    }
    
    class Thread2 extends Thread{
    
    @Override
    
    public void run() {
    
    System.out.println("这是Thread2线程");
    
    synchronized ("html5") {
    
    try {
    
    Thread.sleep(500);
    
    } catch (InterruptedException e) {
    
    // TODO Auto-generated catch block
    
    e.printStackTrace();
    
    }
    
    System.out.println("Thread2刚刚睡醒");
    
    synchronized ("java") {
    
    System.out.println("Thread2内部锁的代码");
    
    }
    
    }
    
    }
    
    }

    解决懒汉模式

    package com.atguigu.demon;
    
    /**
    
     * 此类用于演示解决懒汉模式的线程安全问题
    
     * 1. 代码见下面
    
     */
    
    public class Demo3 {
    
     
    
    }
    
     
    
    class Lazy{
    
    private Lazy(){}
    
    private static Lazy lazy;
    
    public static Lazy getLazy(){
    
      if(lazy==null){
    
        synchronized ("aaa") {
    
        if(lazy==null){
    
          lazy=new Lazy();
    
        }
    
      }
    
    }
    
    return lazy;
    
    }
    
    }

    .线程的生命周期★ 

    1、新生线程,(线程对象刚刚诞生)刚new出来

    可运行(就绪)线程(调用Start方法)

    运行线程 (等待cpu分配执行权)

    阻塞线程

    睡眠sleep

    互斥锁

    io阻塞

    暂停join

    等待wait

    死亡线程

    以上需要背下来

    2、线程的让步和线程的插队

    a.线程的让步

    Thread.yield()

    礼让只是比自己优先级高或者同级别的

    如果不满足这个条件该方法失效

    抢到之后只让一次,下次抢到直接执行

    b.线程的插队

    this.join();

    调用方法时,调用线程将被阻塞,直到join()方法加入的join方法执行完成。

    让给谁,谁执行完,我才执行

    .线程的通信

    1、需求

    启动两个线程:一个线程做普通打印操作(0.5秒打印一次) 另一个线程去监控键盘,

    如果有输入则暂停另一个线程的打印,如果在次检测到键盘有输入,继续运行上一个线程

     

    package com.atguigu.demon;
    
     
    
    import java.io.BufferedReader;
    
    import java.io.IOException;
    
    import java.io.InputStreamReader;
    
     
    
    /**
    
     * 此类用于演示线程的通信
    
     
    
     */
    
    public class Demo5 {
    
     
    
    public static void main(String[] args) {
    
      Thread51 t1=new Thread51();
    
      t1.start();
    
     
    
      Thread52 t2=new Thread52();
    
      t2.start();
    
     
    
      Thread53 t3=new Thread53();
    
      t3.start();
    
    }
    
    }
    
    class Thread51 extends Thread{
    
      public static boolean flag=false;
    
      @Override
    
      public void run() {
    
        while(true){
    
          try {
    
            Thread.sleep(500);
    
          } catch (InterruptedException e) {
    
            e.printStackTrace();
    
         }
    
    if(flag){
    
    //让当前线程暂停(等待)
    
    synchronized ("java") {
    
    try {
    
    "java".wait();//Object   
    
    } catch (InterruptedException e) {
    
    e.printStackTrace();
    
    }
    
    }
    
    }
    
    System.out.println(Math.random());
    
    }
    
    }
    
    }
    
    class Thread53 extends Thread{
    
    public static boolean flag=false;
    
    @Override
    
    public void run() {
    
    while(true){
    
    try {
    
    Thread.sleep(500);
    
    } catch (InterruptedException e) {
    
    e.printStackTrace();
    
    }
    
    if(flag){
    
    //让当前线程暂停(等待)
    
    synchronized ("java") {
    
    try {
    
    "java".wait();//Object   
    
    } catch (InterruptedException e) {
    
    e.printStackTrace();
    
    }
    
    }
    
    }
    
    System.out.println("Thread53-----------"+Math.random());
    
    }
    
    }
    
     
    
    }
    
    class Thread52 extends Thread{
    
     
    
    @Override
    
    public void run() {
    
    BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
    
    try {
    
    in.readLine();
    
    //需要改变flag的值
    
    Thread51.flag=true;
    
    Thread53.flag=true;
    
    in.readLine();
    
    Thread51.flag=false;
    
    Thread53.flag=false;
    
    synchronized ("java") {
    
    // "java".notify();//唤醒在"java"这个对象下等待的线程
    
    "java".notifyAll();//唤醒所有在"java"这个对象下等待的线程
    
    }
    
     
    
    } catch (IOException e) {
    
    e.printStackTrace();
    
    }
    
    }
    
     
    
    }
    
     
    
     

     2. 方法

      wait(): 一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器(锁)

      "java".notify();//唤醒在"java"这个对象下等待的线程  每次只能唤醒一个,多个线程wait 唤醒优先级高的。 

      "java".notifyAll();//唤醒所有在"java"这个对象下等待的线程

      特点:

      必须用在synchronized方法或synchronized代码块中,

      这三个方法全部来自于Object类

     sleep()和wait()的异同?

    相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态

    不同点:1)两个方法声明位置不同,Thread类中声明sleep(),Object类中声明wait

        2)调用的要求不同 :sleep() 可以在任何需要的场景调用。wait()必须在同步代码块中使用

        3)sleep()是不会释放锁的,wait()会释放锁

     

    .线程的创建方式三

    1、第三种创建方式

    a 新建一个接口

    b 实现一个接口Callable<方法的返回值>

    c 实现抽象方法Call()

     

    启动方式 :

    创建对象:FutureTask<String> ft=new FutureTask<>(new Thread61());

    使用Thread类进行包装:Thread t=new Thread(ft);

    调用start方法启动:t.start();

        如何拿到返回值

        String str=ft.get();//返回类型默认是泛型类型

    特点:

    主要是和第二种创建方式的对比

    a. 支持泛型 

    b. 不需要手动处理异常 call方法可以抛出异常,被外面操作捕获,获取异常信息

    c. call方法有返回值

    FutureTask<String> ft=new FutureTask<>(new Thread61());
    
    Thread t=new Thread(ft);
    
    t.start();//运行的是call方法
    
    //返回值如何拿
    
    try {
      String str
    =ft.get();   System.out.println("-----------"+str); } catch (InterruptedException e) {   // TODO Auto-generated catch block   e.printStackTrace(); } catch (ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } class Thread61 implements Callable<String>{ @Override public String call() throws Exception { String str="java"; for (int i = 0; i < 10; i++) { str+=i; System.out.println(str); } return str; } }

     Future 接口

      可以对具体Runable 、callable 任务的执行结果进行取消、查询是否完成、获取结果等。

      FutrueTask 是Futrue 接口的唯一的实现类

      FutrueTask 同时实现Runable,Funture 接口,它既可以作为Runable被线程执行,又可以作为Future得到Callable 的返回值。

     

    .线程的创建方式四(线程池

     2. 第四种创建方式 (线程池)   数据库连接池

    a. 创建了一个线程池容量为5

    ExecutorService es=Executors.newFixedThreadPool(5);

    b. submit(Runnable/Callable<>);  添加子线程   直接就启动

    c. 关闭资源 

    es.shutdown();

    public class Demo6 {
    
     
    
    public static void main(String[] args) {
    
     
    
    //第四种创建方式
    
    //1. 创建了一个线程池容量为5
    
    ExecutorService es=Executors.newFixedThreadPool(5);
    
    //2. 添加子线程
    
    es.submit(new Runnable() {
    
    @Override
    
    public void run() {
    
    for (int i = 0; i < 10; i++) {
    
    try {
    
    Thread.sleep(500);
    
    } catch (InterruptedException e) {
    
    // TODO Auto-generated catch block
    
    e.printStackTrace();
    
    }
    
    System.out.println("----------"+i);
    
    }
    
     
    
    }
    
    });
    
    es.submit(new Runnable() {
    
    @Override
    
    public void run() {
    
    for (int i = 0; i < 10; i++) {
    
    try {
    
    Thread.sleep(500);
    
    } catch (InterruptedException e) {
    
    // TODO Auto-generated catch block
    
    e.printStackTrace();
    
    }
    
    System.out.println(">>>>>>>>>>"+i);
    
    } 
    
    }
    
    });
    
    
    
    //3. 关闭
    
    es.shutdown();
    
    }
    
    }

    Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池

    ① Exectors.newCachedThreadPool():用于创建一个可根据需要创建新线程的线程池

    ② Exectors.newFixedThreadPool(n):创建一个可重用固定线程数的线程池

    ③ Exectors.newSingleThreadPool():创建一个只有一个线程的线程池

    ④ Exectors.newScheduledThreadPool(n):创建一个线程池,它可安排在给定延迟后 运行命令或定期地执行

  • 相关阅读:
    junit4
    spring
    hibernate 的注意事项
    Struts2 的 命名规范
    Struts2 的标签
    OGNL
    添加删除 板块 struts2
    Struts2 的各种xml 和struts 配置信息 都是一样的
    struts2
    struts2
  • 原文地址:https://www.cnblogs.com/minmin123/p/11207046.html
Copyright © 2011-2022 走看看