首页 > 编程语言 > 详细

多线程

时间:2021-02-22 20:23:00      阅读:34      评论:0      收藏:0      [点我收藏+]

多线程

很多线程是模拟出来的,真正的多项成是指由多个CPU,即多核,如服务器。如果是模拟出来的多线程,只有一个CPU,在同一个时间点,CPU只能执行一个代码,因为切换的很快,所以就有同时执行的错局。

  • 线程就是独立的执行路径

  • 在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程

  • main()称之为主线程,为系统的入口,用于执行整个程序

  • 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序不能人为干预

  • 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制

  • 线程会带来额外的开销,如CPU调度时间,并发控制开销

  • 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致

1、三种创建方式

  1. Thread class 继承Thread类(重点)

  2. Runnable 实现Runnable接口(重点)

  3. Callable 实现Callable接口(了解)

继承Thread类

  • 子类继承Thread类具备多线程能力

  • 启动线程:子对象.start()

  • 不建议使用:避免OOP单继承局限性

实现Runnable接口

  • 实现接口Runnable具有多线程能力

  • 启动线程:传入目标对象+Thread对象.start()

  • 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用

2、静态代理

  • 真实对象和代理对象都要实现同一个接口

  • 代理对象代理真实角色

好处:代理对象可以做很多真实对象做不了的事情,真实对象专注做自己的事情

3、Lamda表达式(λ)

函数式接口的定义:

  • 任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口

  • 对于函数式接口,我们可以通过lambda表达式来创建该接口的对象

4、线程的五大状态

创建状态——就绪状态——运行状态——阻塞状态——死亡状态

①、线程停止

//建议线程正常停止--->利用次数,不建议死循环
//建议使用标志位
//不要使用stop或者destroy

②、线程休眠

//模拟网络延时
   Thread.sleep();
//作用:放大问题
  • sleep(时间)指定当前线程阻塞的毫秒数

  • sleep存在异常InterruptedException

  • sleep时间到达后线程进入就绪状态

  • sleep可以模拟网络延时,倒计时等

  • 每一个对象都有一个锁,sleep不会释放锁

package cn.lucky.state;
?
import java.text.SimpleDateFormat;
import java.util.Date;
?
/**
* @author lucky
*/
public class TestSleep2{
?
   public static void main(String[] args) {
       //模拟倒计时
       try {
           tenDown();
      } catch (InterruptedException e) {
           e.printStackTrace();
      }
       //打印当前时间
       Date startTime = new Date(System.currentTimeMillis());
       while(true){
           try {
               Thread.sleep(1000);
               System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
               startTime = new Date(System.currentTimeMillis());//更新当前时间
          } catch (InterruptedException e) {
               e.printStackTrace();
          }
      }
  }
?
   public static void tenDown() throws InterruptedException {
       int num =10;
       while (true){
           Thread.sleep(1000);
           System.out.println(num--);
           if(num<0)break;
      }
  }
}
?

③、线程礼让(yield)

Thread.yield();
//礼让不一定成功,看CPU心情

④、线程强制执行(join)

thread.join();//插队

⑤、线程优先级(Priority)

getPriority();
setPriority();
  • 优先级低只是意味着获得调度的概率低,并不是优先级低就不会被调用了,这都是看CPU的调度。

  • 线程优先级的设定建议在start()调度之前

⑥、守护线程(daemon)

thread.setDaemon(true);//默认是false表示是用户线程,正常的线程都是用户线程

5、线程同步

  • 由于同一-进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入锁机制synchronized ,当-个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可. 存在以下问题:

  • 一个线程持有锁会导致其他所有需要此锁的线程挂起;

  • 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题;

  • 如果一个优先级高的线程等待一个优先级低的线程释放锁 会导致优先级倒置,引起性能问题.

  • synchronized(Obj){}//synchronized同步锁

程序里面避免死锁

产生死锁的四个必要条件

  • 互斥条件:一个资源每次只能被一个进程使用

  • 请求与保持条件:一个进行因请求资源而阻塞时,对已获得的资源保持不放

  • 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺

  • 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系

synchronized与Lock的对比

  • Lock是显式锁(手动开启和关闭锁,别忘记关闭锁) synchronized是隐式锁, 出了作用域自动释放

  • Lock只有代码块锁,synchronized有代码块锁和方法锁

  • 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)

  • 优先使用顺序:

    Lock >同步代码块(已经进入了方法体,分配了相应资源) >同步方法(在方法体之外)

6、线程池

  • 背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。

  • 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。

  • 好处:

  1. 提高响应速度(减少了创建新线程的时间)

  2. 降低资源消耗(重复利用线程池中线程,不需要每次都创建)

  3. 便于线程管理

  4. corePoolSize: 核心池的大小

  5. maximumPoolSize:最大线程数

  6. keepAlive Time:线程没有任务时最多保持多长时间后会终止

使用线程池:

  • JDK 5.0起提供了线程池相关API: ExecutorService 和Executors

  • ExecutorService: 真正的线程池接口。常见子类ThreadPoolExecutor

  • void execute(Runnable command) :执行任务/命令,没有返回值,- 般用来执行Runnable

  • <T> Future<T> submit(Callable<T> task):执行任务,有返回值,-般又来执行Callable

  • void shutdown() :关闭连接池

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

7、总结

package cn.lucky.gaoji;
?
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
?
/**
* @author lucky
*/
public class ThreadNew {
   public static void main(String[] args) {
       new MyThread1().start();
?
       new Thread(new MyThread2()).start();
?
       FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyThread3());
       new Thread(futureTask).start();
?
       try {
           Integer integer = futureTask.get();
           System.out.println(integer);
      } catch (InterruptedException e) {
           e.printStackTrace();
      } catch (ExecutionException e) {
           e.printStackTrace();
      }
  }
?
}
?
//1.继承Thread类
class MyThread1 extends Thread{
   @Override
   public void run() {
       System.out.println("myThread1");
  }
}
//2.实现Runnable接口
class MyThread2 implements Runnable{
   @Override
   public void run() {
       System.out.println("myThread2");
  }
}
//3.实现callable接口
class MyThread3 implements Callable<Integer>{
   @Override
   public Integer call() throws Exception {
       System.out.println("myThread3");
       return 100;
  }
}

 

多线程

原文:https://www.cnblogs.com/lucky8991/p/14432390.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!