JAVA多线程

一.认识线程

进程:操作系统资源分配(CPU和内存)的基本单位,不同进程之间互相隔离,内存空间独立,不共享;

线程:os任务调度的基本单位,线程存在于进程之内,每一个进程都至少存在一个线程(主线程,),其他的子线程和主线程共享进程资源;

线程和进程的区别

1.进程时os资源分配的基本单位,线程时os系统调度的基本单位

2.创建进程和销毁进程的开销远比创建和销毁线程大得多,线程更加轻量化

3.调度一个线程也比调度一个进程快

4.进程包含线程,一个进程最少包含一个线程(主线程)

5.进程之间彼此独立,不同的进程不会共享内存空间,同一个进程的线程共享内存空间

二.线程的创建

1.继承Thread类,覆写run方法

public class MyThread extends Thread {
    @Override
    public void run(){
        System.out.println("继承Thread类创建线程");
    }

    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        t1.start();
    }
}

2.覆写Runnable接口

//这个实现了Runnable接口的子类,并不是直接的线程对象,只是一个线程的核心工作任务
// 线程的任务和线程实体的关系
public class RunnableMethod implements Runnable{
    @Override
    public void run() {
        System.out.println("实现Runnable接口创建线程");
    }
}

3.覆写Callablej接口,覆写run方法

4.使用线程池创建线程

三.Thread类及常见方法

1.构造方法

方法说明
Thread()创建线程对象
Thread(Runnable target)使用Runnable对象创建线程
Thread(String name)创建对象并命名
Thread(Runnable target,String name)Runnable对象创建线程并命名

2.Thread类的常见属性

属性获取方法
IDgetID()
名称getName()
状态

getState()

优先级getPriority()
是否后台线程isDaemon()
是否存活isAlive()
是否被中断isInterrupted()

ID 是线程的唯一标识,不同线程不会重复 

关于后台线程,需要记住一点:JVM会在一个进程的所有非后台线程结束后,才会结束运行。

是否存活,即简单的理解,为 run 方法是否运行结束了。

3.启动线程的方法——start方法

start方法与run方法的区别

作用功能不同:

  1. run方法的作用是描述线程具体要执行的任务;
  2. start方法的作用是真正的去申请系统线程

运行结果不同:

  1. run方法是一个类中的普通方法,主动调用和调用普通方法一样,会顺序执行一次;
  2. start调用方法后, start方法内部会调用Java 本地方法(封装了对系统底层的调用)真正的启动线程,并执行run方法中的代码,run 方法执行完成后线程进入销毁阶段。

多次调用

4.中断线程的方法

1.通过共享变量进行中断

65fb89e160034edc9b1909f6163d309b.png

 2.使用Thread.interrupted()静态方法或Thread.currentThread().isInterrupted() 代替自定义标记位

 1)Thread.interrupted()

public class ThreadDemo1 {
    private static class MyThread implements Runnable{
        @Override
        public void run() {
            while (!Thread.interrupted()){
                System.out.println(Thread.currentThread().getName()+"别管我,我要转账");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    System.err.println(Thread.currentThread().getName()+"有内鬼,终止交易");
                    break;
                }
            }
            System.out.println(Thread.currentThread().getName()+"差点上当了");
        }
    }
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        Thread t1 = new Thread(myThread);
        System.out.println(Thread.currentThread().getName()+"让他转账");
        t1.start();
        Thread.sleep(2000);
        System.out.println(Thread.currentThread().getName()+"它是一个骗子");
        t1.interrupt();
    }
}

输出结果为:

58f9b43a00a648c6b20da273627d9e2d.png

2). Thread.currentThread().isInterrupted()

private static class MyThread implements Runnable{
        @Override
        public void run() {
           while (!Thread.currentThread().isInterrupted()){
                System.out.println(Thread.currentThread().getName()+"别管我,我要转账");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    System.err.println(Thread.currentThread().getName()+"有内鬼,终止交易");
                    break;
                }
            }
            System.out.println(Thread.currentThread().getName()+"差点上当了");
        }
    }
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        Thread t1 = new Thread(myThread);
        System.out.println(Thread.currentThread().getName()+"让他转账");
        t1.start();
        Thread.sleep(2000);
        System.out.println(Thread.currentThread().getName()+"它是一个骗子");
        t1.interrupt();
    }

 输出结果为:

58f9b43a00a648c6b20da273627d9e2d.png

*线程收到内置的中断通知有两种方式;

