Java从零开始学习——Java多线程(未完结)

多线程详解

1.简介

任务:包括用户对计算机操作时的各个动作及所对应的响应事件,如鼠标单击、右击、打开一个对话框、关闭一个文件、启动一个程序等。

程序:指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。

进程(Process)则是执行程序的一次执行过程,是一个动态的概念,是系统资源分配的单位,其可以用多个线程。

线程(Thread)是CPU调度和执行的单位,是独立的执行路径。

程序运行时,及时没有自己创建线程,后台也会有多个线程,如主线程,gc线程。

线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能被人为干预的。

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

线程会带来额外的开销,如cpu调度实现、并发控制开销。

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

 

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

 

1.1 静态代理

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

代理对象要代理真实角色

 

优点:代理对象可以实现真实对象做不了的事件

真实对象可以专注做自己的事情

1 new WeddingCompany(new You()).HappyMary;
2 new Thread(new Runnable()).start();

1.2 Lambda表达式

1 new Thread( ()->sout("Hello,my friend.") ).start();

Functional Interface函数式接口:任何接口,如果只包含为一个抽象方法,name它就是函数式接口。

 1 //Lambda表达式简化
 2 ILove love = null;
 3 love = (int a,int b)->{
 4     System.out.println("i love you"+a);
 5 };
 6  7 //简化参数类型
 8 love = (a,b)->{
 9     System.out.println("i love you"+a);
10 };
11 12 //简化括号,多参数不可省略
13 love = a->{
14     System.out.println("i love you"+a);
15 };
16 17 //简化花括号,代码有多行时,还是用代码块(花括号包裹)
18 love = a->System.out.println("i love you"+a);

 

2.线程实现*

2.1 创建线程的方式

  1. Thread class*

     1 //创建线程类,继承Thread类
     2 public class ThreadDemo01 extends Thread
     3 {
     4     //重写run()方法
     5     @Override
     6     public void run()
     7     {
     8         //线程执行体
     9         for (int i = 0; i < 20; i++)
    10         {
    11             System.out.println("我在看代码——"+i);
    12         }
    13     }
    14 15     //main主线程
    16     public static void main(String[] args)
    17     {
    18         //创建接口对象
    19         ThreadDemo01 threadDemo01 = new ThreadDemo01();
    20         //调用start方法,开启线程
    21         threadDemo01.start();
    22 23         for (int i = 0; i < 20; i++)
    24         {
    25             System.out.println("我在学习多线程——"+i);
    26         }
    27     }
    28 }
  2. Runnable*

     1 //创建线程类,实现runnable接口
     2 public class ThreadDemo03 implements Runnable
     3 {
     4     //重写run()方法
     5     @Override
     6     public void run()
     7     {
     8         //线程实现体
     9         for (int i = 0; i < 20; i++)
    10         {
    11             System.out.println("我在看代码——"+i);
    12         }
    13     }
    14 15     //main主线程
    16     public static void main(String[] args)
    17     {
    18         //创建runnable接口实现对象
    19         ThreadDemo03 t1 = new ThreadDemo03();
    20 21         //创建线程对象,通过线程对象start()方法开启线程,代理
    22         new Thread(new ThreadDemo03()).start();
    23 24         for (int i = 0; i < 20; i++)
    25         {
    26             System.out.println("我在学习多线程——"+i);
    27         }
    28     }
    29 }
  3. Callable

     1 //创建线程类,实现callble接口,有参数
     2 public class CallableDemo01 implements Callable<Boolean>
     3 {
     4     private String url;
     5     private String name;
     6  7     public CallableDemo01(String url, String name)
     8     {
     9         this.url = url;
    10         this.name = name;
    11     }
    12     
    13     //重写call()方法,有返回值
    14     @Override
    15     public Boolean call() throws Exception
    16     {
    17         //线程执行体
    18         WedDownloader wedDownloader = new WedDownloader();
    19         wedDownloader.download(url,name);
    20 21         System.out.println("下载了名为"+name+"的文件");
    22         return true;
    23     }
    24 25     //main主线程
    26     public static void main(String[] args) throws ExecutionException,InterruptedException
    27     {
    28         //创建callable接口实现对象
    29         CallableDemo01 t1 = new CallableDemo01("https://dss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/logo_white-d0c9fe2af5.png","百度1.jpg");
    30         CallableDemo01 t2 = new CallableDemo01("https://dss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/logo_white-d0c9fe2af5.png","百度2.jpg");
    31         CallableDemo01 t3 = new CallableDemo01("https://dss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/logo_white-d0c9fe2af5.png","百度3.jpg");
    32 33         //创建执行服务
    34         ExecutorService es = Executors.newFixedThreadPool(3);
    35 36         //提交执行
    37         Future<Boolean> r1 = es.submit(t1);
    38         Future<Boolean> r2 = es.submit(t2);
    39         Future<Boolean> r3 = es.submit(t3);
    40 41         //获取结果
    42         boolean rs1 = r1.get();
    43         boolean rs2 = r2.get();
    44         boolean rs3 = r3.get();
    45 46         //关闭服务
    47         es.shutdownNow();
    48     }
    49 }

 

