IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Java多线程(创建线程、同步线程、利用解锁死锁解决问题) -> 正文阅读

[Java知识库]Java多线程(创建线程、同步线程、利用解锁死锁解决问题)

多线程

一、线程的概念

线程:在一个程序中同时运行的多个独立流程,每一个独立的流程就是一个线程

进程:线程是程序执行的最小单位,一个进程是由一个或多个线程组成,进程之间相互独立。一个程序的执行就是进程。

并发:一个处理器同时处理多个任务,

并行:多个处理器同时处理多个任务。

同步:当程序1调用程序2时,得先把程序1中所有的步骤都进行完,再执行一个程序

异步:当程序1调用程序2时,程序中的步骤只要地运行就可以。不用管哪个程序中的步骤先进行完。

线程状态:新建-可运行-运行-阻塞-死亡

通知机制:wait() 和 notify() 实现等待通知机制;

主线程:当JVM启动之后,加载类文件,发现main方法,那么就会为main方法创建一个线程,用于main方法执行,这个为main方法创建的线程称为主线程

封装:把属性和行为封装在类中

继承:子类可继承父类的属性和方法

多态:同一父类的,不同子类的对象,可有不同的行为。

二、Java中线程的使用

在Java中创建线程的方法有两种:
方法一:
继承java.lang.Thread类
方法二:
实现java.lang.Runnable接口

继承Thread类

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

创建线程①

继承Thread类创建线程类:

public class MyTheardB extends Thread{