1.当线程调用sleep()/wait()/join()等方法处在阻塞状态时,收到中断通知thread.interrupt(),收到之后会抛出一个中断异常InterruptedException,抛出异常以后线程的中断状态会被清除。

2.线程没有调用以上三种方法时,处在正常运行状态,收到中断通知thread.interrupt();                1).Thread.interrupted():判断当前线程是否中断,若中断状态为true,清除中断标志。

326d53f02e044bb99b0a32abf88f85cd.png

         2).Thread.currentThread.isInterrupted() :判断指定线程对象是否为中断状态,若状态为true,不会清除中断标志。 

38e4be2ac26440f5beb156daec1d2668.png

 5.等待线程的方法——join()

有时,我们需要等待一个线程完成它的工作后,才能进行自己的下一步工作。例如,张三只有等李四转账成功,才决定是否存钱,这时我们需要一个方法明确等待线程的结束。

public class ThreadDemo {
  public static void main(String[] args) throws InterruptedException {
    Runnable target = () -> {
      for (int i = 0; i < 10; i++) {
        try {
          System.out.println(Thread.currentThread().getName()
                   + ": 我还在工作!");
          Thread.sleep(1000);
       } catch (InterruptedException e) {
          e.printStackTrace();
       }
     }
      System.out.println(Thread.currentThread().getName() + ": 我结束了!");
   };
    Thread thread1 = new Thread(target, "李四");
    Thread thread2 = new Thread(target, "王五");
    System.out.println("先让李四开始工作");
    thread1.start();
    thread1.join();
    System.out.println("李四工作结束了,让王五开始工作");
    thread2.start();
    thread2.join();
    System.out.println("王五工作结束了");
 }
}
方法说明
public void join()等待线程结束
public void join(long millis)等待线程结束,最多等待millis毫秒
public void join(long millis ,int nanos)同理,但可以更高精度

6.休眠当前线程——sleep()

Thread.sleep(long millis):在那个线程调用,休眠那个线程

三.线程的状态

9dd0a6fb30844e8494fedbfab6caec33.png

 NEW:安排了工作,还未开始行动

RUNNABLE:可工作的,又可以分成正在工作的和即将开始工作的

BLOCKED、WAITING、TIME_WAITING:这几个都表示排队等着其他事情,都属于线程的阻塞状态,但造成暂缓执行的原因不同

WAITING:等待被另一个线程唤醒

TIMED_WAITING:超时等待,需要等待一段时间后自动唤醒

BLOCKED:锁等待,需要等待其他线程释放锁对象

TERMINATED:工作完成了

四.线程安全

        如果多线程环境下代码运行的结果是符合我们预期的,即在单线程环境应该的结果,则说这个程序是线程安全的。

观察多线程带来的线程安全问题:

static class Counter {
  public int count = 0;
  void increase() {
    count++;
 }
}
public static void main(String[] args) throws InterruptedException {
  final Counter counter = new Counter();
  Thread t1 = new Thread(() -> {
    for (int i = 0; i < 50000; i++) {
      counter.increase();
   }
 });
  Thread t2 = new Thread(() -> {
    for (int i = 0; i < 50000; i++) {
      counter.increase();
   }
 });
  t1.start();
  t2.start();
  t1.join();
  t2.join();
  System.out.println(counter.count);
}

运行多次如上代码都没有得到我们预期的结果

counter.count 是t1和t2线程中都会访问的共享数据

所以要保证原子性,可见性,防止指令重排,

原子性: 该操作对应CPU的一条指令,这个操作不会被中断,要么全部执行,要么都不执行,不会存在中间状态

可见性:一个线程对共享变量的修改,能够及时的被其他线程看到,这种特性被称为可见性

 

解决线程安全问题,就是保证可见性和原子性。

5. synchronized 关键字-监视器锁monitor lock

使用synchronized解决线程的安全问题

synchronized关键字的特性

1.互斥

        synchronized 会起到互斥效果, 某个线程执行到某个对象的 synchronized 中时, 其他线程如果也执行到同一个对象 synchronized 就会阻塞等待

        进入synchronized修饰的代码块,就相当于加锁;

        退出synchronized修饰的代码块,就相当于解锁;

2.刷新内存

synchronized的工作流程

1.获得互斥锁

2.从主内存拷贝变量的最新副本到工作的内存

3.执行代码

4.将更改后的共享变量的值刷新到主内存

5.释放互斥锁

所以synchronized也能保证内存的可见性

3.可重入

synchronized同步块对同一条线程来说是可重入的,不会出现将自己锁死的情况

 

 

 

 

 

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值