Java 基础入门 | 第十五章 Java多线程

目录

线程的概念

线程的使用

线程的状态

线程同步问题

死锁和解决办法


线程的概念

什么是线程
在一个程序中同时运行的多个独立流程,每一个独立的流程就是一个线程
线程并发
多个线程并发执行
主线程  CPU  优先级   时间片  资源
当JVM启动之后,加载类文件,发现main方法,那么就会为main方法创建一个线程,用于main方法执行,这个为main方法创建的线程称为主线程


线程的使用

在Java中创建线程的方法有两种

  • 方法一  继承java.lang.Thread类
  • 方法二  实现java.lang.Runnable接口

继承Thread类

  • 自定义一个线程类继承自Thread
  • 重写Thread的run方法
  • 创建一个该类的对象
  • 调用该类继承自Thread的start方法开启线程

 代码示例

public class MyThread1 extends Thread{
    public void run(){
        for (int i = 0; i < 1000; i++) {
            System.out.println(i+" $$$");
        }
    }
}
public class MyThread2 extends Thread{
    public void run(){
        for (int i = 0; i < 1000; i++) {
            System.out.println(i+" ###");
        }
    }
}
public class ThreadTest {
    public static void main(String[] args) {
        Thread t1=new MyThread1();
        Thread t2=new MyThread2();
        //启动线程
        t1.start();
        t2.start();
    }
}

Runnable接口开发线程 

  • 用户开发一个类实现Runnable接口
  • 实现run()
  • 运行线程 

代码示例 

public class MyRunnable1 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println(i+" $$$");
        }
    }
}
public class MyRunnable2 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println(i+" ###");
        }
    }
}
public class ThreadTest {
    public static void main(String[] args) {
        Runnable r1=new MyRunnable1();
        Thread t1=new Thread(r1);
        Runnable r2=new MyRunnable2();
        Thread t2=new Thread(r2);
        //启动线程
        t1.start();
        t2.start();
    }
}

线程使用总结

  • 根据学习过的知识判断,以上两种线程创建方法,哪一种更好--第二种实现了任务代码和API的分离
  • 程序的输出结果固定吗?
  • 程序中存在几个线程?执行的先后顺序
  • 可不可以直接在main方法中直接调用run()


线程的状态

  • 初始状态
  • 可运行状态
  • 运行状态
  • 终结状态 

sleep方法 

void sleep(long time)方法用于使当前线程休眠指定的毫秒数