    @Override
    public void run() {
        for(char c = 'A'; c<='Z';c++){
            System.out.println(c);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class TEstMain {
    public static void main(String[] args) throws Exception {

        //创建线程对象
        Thread a = new MythreadA();
        //启动支线程
        a.start();
        
        for (int i = 1; i <= 26; i++) {
            System.out.println(i);
            Thread.sleep(1000);
        }

    }
}

Runnable接口开发线程

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

创建线程②

实现Runnable创建线程类:

public class MyThreadD implements Runnable{
    @Override
    public void run() {
        for (char i = 'a'; i < 'z'; i++) {
            System.out.println(i+"@@@");
            try{
                Thread.sleep(1000);
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }
}

测试类:

public class TEstMain {
    public static void main(String[] args) throws Exception {
           for (int i = 1; i <= 26; i++) {
            System.out.println(i);
            Thread.sleep(1000);
        }
    }
}

根据学习过的知识判断,以上两种线程创建方法,哪一种更好 ? Runnable 可实现多继承。

程序的输出结果固定吗?不固定

程序中存在几个线程?执行的先后顺序 :顺序:主线程,其余的先后顺序不一定。

可不可以直接在main方法中直接调用run():可以,只是单纯的对象调用方法。

三、线程的状态

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

在这里插入图片描述

sleep()

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

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

在这里插入图片描述

join()

利用sleep方法对线程的控制是非常不精确的
join方法可以精确控制线程 join方法也会导致线程阻塞

特点:如果当前线程中调用了另外一个线程的 join方法,当前线程会立即阻塞,直到另外一个线程运行完成

练习(实践)

创建一个线程,满足设定的条件以后,将另一个线程添加进来,实现。

继承Thread类创建A类:

public class Thread_A extends Thread{
   private Thread b;

    public Thread getB() {
        return b;
    }

    public void setB(Thread b) {
        this.b = b;
    }

    public Thread_A() {
    }

    public Thread_A(Thread b) {
        this.b = b;
    }

    @Override
    public void run() {
        for (char i = 'a'; i < 'z'; i++) {
            System.out.println(i);

            if(i == 'c'){
                //让另一个线程插入当前线程

                try {
                    b.join(2000); //等两秒后继续运行
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

继承Thread创建B类:

public class Thread_B extends Thread{
    @Override
    public void run() {
        for (int i = 1; i <= 26; i++) {
            System.out.println(i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

测试类:

public class TMain {
    public static void main(String[] args) {
        //在A线程中满足条件后加入B线程

        Thread_A a = new Thread_A();
        Thread_B b = new Thread_B();
        a.setB(b);//将b属性赋给A

        a.start();
        b.start();

    }
}

运行结果:
在这里插入图片描述

其他常用方法:

public class TMain {
    public static void main(String[] args) {
        //在A线程中满足条件后加入B线程

        Thread_A a = new Thread_A();
        Thread_B b = new Thread_B();
        a.setB(b);//将b属性赋给A
        
        //线程的方法
        a.setName("线程——A");//更改线程的名字
        String name = a.getName();//获取线程的名字
        a.getId();//获取ID
        int priority = a.getPriority(); // 获取线程的优先级
        a.setPriority(1); // 更改线程的优先级  /*1-10*/
        a.setPriority(Thread.MAX_PRIORITY);//最大   //选中的概率会更大

        a.start();
        b.start();
    }
}
Thread.currentThread().getName()
获取当前的线程名称。

四、线程同步问题

synchronized 同步

产生数据不一致的原因
多个线程并发访问了同一个对象,如果破坏了不可分割的操作,从而就会造成数据不一致

被多线程并发访问时如果一个对象有可能出现数据不一致的问题,那么这个对象称为线程不安全的对象

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

synchronized(object){
      代码块
 }


//可以直接放 方法头:  synchronized
 public synchronized void deposit(int money){
			//synchronized (this){
            this.balance += money;
            System.out.println(Thread.currentThread().getName() + "存储了" + money + "   余额为:" + this.balance);
			//}
    }

客户存取款多线程实例:

Account类:(包含存取款方法,同步)

package com.richanglianxi_suihsou.one.Thread_1.Balance;

public class Person {
    private int balance;

    public int getBalance() {
        return balance;
    }

    public void setBalance(int balance) {
        this.balance = balance;
    }

    public Person() {
    }

    public Person(int balance) {
        this.balance = balance;
    }
    
	 //可以直接放 方法头:  synchronized
    public synchronized void desvion(int money){
        this.balance += money;
        System.out.println(Thread.currentThread().getName()+"您存储了:"+money+"  账户余额为:"+this.balance);
    }

    //也可以框语句
    public void withdraw(int money) {
        synchronized (this) {
            this.balance -= money;
            System.out.println(Thread.currentThread().getName()+"您取了:" + money + "  账户余额为:" + this.balance);
        }
    }

}

继承Thread类创建线程A:

package com.richanglianxi_suihsou.one.Thread_1.Balance;

public class Per_B implements Runnable {
    private Person p;

    public Person getP() {
        return p;
    }

    public void setP(Person p) {
        this.p = p;
    }

    public Per_B(Person p) {
        this.p = p;
    }

    public Per_B() {
    }

    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            p.withdraw(200);

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

实现Runnable接口创建线程B:

package com.XC_Theard.CunQuPay;

public class Person_B extends Thread{
    private Account acc;

    public Account getAcc() {
        return acc;
    }

    public void setAcc(Account acc) {
        this.acc = acc;
    }

    public Person_B() {
    }

    public Person_B(Account acc) {
        this.acc = acc;
    }

    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            acc.withdraw(100);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

测试类:

package com.richanglianxi_suihsou.one.Thread_1.Balance;

public class Main {
    public static void main(String[] args) {

        Person p = new Person(1000);//实例化对象并初始化
        Thread a = new Per_A(p);//创建A线程对象
        
        Runnable bb = new Per_B(p);
        Thread b = new Thread(bb);//创建B线程对象

        a.setName("线程A:");
        b.setName("线程B:");

        a.start();
        b.start();
    }
}

运行结果:

线程A:您存储了:1000 账户余额为:2000
线程B:您取了:200 账户余额为:1800
线程A:您存储了:1000 账户余额为:2800
线程B:您取了:200 账户余额为:2600
线程B:您取了:200 账户余额为:2400
线程A:您存储了:1000 账户余额为:3400

Process finished with exit code 0

Thread.currentThread().getName() 获取当前的线程名称。

五、 notify方法和wait方法

死锁:

线程A:

//死锁
@Override
public void run() {
    synchronized(a){
        System.out.println(Thread.currentThread().getName()+"获取a锁!");
        synchronized(b){
            System.out.println(Thread.currentThread().getName()+"获取b锁!");
        }
        System.out.println(Thread.currentThread().getName()+"释放b锁");
    }
    System.out.println(Thread.currentThread().getName()+"释放a锁");
}

线程B:

@Override
public void run() {
    synchronized(b){
        System.out.println(Thread.currentThread().getName()+"获取b锁!");
        synchronized(a){
            System.out.println(Thread.currentThread().getName()+"获取a锁!");
        }
        System.out.println(Thread.currentThread().getName()+"释放a锁");
    }
    System.out.println(Thread.currentThread().getName()+"释放b锁");
}

运行结果:

线程A:获取a锁!
线程B:获取b锁!

解锁:(wait())

wait() 释放该锁//当前线程等待(也可以在括号里加等待时间)。

notify() 唤醒等待的单个线程;

notifyAll() 唤醒等待的所有线程;

创建一个AA类:

package com.XC_Theard.SiLuck;

public class Thrad_AA extends Thread{
    private Object a;
    private Object b;

    //死锁


    public Object getA() {
        return a;
    }

    public void setA(Object a) {
        this.a = a;
    }

    public Object getB() {
        return b;
    }

    public void setB(Object b) {
        this.b = b;
    }

    public Thrad_AA() {
    }

    public Thrad_AA(Object a, Object b) {
        this.a = a;
        this.b = b;
    }


    //死锁
    //所有的对象都可以当作锁
    @Override
    public void run() {
        synchronized(a){
            System.out.println(Thread.currentThread().getName()+"获取a锁!");
            try {
                a.wait();//当前线程等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            synchronized(b){
                System.out.println(Thread.currentThread().getName()+"获取b锁!");
            }
            System.out.println(Thread.currentThread().getName()+"释放b锁");
        }
        System.out.println(Thread.currentThread().getName()+"释放a锁");
    }
}

创建一个BB类线程:

package com.XC_Theard.SiLuck;

public class Thread_BB extends Thread{
    private Object a;
    private Object b;

    //死锁


    public Object getA() {
        return a;
    }

    public void setA(Object a) {
        this.a = a;
    }

    public Object getB() {
        return b;
    }

    public void setB(Object b) {
        this.b = b;
    }

    public Thread_BB() {
    }

    public Thread_BB(Object a, Object b) {
        this.a = a;
        this.b = b;
    }

    //死锁
    @Override
    public void run() {
        synchronized(b){
            System.out.println(Thread.currentThread().getName()+"获取b锁!");
            synchronized(a){
                System.out.println(Thread.currentThread().getName()+"获取a锁!");
                a.notify();//唤醒等待的单个线程

            }
            System.out.println(Thread.currentThread().getName()+"释放a锁");
        }
        System.out.println(Thread.currentThread().getName()+"释放b锁");
    }

}

测试函数:

package com.XC_Theard.SiLuck;

public class Main {
    public static void main(String[] args) {
        Object s = new Object();
        Object s1 = new Object();

        Thread a = new Thrad_AA(s, s1);
        Thread b = new Thread_BB(s, s1);

        a.setName("线程A:");
        b.setName("线程B:");

        a.start();
        b.start();


    }
}

运行结果:

线程B:获取b锁!
线程A:获取a锁!
线程B:获取a锁!
线程B:释放a锁
线程B:释放b锁
线程A:获取b锁!
线程A:释放b锁
线程A:释放a锁

Process finished with exit code 0

死锁的产生原因和解决方法:

是由于两个或以上的线程互相持有对方需要的资源,导致这些线程处于等待状态,无法执行。

用wait() 和 notifyall();

练习题:生产者生产食物,消费者拿走食物,利用解决死锁解决问题。

编号类:

package com.XC_Theard.ShengChanXiaoFei;

public class ProDuct {
    private int id;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public ProDuct() {
    }

    public ProDuct(int id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "产品:"+id;
    }

}

生产者:

package com.XC_Theard.ShengChanXiaoFei;

import java.util.Random;

public class Perductor extends Thread{
    private Storage  sto;

    public Storage getSto() {
        return sto;
    }

    public void setSto(Storage sto) {
        this.sto = sto;
    }

    public Perductor() {
    }

    public Perductor(Storage sto) {
        this.sto = sto;
    }

    @Override
    public void run() {

        for (int i = 1; i < 20; i++) {
            ProDuct p = new ProDuct(i);
            sto.push(p);
            try {
                Thread.sleep(new Random().nextInt(1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

消费者:

package com.XC_Theard.ShengChanXiaoFei;

import java.util.Random;

public class Consumer extends Thread{
    private Storage sto;

    public Storage getSto() {
        return sto;
    }

    public void setSto(Storage sto) {
        this.sto = sto;
    }

    public Consumer() {
    }

    public Consumer(Storage sto) {
        this.sto = sto;
    }

    @Override
    public void run() {

        for (int i = 1; i < 20; i++) {
            sto.pop();
            try {
                Thread.sleep(new Random().nextInt(3000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

存储和取出方法:

package com.XC_Theard.ShengChanXiaoFei;

public class Storage {
    private ProDuct[] peos = new ProDuct[5];

    int index = 0;

    //存
    public synchronized void push(ProDuct p) {

        while(index >= peos.length){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        peos[index] = p;
        index++;
        System.out.println(Thread.currentThread().getName()+"在"+index+"位置放入了"+p);
        this.notifyAll();

    }


    //取
    public synchronized void pop() {

        while(index<=0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        index--;
        ProDuct p = peos[index];
        System.out.println(Thread.currentThread().getName()+"在"+index+"位置取出了"+p);

        this.notifyAll();

    }




}

测试函数:

package com.XC_Theard.ShengChanXiaoFei;

public class Maina {
    public static void main(String[] args) {
        Storage s =new Storage();
        Perductor p = new Perductor(s);
        Consumer c = new Consumer(s);

        p.setName("生产者:");
        c.setName("消费者:");
        p.start();
        c.start();

    }
}

运行结果:

生产者:在1位置放入了产品:1
消费者:在0位置取出了产品:1
生产者:在1位置放入了产品:2
生产者:在2位置放入了产品:3
生产者:在3位置放入了产品:4
生产者:在4位置放入了产品:5
消费者:在3位置取出了产品:5
生产者:在4位置放入了产品:6
生产者:在5位置放入了产品:7
消费者:在4位置取出了产品:7
生产者:在5位置放入了产品:8
消费者:在4位置取出了产品:8
生产者:在5位置放入了产品:9
消费者:在4位置取出了产品:9
生产者:在5位置放入了产品:10
消费者:在4位置取出了产品:10
生产者:在5位置放入了产品:11
消费者:在4位置取出了产品:11
生产者:在5位置放入了产品:12
消费者:在4位置取出了产品:12
生产者:在5位置放入了产品:13
消费者:在4位置取出了产品:13
生产者:在5位置放入了产品:14
消费者:在4位置取出了产品:14
生产者:在5位置放入了产品:15
消费者:在4位置取出了产品:15
生产者:在5位置放入了产品:16
消费者:在4位置取出了产品:16
生产者:在5位置放入了产品:17
消费者:在4位置取出了产品:17
生产者:在5位置放入了产品:18
消费者:在4位置取出了产品:18
生产者:在5位置放入了产品:19
消费者:在4位置取出了产品:19
消费者:在3位置取出了产品:6
消费者:在2位置取出了产品:4
消费者:在1位置取出了产品:3
消费者:在0位置取出了产品:2

Process finished with exit code 0

练习:用两种方式实现两个线程,一个线程负责打印1-2600,另一个线程打印A-Z,反复打印100遍

输出数字线程A类:

package com.XC_Theard.PrintA_Z;

public class Thr_A extends Thread{
    @Override
    public void run() {
        for (int i = 1; i < 2600; i++) {
            System.out.println(i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

输出字母线程B类:

package com.XC_Theard.PrintA_Z;

public class Thr_B implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            for (char j = 'A'; j <= 'Z'; j++) {
                System.out.println(j);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

测试类:

package com.XC_Theard.PrintA_Z;

public class Main {
    public static void main(String[] args) {
        Thread a = new Thr_A();

        Runnable b1 = new Thr_B();
        Thread b = new Thread(b1);

        a.start();
        b.start();
    }
}

运行结果:

1
A
2
B
3
C
D
4

……省略。。。

Process finished with exit code -1

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-08-03 11:01:36  更:2021-08-03 11:02:11 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/3 22:16:30-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码