初探Java之多线程

1、基本概念

1.1进程与线程

一个应用程序(软件)一般由1个或多个进程运行
1个进程又是由多个线程来进行分配任务处理

当多个线程抢占资源来完成任务时,这就涉及到线程调度问题了。

1.2 同步与异步

同步:多个线程排队执行,一次只能执行一个线程,synchronized关键字修饰。
异步:多个线程抢占式同时进行,一次执行多个线程。

1.3 并发与并行

并发:一个时间段内,多个线程同时进行
并行:同一个时刻内,多个线程同时进行

2.Java中的线程调度

2.1.抢占式调度:

指的是每条线程执行的时间、线程的切换都由系统控制。系统控制指的是在系统某种运行机制下,可能每条线程都分同样的执行时间片,也可能是某些线程执行的时间片较长,甚至某些线程得不到执行的时间片。在这种机制下,一个线程的堵塞不会导致整个进程堵塞。

2.2.协同式调度:

指某一线程执行完后主动通知系统切换到另一线程上执行。线程的执行时间由线程本身控制,线程切换可以预知,不存在多线程同步问题,但它有一个致命缺点:如果一个线程编写有问题,运行到一半就一直阻塞,那么可能导致整个系统崩溃。

2.3.JVM的线程调度的实现(抢占式调度):

Java使用的线程调度使用抢占式调度,Java中线程会按优先级分配CPU时间片运行,且优先级越高越优先执行,但优先级高并不代表能独自占用执行时间片,可能是优先级高得到越多的执行时间片,反之,优先级低的分到的执行时间少但不会分配不到执行时间。

2.4.线程让出CPU的情况:

1)当前运行线程主动放弃CPU,JVM暂时放弃CPU操作(基于时间片轮转调度的JVM操作系统不会让线程永久放弃CPU,或者说放弃本次时间片的执行权),例如调用yield方法。

2)当前运行线程因为某些原因进入阻塞状态,例如阻塞在IO上。

3)当前运行线程结束,即运行完run方法里面的任务。


3.Java中的三种多线程实现方式

3.1 第一种:继承Thead
public class Demo1 {
    public static void main(String[] args) {
    MyDemo1 myDemo1 = new MyDemo1();
    myDemo1.start();
        System.out.println(Thread.currentThread().getName() + " :     11111111111");;
    }
    
}
class MyDemo1 extends Thread {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " : 22222222222");;
    }
}
3.2 第二种:实现Runnable
public class Demo2 {
    public static void main(String[] args) {
        Thread t = new Thread(new MyDemo2());
        t.start();
        System.out.println(Thread.currentThread().getName() + " :     11111111111");
    }
}
class MyDemo2 implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " : 22222222222");;
    }
}
3.3 实现Runnable与继承Thread两种方式的比较
实现Runnable与继承Thread相比有如下优势
1.通过创建任务,然后给线程分配任务的方式实现多线程,更适合多个线程同时执行任务的情况
2,可以避免单继承所带来的局限性
3,任务与线程是分离的,提高了程序的健壮性
4,后期学习的线程池技术,接受Runnable类型的任务,不接受Thread类型的线程
Thread.currentThread().getName()  获取当前线程名称
Thread.currentThread().setName()  设置当前线程名称

3.4 第三种:带返回值的Callable接口

Callable接口类似于Runnable ,因为它们都是为其实例可能由另一个线程执行的类而设计的。 但是, Runnable不会返回结果,也不能抛出已检查的异常。

该Executors类包含的实用方法,从其他普通形式转换为Callable类。

方法描述
call()计算结果,如果无法执行,则抛出异常

使用步骤

 - 编写类实现Callable接口 , 实现call方法 
 	class XXX implements Callable<T> {
 		@Override 
 	    public <T> call() throws Exception { 
		 	return T; } } 
 - 创建FutureTask对象 , 并传入第一步编写的Callable类对象 
 	FutureTask<Integer> future = new FutureTask<>(callable); 
 - 通过Thread,启动线程 
	new Thread(future).start();

Runnable 与 Callable的相同点

  • 都是接口
  • 都可以编写多线程程序
  • 都采用Thread.start()启动线程

Runnable 与 Callable的不同点

  • Runnable没有返回值;
  • Callable可以返回执行结果
  • Callable接口的call()允许抛出异常;
  • Runnable的run()不能抛出

4.Thead类的常用方法

构造方法描述
Thread()分配新的 Thread对象
Thread​(Runnable target)分配新的 Thread对象
Thread​(Runnable target, String name)分配新的 Thread对象
常用字段描述
int MAX_PRIORITY线程可以拥有的最大优先级
int MIN_PRIORITY线程可以拥有的最低优先级
int NORM_PRIORITY分配给线程的默认优先级
常用方法描述
long getId()返回此Thread的标识符
String getName()线程可以拥有的最低优先级
Thread.State getState()返回此线程的状态
interrupt()中断此线程
setPriority​(int newPriority)更改此线程的优先级
sleep​(long millis, int nanos)线程休眠,指定的毫秒数加上指定的纳秒数
static Thread currentThread()返回对当前正在执行的线程对象的引用

5. 线程死亡

5.1 线程中断

