java - 多线程 01 - 初步了解创建线程和线程安全问题

一、创建线程的多种方式

  1、继承Thread类

public class Demo1 extends Thread {
   
   public Demo1(String name) {
      super(name); // 给线程命名
   }
   
   @Override
   public void run() {
      while(!interrupted()) {
         System.out.println(getName() + "线程执行了 .. ");
         try {
            Thread.sleep(200);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }
   
   public static void main(String[] args) {
      // 创建线程
      Demo1 d1 = new Demo1("first-thread");
      Demo1 d2 = new Demo1("second-thread");
      
      // 启动线程
      d1.start();
      d2.start();

      // 守护线程:随着主线程的结束也会结束线程,不论线程是否执行完毕; true:守护线程;false:用户线程
      //d1.setDaemon(true);

      // 停止线程(方法已过期,不建议使用);
      // 使用这种方式去停止线程的话,是一种不正常的中断,所以这个线程获取的锁和其他资源都不会释放
      //d1.stop();

      // 中断线程:把线程的中断标志改成 “已中断” ,然后在线程中通过判断 中断标志 来决定是否(正常)结束线程
      d1.interrupt();
   }
   
}

  2、实现Runnable接口

public class Demo2 implements Runnable {

   @Override
   public void run() {
      while(true) {
         System.out.println("thread running ...");
      }
   }

   public static void main(String[] args) {

      // 用 Tread 来包装 Runnable 线程对象来启动线程
      // 被包装的 线程对象 作为target 参数传入 Thread 中
      /*
      Thread 中的方法
      @Override
      public void run() {
         if (target != null) {
            target.run();
         }
      }*/
      Thread thread = new Thread(new Demo2());
      thread.start();
   }
}

  3、匿名内部类的方式

public class Demo3 {
   
   public static void main(String[] args) {
      // 匿名内部类 启动线程 -- Thread方式
      /*new Thread() {
         public void run() {
            System.out.println("thread start ..");
         };
      }.start();*/

      // 匿名内部类 启动线程 -- Runnable方式
      /*new Thread(new Runnable() {
         @Override
         public void run() {
            System.out.println("thread start ..");
         }
      }).start();*/
      
      // 当 Runnable 类型的匿名内部类 Thread和Runnable都有重写run方法时,执行的是 Thread 的run方法
      /**
       * class Thread implements Runnable {
       *
       * 可看出 Thread 是 Runnable 的实现类,即是子类
       * 故调用方法时 是调用子类的方法
       */
      new Thread(new Runnable() {
         @Override
         public void run() {
            System.out.println("runnable");
         }
      }) {
         public void run() {
            System.out.println("sub"); // 执行 Thread 的 run()
         };
      }.start();
      
      
   }
}

  4、带返回值的线程

/**
* Callable 有返回值和异常的线程
*/
public class Demo4 implements Callable<Integer> {
   
   
   public static void main(String[] args) throws Exception {
      Demo4 d = new Demo4();
      // 对线程任务的一个封装
      FutureTask<Integer> task = new FutureTask<>(d);
      
      Thread t = new Thread(task);
      
      t.start();
      
      System.out.println("我先干点别的。。。");
      
      Integer result = task.get();
      System.out.println("线程执行的结果为:" + result);
   }

   @Override
   public Integer call() throws Exception {
      System.out.println("正在进行紧张的计算....");
      // 模拟计算时间
      Thread.sleep(3000);
      return 1;
   }

}

  5、定时器

public class Demo5 {

   public static void main(String[] args) throws InterruptedException {
      // jdk 提供的Timer类定时器
      Timer timer = new Timer();

      // schedule(TimerTask task, long delay, long period)
      // task 执行任务,delay 延时多长时间开始执行, period 每隔多长时间执行一次
      // 这种方法的线程实现简单,但更难控制
      timer.schedule(new TimerTask() {

         @Override
         public void run() {
            // 实现定时任务
            System.out.println("timertask is run");
         }
      }, 0, 1000);
      while (true){
         System.out.println("====");
         Thread.sleep(1000);
      }
   }
}

  6、线程池的实现

public class Demo6 {

   public static void main(String[] args) {
      //创建一个 10个容量的线程池
      //ExecutorService threadPool = Executors.newFixedThreadPool(10);

      // 创建一个缓冲线程池,不断的在线程池中创建新的线程,当觉得线程数量够用的时候则不创建,否则创建新的
      ExecutorService threadPool = Executors.newCachedThreadPool();

      for (int i = 0; i < 1000; i++) {
         // 线程池用 execute(new Runnable(){ run(){xxx} }) 来启动线程
         threadPool.execute(new Runnable() {
            @Override
            public void run() {
               System.out.println(Thread.currentThread().getName());
            }
         });
      }
      // 线程池销毁(停止线程池)
      threadPool.shutdown();
   }
}

  7、Lambda表达式实现

/**
*     Lambad 表达式
*     对并发的支持是非常好的,性能较高
*  函数式编程
*/
public class Demo7 {
   
   public static void main(String[] args) {
      
      List<Integer> values = Arrays.asList(10,20,30,40);
      int res = new Demo7().add(values);
      System.out.println("计算的结果为:" + res);
      
      
   }
   
   public int add (List<Integer> values) {
      // 并行的打印 values 的数据  forEach(对象::方法)
      // values.parallelStream().forEach(System.out :: println);

      // 先获取 “并发流”parallelStream() -- 调用 mapToInt() 方法 将 “流” 转成 int 类型的来进行运算
      return values.parallelStream().mapToInt( i -> i * 2).sum(); // i -> i*2 表示 i = i*2(i为流中的每一个值)
   }

}

  8、Spring 实现多线程

        在相应的方法上标注 @Async //开启异步调用

        @EnableAsync // 开启支持异步任务

        然后异步调用其方法即可实现 spring 多线程(异步调用 相当于 在后台运行该方法)

 

二、多线程带来的风险

    1、线程安全性问题的前提

        1.1、多线程环境下

        1.2、多个线程共享同一资源

        1.3、对资源进行非原子性操作

 

    2、活跃性问题

        2.1、死锁

            --- 1 在等待 2 的资源,2 在等待 3 的资源,3 在等待 1 的资源;成为一个死循环,不可能得到资源的情况,则称为死锁;

        2.2、饥饿

            --- 排队时中的插队现象,一时插不上的队的那个则获取不到资源,但并不是 不可能得到资源的这种情况 ,则称为饥饿

            2.2.1、饥饿与公平

                2.2.1.1、高优先级吞噬所有低优先级的CPU时间片

Thread t1 =  new Thread(new Target());
// 设置优先级 1-10,数值越大优先级越高
t1.setPriority(Thread.MIN_PRIORITY);

                2.2.1.2、线程被永远堵塞在一个等待进入同步快的状态

                2.2.1.3、等待的线程永远不被唤醒

            2.2.2、如何尽量避免饥饿问题

                2.2.2.1、设置合理的优先级

                2.2.2.2、使用锁来代替synchronized

        2.3、活锁

            --- 特殊的排队时中的插队现象,始终插不上的队的那个则获取不到资源,但 是一种 不可能得到资源的这种情况 ,则称为活锁

    3、性能问题

        多线程执行一定会快吗? --- 不一定,线程的切换是非常消耗cpu的资源的;

 

 

有啥不正确的还望大佬指正,谢谢

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值