什么是线程?

进程:是程序执行一次的过程或正在执行的程序,进程有自己的生命周期:产生-执行-死亡;
线程:一个进程在执行的过程中没有多个线程,它是进程中的一条路径。

内存模型

什么是内存模型:
总结:java内存模型简称JMM,定义一个线程对另一个线程可见。共享变量存放在主内存当中,每个线程都有自己的本地内存,当多个线程同时访问同一个变量时,可能本地内存没有及时的刷新到主内存,所以就产生了线程安全问题。

多线程使用的API

//获取当前线程的名字

Thread.currentThread().getName()

1.start():1.启动当前线程2.调用线程中的run方法

2.run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中

3.currentThread():静态方法,返回执行当前代码的线程

4.getName():获取当前线程的名字

5.setName():设置当前线程的名字

6.yield():主动释放当前线程的执行权

7.join():在线程中插入执行另一个线程,该线程被阻塞,直到插入执行的线程完全执行完毕以后,该线程才继续执行下去

8.stop():过时方法。当执行此方法时,强制结束当前线程。

9.sleep(long millitime):线程休眠一段时间

10.isAlive():判断当前线程是否存活

多线程实现的方式

第一种方式:

//实现Runnable接口

public class MyThread implements Runnable {
  public void run() {
   System.out.println("MyThread.run()");
  }
}

启动

MyThread myThread = new MyThread();

Thread thread1 = new Thread(myThread);
Thread thread2 = new Thread(myThread);

thread1.start();
thread2.start();

第二种方式:

//继承Thread方法
public class MyThread extends Thread{
  public vlid run(){
    System.out.println("MyThread.run()");
  }
}

启动

MyThread myThread = new MyThread();
myThread.start();

第三种方式:
使用线程池创建线程


class NumberThread1 implements Runnable{
    @Override
    public void run() {
        try {
            //睡眠
            sleep(3000);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i=0;i<100;i++){
            if (i%2==0){
                System.out.println(Thread.currentThread().getName() +":"+i);
            }
        }
    }
}class NumberThread2 implements Runnable{
    @Override
    public void run() {
        for (int i=0;i<100;i++){
            if (i%2==1){
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }

    }
}
public class NumberThread {

    public static void main(String[] args) {
        //创建固定线程10个的线程池
        ExecutorService executorService= Executors.newFixedThreadPool(10);

        NumberThread1 numberThread1=new NumberThread1();
        NumberThread2 numberThread2=new NumberThread2();
        //调用
        executorService.execute(numberThread1);
        executorService.execute(numberThread2);

        //关闭线程
        executorService.shutdown();


    }
}

线程的生命周期

新建

当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态

就绪

处于新建状态的线程被start后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件,只是没分配到CPU资源

运行

当就绪的线程被调度并获得CPU资源时,便进入运行状态,run方法定义了线程的操作和功能

阻塞

在某种特殊情况下,被人为挂起或执行输入输出操作时,让出CPU并临时终止自己的执行,进入阻塞状态。

死亡

线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束!

在这里插入图片描述

sleep()与wait()方法区别:

二者之间最大的区别就是实现线程阻塞的方式不同。

二者“是否释放同步锁”不一样。在多线程开发中,为了实现不同线程间的同步会采用同步锁的方式,为线程使用的资源加锁。

而使用sleep()和wait()两种方法对于“CPU执行权”和“同步锁”的方式不同:

①sleep()释放CPU执行权,但不释放同步锁;

②wait()释放CPU执行权,也释放同步锁,使得其他线程可以使用同步控制块或者方法。

以上,就是sleep()和wait()方法的两个关键性区别。

总结:综上我们利用下表展示sleep()和wait()的所有区别:

sleep()和wait()的区别
所属的类不同1、sleep()是Thread类的方法;2、wait()是Object类的方法;
时间不同1、sleep()必须指定时间;2、wait()可以指定时间也可以不指定时间
释放锁不同1、sleep()释放CPU执行权不释放同步锁;2、wait()释放CPU执行权也释放同步锁
使用的地方不同1、sleep()可以在任意地方使用;2、wait()只能在同步代码方法或同步代码块使用
捕获异常不同1、sleep()必须捕获异常;2、wait是Object方法,调用需捕获/抛出异常

锁的机制:synchronized实现同步机制

synchronized关键字:Java语言的关键字,可用来给对象和方法或者代码块加锁,当它锁定一个方法或者一个代码块的时候,同一时刻最多只有一个线程执行这段代码。当两个并发线程访问同一个对象object中的这个加锁同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。它包括两种用法:synchronized 方法和 synchronized 块。

即实现线程间同步的方式有两种:

①使用synchronized同步代码块;

②使用synchronized关键字创建synchronized()方法

下面分别进行解析,对上面售票的代码进行改造:

①代码——使用synchronized同步代码块


/**
 *
 * 创建三个窗口卖票,总票数为100张,使用继承自Thread方式
 * 用静态变量保证三个线程的数据独一份
 *
 * 存在线程的安全问题,有待解决
 *
 * */

public class ThreadDemo {


    public static void main(String[] args){
        window  w=new window();

        Thread t1=new Thread(w);
        Thread t2=new Thread(w);
        Thread t3  =new Thread(w);

        t1.setName("售票口1");
        t2.setName("售票口2");
        t3.setName("售票口3");


        t1.start();
        t2.start();
        t3.start();

    }

}

class window implements Runnable{
    private  int ticket = 100; //将其加载在类的静态区,所有线程共享该静态变量
    //实例化锁
    private ReentrantLock lock=new ReentrantLock();

    @Override
    public void run() {
        while(true) {
            //调用锁定方法synchroonized
            synchronized (this) {
                if (ticket > 0) {
     
                    System.out.println(Thread.currentThread().getName() + "当前售出第" + ticket + "张票");
                    ticket--;
                } else {
                    break;
                }
            }
        }
    }
}

②代码——使用synchronized关键字创建synchronized()方法

class TicketThreadMethod implements Runnable {
    private int ticket = 5;

    public void run(){

        for (int i = 0; i < 5; i++){

            this.sale();

        }

    }

    public synchronized void sale(){

            if (ticket > 0){

                try {
        	 Thread.sleep(300);

                } catch (InterruptedException e) {

                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "卖票:ticket = " + ticket--);

            }

    }

}

synchronized方法和synchronized同步代码块的区别:

  • synchronized同步代码块只是锁定了该代码块,代码块外面的代码还是可以被访问的。
  • synchronized方法是粗粒度的并发控制,某一个时刻只能有一个线程执行该synchronized方法。
  • synchronized同步代码块是细粒度的并发控制,只会将块中的代码同步,代码块之外的代码可以被其他线程同时访问。
Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