Java 线程 —— 基础

前言

当回顾其线程的时候,感觉什么都忘了,在网上查资料的时候,都没有什么好的文章,决定写一系列关于Java线程的文章。

这篇博客特别基础,所以推荐一篇写的比较好文章 —— Java多线程学习(吐血超详细总结)

线程和进程概述

  • 进程:计算机中特定功能的程序在数据集上的一次运行。
  • 线程:线程是进程的一个单元。
  • 多线程:一个进程中有多个线程在同时运行,如迅雷下载,迅雷软件的一次运行就是一个进程,那么在迅雷中可以同时下载多个电影,这就是多线程(每一个下载都是一个线程)
  • Jvm是多线程的,在我们运行jvm的时候后台会运行垃圾回收的线程,来清理没有被引用的对象。

举个例子:

进程:启动一个LOL.exe就叫一个进程。 接着又启动一个DOTA.exe,这叫两个进程。
线程:线程是在进程内部同时做的事情,比如在LOL里,有很多事情要同时做,比如"盖伦” 击杀“提莫”,同时“赏金猎人”又在击杀“盲僧”,这就是由多线程来实现的。


常用创建线程的两种方式

一种是继续Thread类,另外一种是实现Runable接口.(其实准确来讲,应该有三种,还有一种是实现Callable接口,并与Future、线程池结合使用,此文这里不讲这个,有兴趣看这里Java并发编程与技术内幕:Callable、Future、FutureTask、CompletionService 、BF360C )

使用Thread创建线程的步骤:

  1. 自定义一个类,继承java.lang包下的Thread类
  2. 重写run方法
  3. 将要在线程中执行的代码编写在run方法中
  4. 创建上面自定义类的对象
  5. 调用start方法启动线程
1. 继承Thread类
//1.自定义一个类,继承java.lang包下的Thread类
public class MyThreed extends Thread {
    //通过构造函数为线程起名字
    private String name;
    public MyThreed(String name) {
        this.name = name;
    }

    //2.重写run方法
    @Override
    public void run() {
    	//3.将要在线程中执行的代码编写在run方法中
        for (int i = 0; i <=50 ; i++) {
            System.out.println(name+"跑了"+i+"米");
        }
    }
}

测试

public static void main(String[] args) {
 		//4.创建上面自定义类的对象
        MyThreed myThreed=new MyThreed("懒洋洋");
        myThreed.start();
        MyThreed myThreed1=new MyThreed("灰太狼");
        //5.调用start方法启动线程
        myThreed1.start();
        System.out.println("方法结束");
    }
2. 实现Runnable接口创建线程

使用Runnable创建线程步骤:

  1. 自定义一个类实现java.lang包下的Runnable接口
  2. 重写run方法
  3. 将要在线程中执行的代码编写在run方法中
  4. 创建自定义类的对象
  5. 创建Thread对象并将上面自定义类的对象作为参数传递给Thread的构造方法
  6. 调用start方法启动线程
// 1.自定义一个类实现java.lang包下的Runnable接口
public class MyRunable implements Runnable {
    private String name;

    public MyRunable(String name) {
        this.name = name;
    }

	 // 2.重写run方法
    @Override
    public void run() {
    	 // 3.将要在线程中执行的代码编写在run方法中
        for (int i = 0; i <=50 ; i++) {
            System.out.println(name+"跑了"+i+"米");
        }
    }


	//测试
	 public static void main(String[] args) {
 		// 4.创建Thread对象并将上面自定义类的对象作为参数传递给Thread的构造方法
        Thread thread = new Thread(new MyRunable("喜洋洋进度"));
        Thread thread1 = new Thread(new MyRunable("灰太狼进度"));
        //6.调用start方法启动线程
        thread.start();
        thread1.start();
    }

多线程创建的二种方式对比

继承Thread

  • 优点:可以直接使用Thread类中的方法,代码简单
  • 缺点:继承Thread类之后就不能继承其他的类

实现Runnable接口

  • 优点:即时自定义类已经有父类了也不受影响,因为可以实现多个接口
  • 缺点: 在run方法内部需要获取到当前线程的Thread对象后才能使用Thread中的方法

线程的声明周期

线程是一个动态执行的过程,它也有一个从产生到销毁的过程。

下图显示了一个线程完整的生命周期。
Java 线程 —— 基础_第1张图片

  1. 新建: 线程被new出来
  2. 准备就绪:线程具有执行的资格,即线程调用了start(),没有执行的权利
  3. 运行:具备执行的资格和具备执行的权利
  4. 阻塞:没有执行的资格和执行权利
  5. 销毁: 线程的对象变成垃圾,释放资源。

线程的并发

我们举个例子,让我们更加懂得并发。

现有一家电影院,现哪吒之魔童降世的电影票共有一百张,共有三个窗口卖票,程序怎么实现?

Java 线程 —— 基础_第2张图片

public class MoviceThreed extends Thread {
    //为线程起别名
    private String name;
    //共工100张静态变量的票
    static int tickets = 100;
    //创建一个锁对象,这个对象是多个线程对象共享的数据
    static Object obj = new Object();


    public MoviceThreed(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        //卖票是持续的
        while (true) {
            synchronized (obj) {
                if (tickets > 0) {
                    System.out.println(name + "卖出座位是" + (tickets--) + "号");
                } else {
                    break;
                }
            }
        }
        System.out.println(name+"卖票结束");
    }
}

public class MoviceTest {

    public static void main(String[] args) {
        MoviceThreed m1 = new MoviceThreed("窗口1");
        MoviceThreed m2 = new MoviceThreed("窗口2");
        MoviceThreed m3 = new MoviceThreed("窗口3");
        m1.start();
        m2.start();
        m3.start();
    }
}

如果我么用传统的方式的话会发生并发行为,可能1号窗口,2号窗口卖出同一张票,这样是不对的所以我们上面代码用到 synchronized

  • 语法:

    synchronized(锁对象){
    //操作共享资源的代码
    }
    
同步代码加在什么地方?
  1. 代码被多个线程访问
  2. 代码中有共享的数据
  3. 共享数据被多条语句操作。

你可能感兴趣的:(Java 线程 —— 基础)