interrupt()方法中断此线程
以前的stop()方法已经弃用,现在让线程死亡的方式:通过控制线程中断,然后抓取中断信号,然后return,自己直接结束。如下例:

public class Demo2 {
  public static void main(String[] args) {
      Thread t = new Thread(new MyDemo2());
      t.start();        
      System.out.println(Thread.currentThread().getName() + " :     11111111111");
      t.interrupt();//中断信号
  }
}
class MyDemo2 implements Runnable{
  @Override
  public void run() {
      Thread.currentThread().setName("123");
      for (int i = 0; i < 10; i++) {
          System.out.println(Thread.currentThread().getName() + " : 22222222222");
          try {
              Thread.sleep(1000);
          } catch (InterruptedException e) {//获取中断信号
              System.out.println("中断标记!自己中断,线程死亡");
              return;//线程自己结束
          }
      }
  }
}

5.2 用户线程&&守护线程

用户线程:当无一个存活的用户线程时,程序结束(主人)
守护线程:当所有用户线程挂了,所有守护线程也就挂了(宠物)

设置守护线程

在线程启动之前,设置setDaemon(true)

		Thread t = new Thread(new MyDemo2());
        t.setDaemon(true);
        t.start();

6.线程安全

6.1同步代码块
private Object o = new Obect();

在重写Run()方法里面加上synchronize(o){} 
控制代码,使得线程同步
6.2 代码方法
方法前面使用synchronize修饰
使得线程每次独自运行一个方法
6.3 显式Lock锁
private Lock l = new ReentrantLock()
l.lock();
6.4 公平锁 && 非公平锁
公平锁:所有线程依着先来后到的抢占资源(公平,先来后到)
非公平锁:所有线程自由抢占资源(不公平,自由争抢,不管先来先到)
6.5 线程死锁

两个或多个线程互相争夺资源,然后由于死锁的缘故导致互相卡死在当前状态,退出不了当前资源,也获取不了新资源

典型的案例:生产者与消费者的案例
package com.java.demo;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Demo4  {

    /**
     * 多线程通信问题, 生产者与消费者问题
     * @param args
     */
    public static void main(String[] args) {
        Food f = new Food();
        new Cook(f).start();
        new Waiter(f).start();
    }

    //厨师
    static class Cook extends Thread{
        private Food f;
        public Cook(Food f) {
            this.f = f;
        }

        @Override
        public void run() {
            for(int i=0;i<100;i++){
                if(i%2==0){
                    f.setNameAndSaste("老干妈小米粥","香辣味");
                }else{
                    f.setNameAndSaste("煎饼果子","甜辣味");
                }
            }
        }
    }
    //服务生
    static class Waiter extends Thread{
        private Food f;
        public Waiter(Food f) {
            this.f = f;
        }
        @Override
        public void run() {
            for(int i=0;i<100;i++){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                f.get();
            }
        }
    }
    //食物
    static class Food{
        private String name;
        private String taste;

        //true 表示可以生产
        private boolean flag = true;

        public synchronized void setNameAndSaste(String name,String taste){
            if(flag) {
                this.name = name;
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                this.taste = taste;
                flag = false;
                this.notifyAll();
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        public synchronized void get(){
            if(!flag) {
                System.out.println("服务员端走的菜的名称是:" + name + ",味道:" + taste);
                flag = true;
                this.notifyAll();
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}


7.Lambda表达式

Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性
Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
使用 Lambda 表达式可以使代码变的更加简洁紧凑。
语法

(参数) -> 表达式
或
(参数) ->{ 表达式; }

lambda表达式的重要特征:

  • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
  • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
  • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
  • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
// 1. 不需要参数,返回值为 5  
() -> 5  
  
// 2. 接收一个参数(数字类型),返回其2倍的值  
x -> 2 * x  
  
// 3. 接受2个参数(数字),并返回他们的差值  
(x, y) -> x – y  
  
// 4. 接收2个int型整数,返回他们的和  
(int x, int y) -> x + y  
  
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)  
(String s) -> System.out.print(s)

8.线程池

8.1 缓存线程池(非定长线程池)
public class CachTheadPoolDemo {
    public static void main(String[] args) {
        //创建缓存线程池
        ExecutorService service = Executors.newCachedThreadPool();
        //添加任务
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("11111");
            }
        });
    }
}
8.2 定长线程池
public class FixedTheadPool {
    public static void main(String[] args) {
        //创建定长线程池
        ExecutorService service = Executors.newFixedThreadPool(2);
        //添加任务
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("11111");
            }
        });
    }
}
8.3 单线程线程池
public class SingleTheadPool {
    public static void main(String[] args) {
        //创建单线程线程池
        ExecutorService service = Executors.newSingleThreadExecutor();
        //添加任务
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("11111");
            }
        });
    }
}
8.2 周期定长线程池
public class SchedledTheadPoolDemo {
    public static void main(String[] args) {
        //创建周期性定长线程池
        ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
        /**
         * 1.定时执行一次
         *     参数1:定时执行的任务
         *     参数2:时长数字
         *     参数3:时长数字的单位,TimeUnit的常数指定
         */
        service.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("1111111");
            }
        },5, TimeUnit.MICROSECONDS)
        service.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("22222222");
            }
        },1,TimeUnit.MINUTES);
    }
}
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值