JAVA中的线程详解

并发与并行

  • 并发:两个或多个事件在同一事件段内发生
  • 并行:两个或多个事件在同一时刻发生(同时发生)
    在这里插入图片描述

线程与进程

  • 进程: 是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创
    建、运行到消亡的过程。
  • 线程: 线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。

简而言之: 一个程序运行后至少有一个进程,一个进程中可以包含多个线程

Thred类

构造方法

  • public Thread() :分配一个新的线程对象。

  • public Thread(String name):分配一个指定名字的新的线程对象。

  • public Thread(Runnable target) :分配一个带有指定目标新的线程对象。

  • public Thread(Runnable target,String name) :分配一个带有指定目标新的线程对象并指定名字。

常用方法

  • public String getName() :获取当前线程名称。
  • public void start() :导致此线程开始执行; Java虚拟机调用此线程的run方法。
  • public void run() :此线程要执行的任务在此处定义代码。
  • public static void sleep(long millis) :使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
  • public static Thread currentThread() :返回对当前正在执行的线程对象的引用。

使用继承Thread类的方式创建线程对象


public class Main {
    public static void main(String[] args){
        //创建线程对象
        MyThread thread= new MyThread("新线程");
        //开启新线程
        thread.start();
        for (int i = 0; i <10; i++) {
            System.out.println("主线程------》"+i);
        }
    }
}

class MyThread extends Thread{

    //定义线程的名称
    public MyThread(String name) {
        super(name);
    }
    //线程的执行方法,当线程启动时执行此方法
    @Override
    public void run() {
        for (int i = 0; i <10; i++) {
            System.out.println(getName()+"------》"+i);
        }
    }
}

使用实现Runnable接口的方式创建线程对象

public class Main {
    public static void main(String[] args){
        //创建线程
       Thread thread=new Thread(new MyRunnable(),"新线程");
       //开启线程
       thread.start();
        for (int i = 0; i <10; i++) {
            System.out.println("主线程------》"+i);
        }
    }
}

class MyRunnable implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i <10; i++) {
            System.out.println(Thread.currentThread().getName()+"----》"+i);
        }
    }
}


实现Runnable接口比继承Thread类所具有的优势:

  1. 适合多个相同的程序代码的线程去共享同一个资源。
  2. 可以避免java中的单继承的局限性。
  3. 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
  4. 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。

线程安全

定义: 如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

案例:

public class Ticket implements Runnable{

    //总共100张票
    private int ticket=100;

    @Override
    public void run() {
        //开启窗口卖票
        while (true){
            //有票可卖
            if(ticket>0){
                //出票操作
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                String name=Thread.currentThread().getName();
                System.out.println(name+"正在卖:"+ticket--);
            }
        }
    }
}
public class Main {
    public static void main(String[] args){
        Ticket ticket = new Ticket();
        //创建三个窗口进行卖票
        Thread thread1=new Thread(ticket,"窗口1");
        Thread thread2=new Thread(ticket,"窗口2");
        Thread thread3=new Thread(ticket,"窗口3");
        //三个窗口开始卖票
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

结果(不唯一):

在这里插入图片描述

通过结果可以看到几个线程中的票数不同步,这种问题就是线程不安全。

线程同步的实现方式

  • 同步代码块
  • 同步方法
  • 锁机制

同步代码块

格式

synchronized(同步锁){
     需要同步操作的代码
}

同步锁:
对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁.

  1. 锁对象可以是任意类型。
  2. 多个线程对象 要使用同一把锁。

案例

public class Ticket implements Runnable{

    //总共100张票
    private int ticket=100;
    //同步锁中的对象
    Object lock=new Object();

    @Override
    public void run() {
        //开启窗口卖票
        while (true){
            //同步代码块
            synchronized (lock){
                //有票可卖
                if(ticket>0){
                    //出票操作
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    String name=Thread.currentThread().getName();
                    System.out.println(name+"正在卖:"+ticket--);
                }
            }
        }
    }
}

同步方法

格式

public synchronized void method(){
   可能会产生线程安全问题的代码 
}

同步方法中的同步锁是谁?
对于非static方法,同步锁就是this。
对于static方法,我们使用当前方法所在类的字节码对象(类名.class)。

案例:

public class Ticket implements Runnable{

    //总共100张票
    private int ticket=100;

    @Override
    public void run() {
       sellTicket();
    }
    public synchronized void sellTicket(){
        //开启窗口卖票
        while (true){
            //有票可卖
            if(ticket>0){
                //出票操作
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                String name=Thread.currentThread().getName();
                System.out.println(name+"正在卖:"+ticket--);
            }
        }
    }
}

Lock锁

常用的方法

  • public void lock() :加同步锁。
  • public void unlock() :释放同步锁

案例:

public class Ticket implements Runnable{

    //总共100张票
    private int ticket=100;

    //创建锁对象
    Lock lock=new ReentrantLock();

    @Override
    public void run() {
        //开启窗口卖票
        while (true){
            //加锁
            lock.lock();
            //有票可卖
            if(ticket>0){
                //出票操作
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                String name=Thread.currentThread().getName();
                System.out.println(name+"正在卖:"+ticket--);
            }
            lock.unlock();
        }
    }
}

线程的状态

线程状态描述
New(新建状态)线程刚被创建,但是并未启动。还没调用start方法。
Runnable(就绪状态)线程创建对象后,其他线程调用start()方法,该线程处于就绪状态,资源已经准备就绪,等待CPU资源。
Running(运行状态):处于就绪状态的线程获取到CPU资源后进入运行状态。
Waiting(无限等待)一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。
Timed Waiting(计时等待)同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep 、Object.wait
Teminated(终止)因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡

等待唤醒机制

常用方法:

  • wait() – 让当前线程处于“等待(阻塞)状态,直到被其他线程唤醒
  • wait(long timeout) – 让当前线程处于“等待(阻塞)状态,直到被其他线程唤醒或等待时间到了
  • notify() – 唤醒在此对象监视器上等待的单个线程。
  • notifyAll() – 唤醒在此对象监视器上等待的所有线程。
public class MyThread extends Thread{

    public MyThread(String name){
        super(name);
    }

    public void run(){
        synchronized(this){
            System.out.println(Thread.currentThread().getName()+"运行了");
            // 唤醒主线程
            notify();
        }
    }
}
public class Main {
    public static void main(String[] args) throws Exception {
        MyThread thread = new MyThread("副线程");

        synchronized (thread) {
            try {

                System.out.println(Thread.currentThread().getName() + " 运行了");
                // 启动副线程
                thread.start();

                System.out.println(Thread.currentThread().getName() + "进入阻塞");
                // 主线程进入等待状态,释放锁资源
                thread.wait();

                System.out.println(Thread.currentThread().getName() + "被唤醒了");

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


运行结果

main 运行了
main进入阻塞
副线程运行了
main被唤醒了
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值