线程

线程

进程与线程

1、什么是进程

进程:启动一个QQ.exe就叫一个进程。 接着又启动一个360.exe,这叫两个进程。以此类推,每个独立执行的程序都称为进程。

2、什么是线程

线程:线程是在进程内部同时做的事情,比如在QQ里,有很多事情要同时做,比如发送消息和接受消息,同时上传文件,这就是由多线程

一、创建的四种方式

1、继承Thread方法
public class ThreadextendThreade {

    public static void main(String[] args) {
        //实现第一种方式
        One one = new One();
        Thread thread = new Thread(one, "我是第一个");
        Thread thread1 = new Thread(one, "我是第二个");
        Thread thread2 = new Thread(one, "我是第三个");
        thread.start();
        thread1.start();
        thread2.start();
    }
}
//1、第一种方法:继承Thread
class One extends Thread{
    //加上同步关键字
    @Override
    public synchronized void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("我是继承Thread创建的线程"+Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
2、实现Runnable接口
public class ThreadextendThreade {

    public static void main(String[] args) {
        //第二种实现方式
        Two two = new Two();
        Thread thread = new Thread(two);
        thread.start();
    }
}
//2、第二种方式:实现Runnable接口
class Two implements Runnable{
    @Override
    public void run() {
        while(true){
            System.out.println("我是实现Runnable的线程"+Thread.currentThread().getName());
        }
    }
}
3、实现Callable接口
public class ThreadextendThreade {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //第三种实现方式
        Three three = new Three();
        FutureTask<Integer> futureTask=new FutureTask<Integer>(three);
        Thread thread=new Thread(futureTask);
        thread.start();
        //获取返回值
        Integer integer = futureTask.get();
        System.out.println(integer);

    }
}
//3、第三种方式:实现Callable接口
class Three implements Callable<Integer>{

    @Override
    public Integer call() throws Exception {
        while (true){
            System.out.println("我是实现Callable接口的线程"+Thread.currentThread().getName());
            return 1;
        }
    }
}
4、线程池

主要有newFixedThreadPool,newCachedThreadPool,newSingleThreadExecutor,newScheduledThreadPool

public class ThreadextendThreade {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //第四种方式实现
        Four four = new Four();
        ExecutorService service= Executors.newSingleThreadExecutor();
        for (int i = 0; i < 5; i++) {
            service.execute(four);
        }
        service.shutdown();
    }
}