3.线程状态

  1. 创建状态

    Thread t = new Thread();

    线程对象一旦被创建就进入到新生状态

  2. 就绪状态

    t.start();

    调用start()方法后线程立即进入就绪状态,但不以为这立即调度执行

  3. 阻塞状态

    当调用sleep()、wait()或同步锁定时,线程进入阻塞状态,代码不往下执行,直到阻塞事件结束后,重新进入就绪状态,等待cpu调度执行

  4. 运行状态

    进入运行状态的线程,才真正执行线程体的代码块

  5. 死亡状态

    线程中断或结束,一旦进入死亡状态,就不能再次启动

     

3.1线程方法

  1. setPriority(int newPriority) 更改线程优先级

  2. static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠

  3. void join() 等待改线程终止

  4. static void yield() 暂停当前正在执行的线程对象,并执行其他线程

  5. void interrupt() 中断线程(别用这个方式)

  6. boolean is isAlive() 测试线程是否处于活跃状态

 

3.2 线程停止方法

建议线程自然停止

建议使用标记

不建议使用stop和destroy以及Java不推荐使用的方法

 

3.3 线程休眠

1 Thread.sleep(1000);

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

可以模拟网络延时:放大问题的发生性

 

3.4 线程礼让

让当前正在执行的线程暂停,但不阻塞

线程从运行状态转为就绪状态

让cpu重新调度,所以礼让不一定成功

 

3.5 Join

Join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞(插队)

 

3.6 线程状态观察

 1 public class CheckStateDemo
 2 {
 3  4     public static void main(String[] args)
 5     {
 6         Thread thread = new Thread(()->
 7         {
 8             for (int i = 0; i < 5; i++)
 9             {
10                 try
11                 {
12                     Thread.sleep(1000);
13                 }
14                 catch (InterruptedException e)
15                 {
16                     e.printStackTrace();
17                 }
18             }
19             System.out.println("执行体运行结束.");
20         });
21 22         //观察状态
23         Thread.State state = thread.getState();
24         System.out.println(state);
25 26         //开启
27         thread.start();
28         state = thread.getState();
29         System.out.println(state);
30 31         //只要线程不终止,就一直观察线程状态
32         while(state != Thread.State.TERMINATED)
33         {
34             try
35             {
36                 Thread.sleep(100);
37             }
38             catch (InterruptedException e)
39             {
40                 e.printStackTrace();
41             }
42             state = thread.getState();
43             System.out.println(state);
44         }
45     }
46 }

3.7 线程优先级

Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行。

1 #优先级用数字表示1~10
2 Thread.MIN_PRIORITY = 1;
3 Thread.MAX_PRIORITY = 10;
4 Thread.NORM_PRIORITY = 5;
5 //获取和改变优先级
6 getPriority();
7 setPriority(int XXX);

3.8 守护线程

线程分为洪湖线程和守护线程

虚拟机必须确保用户线程执行完毕

虚拟机不用等待守护线程执行完毕

如:后台记录操作日志,监控内存,垃圾回收等待......

1 Thread thread = new Thread(god);
2 thread.setDeamon(true);
3 //用户线程结束后,守护线程也会结束

4.线程同步synchronized*

多个线程操作同一个资源(抢票)

并发:同一个对象被多个线程同时操作

4.1 队列和锁

由于同一进程的多个线程共享同一块存储空间,在带来方便的同时也带来了访问冲突的问题。

当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可再次访问。

 

4.2 多线程产生的问题

抢票、同时取钱、线程增加数组内容

 

4.3 同步方法

4.3.1 产生的弊端

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

  2. 在多线程竞争下,枷锁、释放锁会导致较多的上下文切换和调度延时,引起性能下降问题

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

4.3.2 锁的实现

实现方法的修饰符+synchronized

 

同步块 synchronized(obj){}(锁对象)

默认锁的内容是this

锁的对象是变化的(增删改)量

抢票 锁票

取钱 锁钱

增加数组 锁数组

 

4.4 JUC并发包

1 CopyOnWriteArrayList<String> list= new CopyOnWriteArrayList<String>();
2 for (int i = 0; i < 10000; i++)
3 {
4     new Thread(()->
5     {
6         list.add(Thread.currentThread().getName());
7     }).start();
8 }

 

4.5 死锁

多个线程个子战友一些共享的资源,并且相互等待其他线程的资源才能运行,而且倒置两个或者多个线程都在等待对方释放资源,都停止执行的情形。

某一个同步块同时拥有两个以上对象的锁时,就可能会发生。

 

5.线程通信

6.高级主题

 

猜你喜欢

转载自www.cnblogs.com/kevinsblog/p/12975871.html