public class MyThread1 extends Thread{
    public void run(){
        for (int i = 0; i < 1000; i++) {
            System.out.println(i+" $$$");
            try {
                //睡眠1秒
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class MyRunnable1 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println(i+" $$$");
            try {
                //睡眠1秒
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

 一旦调用了sleep方法,该线程就由运行状态进入了阻塞状态
join方法的使用

  • 利用sleep方法对线程的控制是非常不精确的
  • join方法可以精确控制线程
  • join方法也会导致线程阻塞
  • 特点:如果当前线程中调用了另外一个线程的 join方法,当前线程会立即阻塞,直到另外一个线程运行完成
public class MyThread1 extends Thread{
    public void run(){
        for (int i = 0; i < 1000; i++) {
            System.out.println(i+" $$$");
        }
    }
}

public class MyThread2 extends Thread{
    Thread thread;
    public void run(){
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < 1000; i++) {
            System.out.println(i+" ###");
        }
    }
}

public class ThreadTest {
    public static void main(String[] args) {
        MyThread1 t1=new MyThread1();
        MyThread2 t2=new MyThread2();
        //将线程1赋值到MyThread2的属性thread
        t2.thread=t1;
        //启动线程
        t1.start();
        t2.start();
    }
}

join方法的问题
如果2个线程彼此调用对方的join方法,会导致程序无法进行。
解决办法

调用 void join(long millis)  方法

客户存取款多线程实例


public class Account {
    //银行账户余额
    private int balance;

    public Account(int balance) {
        this.balance = balance;
    }

    //存款方法
    public void deposit(int money){
        balance=balance+money;
    }
    //取款方法
    public void withdrawal(int money){
        if(money>balance){
            System.out.println("余额不足");
        }else{
            balance=balance-money;
        }
    }

    public int getBalance() {
        return balance;
    }
}

public class PersonA extends Thread{
    //用户A的账户属性
    private  Account account;
    public PersonA(Account account){
        this.account=account;
    }

    public void run(){
        for (int i = 0; i < 2; i++) {
            System.out.println("此时账户还有"+account.getBalance()+"元");
            account.deposit(500);
            System.out.println("用户A存款500元");
            System.out.println("此时账户还有"+account.getBalance()+"元");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class PersonB extends Thread{
    //用户B的账户属性
    private  Account account;
    public PersonB(Account account){
        this.account=account;
    }

    public void run(){
        for (int i = 0; i < 2; i++) {
            System.out.println("此时账户还有"+account.getBalance()+"元");
            account.withdrawal(1000);
            System.out.println("用户B取款1000元");
            System.out.println("此时账户还有"+account.getBalance()+"元");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class BankDemo {
    public static void main(String[] args) {
        //初始账户余额3000块
        Account account=new Account(3000);
        PersonA personA=new PersonA(account);
        PersonB personB=new PersonB(account);
        //启动线程
        personA.start();
        personB.start();
    }
}


线程同步问题

  • 产生数据不一致的原因:多个线程并发访问了同一个对象,如果破坏了不可分割的操作,从而就会造成数据不一致
  • 被多线程并发访问时如果一个对象有可能出现数据不一致的问题,那么这个对象称为线程不安全的对象

如何解决多线程并发访问的问题

利用synchronized 同步锁


  synchronized(Object o){
      代码块
    }

public class Account {
    //银行账户余额
    private int balance;

    private Object o=new Object();

    public Account(int balance) {
        this.balance = balance;
    }

    //存款方法
    public void deposit(int money){
        synchronized (o){
            balance=balance+money;
        }
    }
    //取款方法
    public void withdrawal(int money){
        synchronized (o){
            if(money>balance){
                System.out.println("余额不足");
            }else{
                balance=balance-money;
            } 
        }
    }

    public int getBalance() {
        return balance;
    }
}


死锁和解决办法

什么是死锁 

死锁是因为多线程访问共享资源,由于访问的顺序不当所造成的,通常是一个线程锁定了一个资源A,而又想去锁定资源B;在另一个线程中,锁定了资源B,而又想去锁定资源A以完成自身的操作,两个线程都想得到对方的资源,而不愿释放自己的资源,造成两个线程都在等待,而无法执行的情况。



public class MyThread1 extends Thread{
    //资源a
    private Object a;
    //资源b;
    private Object b;

    public MyThread1(Object a,Object b){
        this.a=a;
        this.b=b;
    }
    public void run(){
        //当前线程名称
        String name = Thread.currentThread().getName();
        System.out.println(name+"获取资源a");
        synchronized (a){
            System.out.println(name+"获取资源b");
            await();
            synchronized (b){
                System.out.println(name+"释放资源b");
                await();
            }
            System.out.println(name+"释放资源a");
        }
    }

    private void await() {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


public class MyThread2 extends Thread{
    //资源a
    private Object a;
    //资源b;
    private Object b;
    public MyThread2(Object a,Object b){
        this.a=a;
        this.b=b;
    }
    public void run(){
        //当前线程名称
        String name = Thread.currentThread().getName();
        System.out.println("获取资源b");
        synchronized (b){
            System.out.println(name+"获取资源b");
            await();
            synchronized (a){
                System.out.println(name+"释放资源a");
                await();
            }
            System.out.println(name+"释放资源b");
        }
    }

    private void await() {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class ThreadTest {
    public static void main(String[] args) {
        Object a=new Object();
        Object b=new Object();
        MyThread1 t1=new MyThread1(a,b);
        MyThread2 t2=new MyThread2(a,b);
        //启动线程
        t1.start();
        t2.start();
    }
}

解决死锁

利用wait()方法和notify()方法

  • wait():等待,如果线程执行了wait方法,那么该线程会进入等待的状态,等待状态下的线程必须要被其他线程调用notify()方法才能唤醒。
  • notify():唤醒,唤醒线程池等待线程其中的一个。
  • notifyAll():唤醒线程池所有等待线程。

wait与notify方法要注意的事项:

  1. wait方法与notify方法是属于Object对象的。
  2.  wait方法与notify方法必须要在同步代码块或者是同步函数中才能使用。
  3. wait方法与notify方法必须要由所对象调用。

public class MyThread1 extends Thread{
    //资源a
    private Object a;
    //资源b;
    private Object b;

    public MyThread1(Object a,Object b){
        this.a=a;
        this.b=b;
    }
    public void run(){
        //当前线程名称
        String name = Thread.currentThread().getName();
        System.out.println(name+"获取资源a");
        synchronized (a){
            try {
                a.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(name+"获取资源b");
            await();
            synchronized (b){
                System.out.println(name+"释放资源b");
                await();
            }
            System.out.println(name+"释放资源a");
        }
    }

    private void await() {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


public class MyThread2 extends Thread{
    //资源a
    private Object a;
    //资源b;
    private Object b;
    public MyThread2(Object a,Object b){
        this.a=a;
        this.b=b;
    }
    public void run(){
        //当前线程名称
        String name = Thread.currentThread().getName();
        System.out.println("获取资源b");
        synchronized (b){
            System.out.println(name+"获取资源b");
            await();
            synchronized (a){
                System.out.println(name+"释放资源a");
                a.notify();
                await();
            }
            System.out.println(name+"释放资源b");
        }
    }

    private void await() {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class ThreadTest {
    public static void main(String[] args) {
        Object a=new Object();
        Object b=new Object();
        MyThread1 t1=new MyThread1(a,b);
        MyThread2 t2=new MyThread2(a,b);
        //启动线程
        t1.start();
        t2.start();
    }
}

下一章《Java 基础入门 | 第十六章 Java网络编程》icon-default.png?t=M1H3https://tarzan.blog.csdn.net/article/details/123034860上一章《Java 基础入门 | 第十四章 File以及I/O流》icon-default.png?t=M1H3https://tarzan.blog.csdn.net/article/details/122785711

 解决死锁

  • 3
    点赞
  • 6
    收藏
  • 打赏
    打赏
  • 1
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:代码科技 设计师:Amelia_0503 返回首页
评论 1

打赏作者

洛阳泰山

原创不易,感谢支持

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值