//4、第四种方式:使用线程池
class Four implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("run……"+Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

二、线程的同步

1、synchronized
  • 锁方法,类,都是锁定的this对象
  • 锁对象中的属性(适用于多线程下会被修改的数据)
  • synchronized非函数签名,因此无法被继承,所以无法保证子类调用同步.
  • image-20200708110208032
###
synchronized:java关键字,具有锁

###
  • t1和t2共享线程对象Account对象 Account中有个属性为Object obj (只要是唯一对象。就可以锁住)
  • 局部变量就不行
  • image-20200708114856688
  • java三大变量:image-20200708132724953
  • 存在方法区和堆区的对象。只有一个,并且是唯一的
  • 常量不会有线程安全问题,因为它不可修改
  • 使用局部变量操作字符串的时候是选择StringBuffer还是StringBuilder:答案是:StringBuider,局部变量没有线程安全。
  • image-20200708135535287
  • 让t1线程先执行方式:
  • image-20200708135759117
  • 以上方式叫做排它锁 还有个叫互斥锁
2、Lock
public class 同步 {
    public static void main(String[] args) {
        TestLock testLock=new TestLock();
        new Thread(testLock).start();
        new Thread(testLock).start();
        new Thread(testLock).start();
    }
}

class TestLock implements Runnable{
    private int num=10;
    private ReentrantLock reentrantLock=new ReentrantLock(); //new
    @Override
    public void run() {
        while (true){
            try {
                reentrantLock.lock();//加锁
                if (num<=0)break;
                System.out.println(num--);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }finally {
                reentrantLock.unlock();//解锁
            }
        }
    }
}
总结:

image-20200704225120517

三、lamda表达式

1、什么是lamda表达式

https://www.runoob.com/java/java8-lambda-expressions.html

3、进程与线程的区别
/*
进程是资源分配最小单位,线程是程序执行的最小单位;

进程有自己独立的地址空间,每启动一个进程,系统都会为其分配地址空间,建立数据表来维护代码段、堆栈段和数据段,线程没有独立的地址空间,它使用相同的地址空间共享数据;
线程:堆和方法区地址空间共享,栈内存空间独立,一个线程一个栈


CPU切换一个线程比切换进程花费小;

创建一个线程比进程开销小;

线程占用的资源要⽐进程少很多。

线程之间通信更方便,同一个进程下,线程共享全局变量,静态变量等数据,进程之间的通信需要以通信的方式(IPC)进行;(但多线程程序处理好同步与互斥是个难点)

多进程程序更安全,生命力更强,一个进程死掉不会对另一个进程造成影响(源于有独立的地址空间),多线程程序更不易维护,一个线程死掉,整个进程就死掉了(因为共享地址空间);

进程对资源保护要求高,开销大,效率相对较低,线程资源保护要求不高,但开销小,效率高,可频繁切换;
*/
  • 运行mian方法,最少开启两个线程:主线程和gc回收线程
  • 所以当主线程结束了,那么主线程栈空了,然后其他栈并没有结束
  • start()方法的作用:启动一个分支线程,在JVM中开辟一个新的栈空间。然后该线程自动启动run方法。让线程从新建状态转化为就绪状态 。run方法在新栈的底部。main方法也在主线程栈的底部
  • image-20200707230108605
  • image-20200707230617058
  • 直接调用类名.run()方法:该线程是单线程状态
  • 如果是调用start方法时:如下图
  • image-20200707230811960

四、线程对象的生命周期

image-20200707232449539

  • 特点:
    1. 线程运行后会在就绪状态和运行状态来回切换。
    2. 当进程从阻塞状态到就绪状态然后到运行状态时,会接上一次的代码继续运行
    3. 主线程的名字就叫main
    4. 线程拥有setName getName Thread.crrurentName().getName() start sleep notify notifyAll wait yeild join
  1. Thread中有哪些方法
    • sleep方法

      • 作用:执行的时候会转化为静态的方法,会让当前线程睡眠
      1. image-20200708090406303
    • 重写run方法,里面的异常不能抛出(原因是:父类没有抛出异常,所以子类重写不能抛出更多异常)

    • image-20200708091246710

    • 在主方法中调用t.interrupt方法会干扰t的睡眠。直接抛出异常,被t的try捕获,然后执行run方法之后的代码

    • stop方法:强行终止(已过时),缺点:容易丢数据,损坏数据。

    • 合理的终止线程;看源码

    • yield(静态方法)方法:当前线程让出当前时间片。从运行状态回到就绪状态(仍然有可能再次获得时间片)

    • join方法、t.join()合并t线程。使当前main线程受阻。让t运行完毕后main才接着运行

    • 什么时候会产生数据安全性问题

      1. 多线程
      2. 有共享数据
      3. 共享数据有修改的行为
  2. Object中有哪些方法:

    • image-20200704204850853

六、Thread中的几个方法的理解

1.start(开启线程,start是通过线程来调用run方法)

2.run 此run非彼run (不是在run方法实现线程的逻辑,而是thread.run(),这个run方法是直接调用了线程中的run)

3.yield:执行状态(运行状态)变为可执行态(就绪状态)

4.sleep(使当前线程由运行状态变成阻塞状态,若睡眠时其他线程调用了interrupt方法,会导致sleep抛出异常InterruptException)

5.join(会抢占资源)(保证当前线程在其他线程开始时会结束)(如下,A线程想运行的话,必须等B线程结束才能运行(将处于阻塞状态))

Thread A{

run{

new ThreadB.join();

}

}.start;

6.interrupt(中断线程)

7.wait/notify(从Object类继承下来的方法)

image-20200708200652967

8.setPriority(设置线程优先级(只能在线程开始前设置)默认为5 【1,10】

9.stop(强制结束线程)

10、interrupt():中断方法

  • 当线程使用wait()、join()、yield()等方法时会调用它
  • 中断标志位:默认为false。当执行wait()方法后,标志位会变为true
  • 阻塞的线程会不断的检查自己的标志位,

七、死锁

  1. 多个线程对相互资源的抢占。导致都处于等待状态而形成死锁。

八、守护线程

  • 线程分为用户线程守护线程
  • 虚拟机必须确保用户线程执行完毕
  • 虚拟机不用等待守护线程执行完毕
  • 如:后台记录操作日志,监控内存,垃圾回收等待
  • 场景:作为定时任务,每天凌晨进行数据备份。

九、生产者和消费者

生产者:仓库,初始化仓库构造方法,一直生产的方法,实现Runnable接口

消费者:仓库,初始化仓库构造方法,一直消费的方法,实现Runnable接口

仓库:存放产品属性,get,set方法

image-20200708201110948

管程法
package com.xd.线程;

import java.util.ArrayList;
import java.util.List;

/**
 * ClassName:TwoDomn
 * Package:com.xd.线程
 * Description:
 *
 * @Date:2020/07/07 13:31
 * @Author:xd
 */
public class TwoDomn {
    public static void main(String[] args) {
        Contain contain = new Contain();
        new Thread(new Producer(contain)).start();
        new Thread(new Cosumer(contain)).start();

    }

}
//消费者
class Cosumer implements Runnable{
    Contain contain;

    public Cosumer(Contain contain) {
        this.contain = contain;
    }

    @Override
    public void run() {
        for (int i = 1; i < 20; i++) {
            contain.pop();
        }
    }
}
//生产者
class Producer implements Runnable{
    Contain contain;

    public Producer(Contain contain) {
        this.contain = contain;
    }

    @Override
    public void run() {
        for (int i = 1; i < 20; i++) {
            contain.push(new Chicken(i));

        }
    }

}
//产品
class Chicken{
    int id;//产品编号

    public Chicken(int id) {
        this.id = id;
    }
}
//容器
class Contain{
    //容器计数器
    List<Chicken> chickens=new ArrayList<Chicken>(10);
    //放入产品
    public synchronized void push(Chicken chicken){
        //如果容器满了就需要等待消费者消费
        if(chickens.size()==10){
            //通知消费者消费
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //如果容器没满。则生产产品
        chickens.add(chicken);
        System.out.println("生产了第"+chicken.id+"只鸡");
        //唤醒消费者
        this.notifyAll();
    }

    public synchronized void pop(){
        //如果容器没有产品就通知生产者生产
        if(chickens.size()==0){
            //通知生产者生产
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //如果容器里面还有则消费
        Chicken remove = chickens.remove(chickens.size() - 1);
        System.out.println("当前消费的鸡"+remove.id);
        //唤醒生产者
        this.notifyAll();
    }
}

十、解决线程安全,方案选择

image-20200708150433365

十一、定时器

1、java.util.Timer(底层)

2、Spring 中的SpringTask

十二、Callable详解

  • 获取返回值得时候会导致当前线程阻塞
  • image-20200708195424506
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值