JAVA多线程最实用教程

多线程(非常重要)

一、多线程概述
1.什么是多线程?

​ 线程就是执行一条程序的步骤,多线程就是执行多个程序的步骤

申明:文章通过学习撰写的笔记,里面图片资源来源黑马阿伟教程
2.多线程的作用

​ 提高效率

3.多线程的应用场景?

​ 只要想让软件同时完成几个步骤,就必须用到多线程

​ 比如:软件中的耗时操作,所有的聊天软件,所有的服务器

二、并发和并行
1.并发:同一时刻,有多个指令在单个CPU上交替执行

2.并行:同一时刻,有多个指令在多个CPU上同时执行
三、多线程的三种实现方式
1.继承Thread类的方式进行实现
package itheima.thread;

public class Test03 {
    public static void main(String[] args) {
        //实例化多线程类
        Thread t = new MyThread();
        t.start();//只能是调用start方法来执行多线程

        for (int i = 0; i < 10; i++) {
            System.out.println("Main多线程执行"+i);
        }
    }
}
/**
*创建多线程类
*/
class MyThread extends Thread{
    @Override
    public void run(){
        for (int i = 0; i < 10; i++) {
            System.out.println("Mythread多线程执行"+i);
        }
    }
}
2.实现Runnable接口的方式进行实现
package itheima.thread;

public class Test01 {
    public static void main(String[] args) {
        //匿名内部类+Runnable实现
        new Thread(()->{
                for (int i = 0; i < 10; i++) {
                    System.out.println("多线程Run:"+i);
                }
        }).start();

        for (int i = 0; i < 10; i++) {
            System.out.println("多线程Main:"+i);
        }

    }
}

3.利用Callable接口和Future接口方式实现(可获取返回值)
//创建MyCallable的对象(表示多线程要执行的任务)
//创建FutureTask的对象(作用管理多线程运行的结果)
//创建Thread的对象(表示线程)
package itheima.thread;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Test04 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyCallable mc = new MyCallable();//多线程要执行的任务对象
        FutureTask<Integer> ft = new FutureTask<>(mc);//管理多线程任务执行的结果对象
        //创建多线程的对象
        Thread t = new Thread(ft);
        t.setName("线程t");
        t.start();


        //多线程执行的任务的结果
        System.out.println(t.getName()+"线程的结果:"+ft.get());

    }
}

/**
 * 创建将要执行的线程任务
 */
class MyCallable implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 1;i <= 100;i++){
            sum = sum + i;
        }
        return sum;
    }
}


四、常用的成员方法
1.常用的方法
方法名称说明
String getName()返回此线程的名称
void setName(String name)设置线程的名字(构造方法也可以设置名字)
static Thread currentThread()获取当前线程的对象
static void sleep(long time)设置当前线程休眠的时间,单位为毫秒
setPriority(int newPriority)设置线程的优先级
final int getPriority()获取线程的优先级
final void setDaemon(boolean on)设置为守护线程
public static void yield()出让线程/礼让线程
public static void join()插队线程/插入线程

​ 2.代码实现

/*
	--String getName()	返回此线程的名称
	
	--void setName(String name)	设置线程的名字(构造方法也可以)
	细节:
		1、如果外面没有给线程设置名字,线程也是有默认名字的。
		格式:Thread-x(x是从0开始的序号)
		2、如果外面要给线程设置名字,可以用set方法进行设置,也可以构造方法设置
	
	--static Thread currentThread()	获取当前线程的对象
	细节:
		当JVM虚拟机启后,会自动启动多条线程
		其中有一条就是main线程
		他的作用方法就是去调用main方法,并执行里面的代码
		在以前,我们写的所有代码,都是运行在main线程中的
	
	--static void sleep(long time)	让线程按照指定时间休眠,单位为毫秒
	细节:
		1.哪条线程执行到了这个方法,哪么就会休眠对应的时间
		2.1s = 1000ms
		3.时间到了后会自动醒来继续执行*/
		
		public class Test04 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyCallable mc = new MyCallable();//多线程要执行的任务对象
        FutureTask<Integer> ft = new FutureTask<>(mc);//管理多线程任务执行的结果对象
        //创建多线程的对象
        Thread t = new Thread(ft);
        //t.setName("线程t");注释设置名字,获取的是默认的名字Thread-0

        //设置后就按照设置的来。java是抢占式调度,优先级越高,先执行完的概率就越大
        //不设置的话,默认优先级就是5
        t.setPriority(3);

        t.start();//启动线程

        System.out.println("哈喽");
        Thread.sleep(1000);//让当前线程按照指定毫秒进行休眠
        //多线程执行的任务的结果

        System.out.println(t.getName()+"线程的结果:"+ft.get()+", 优先级:"+t.getPriority());
    }
}

/**
 * 创建将要执行的线程任务
 */
class MyCallable implements Callable<Integer> {
    //
    @Override
    public Integer call() throws Exception {

        Thread t = Thread.currentThread();
        System.out.println("线程的名字:"+t.getName());
        int sum = 0;
        for (int i = 1;i <= 100;i++){
            sum = sum + i;
        }
        return sum;
    }
}


/**
*练习守护线程
*/
public class Test05 {
    public static void main(String[] args) {
        //设置备胎线程
        Thread t1 = new MyThread1();
        Thread t2 = new MyThread2();

        t2.setName("--女神线程--");

        t1.setName("备胎线程");
        t1.setDaemon(true);//备胎线程设置成功

        t1.start();//备胎线程的优先级低于其他非备胎线程
        t2.start();//



    }
}
//
class MyThread1 extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(getName()+"运行第"+i+"次");
        }
    }
}

//
class MyThread2 extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(getName()+"运行第"+i+"次");
        }
    }
}
五、线程的生命周期

在这里插入图片描述

六、线程锁
1.synchronized//同步代码块
package itheima.thread;

//多线程的安全问题
public class Test06 {
    public static void main(String[] args) {
        Thread t1 = new MyThread0();
        Thread t2 = new MyThread0();
        Thread t3 = new MyThread0();

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

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

class MyThread0 extends Thread {
    private static int ticket = 1;//票

    private static Object o = new Object();//不重复的,唯一的

    //为什么要加线程锁
    //因为三个线程可能同时抢到cpu的执行权,执行这个打印而不执行ticket++
    //这样就会出现三个售票台出售一张票的情况
    @Override
    public void run() {
        while (true) {
            synchronized (o) {//门锁住了,就只能等抢到的线程,cpu分配给他的时间结束再去抢
                if (ticket <= 100) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }//因为父类run方法没有抛出异常,所以重写的run方法是不能抛出异常的
                    System.out.println(getName() + "正在卖第" + ticket + "张票");
                    ticket++;
                } else {
                    break;
                }
            }
        }
    }
}
2.同步方法写法
package itheima.synchronized1;

public class Test {
    public static void main(String[] args) {
        MyRun r1 = new MyRun();
        Thread t1 = new Thread(r1);//创建售票台1
        Thread t2 = new Thread(r1);//创建售票台2
        Thread t3 = new Thread(r1);//创建售票台3

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

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


    }
}

/**
 * 创建任务类
 */
class MyRun implements Runnable {
    private static int ticket = 0;//设置票数,三个售票台都共享

    @Override
    public void run() {
        while (true) {
            if (method()) break;
        }
    }

    //同步方法锁住线程
    public synchronized boolean method() {
        if (ticket == 100) {
            return true;
        } else {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            ticket++;
            System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张票");


        }
        return false;
    }
}

3.lock锁
package itheima.synchronized1;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

//多线程的安全问题
public class Test01 {
    public static void main(String[] args) {
        Thread t1 = new MyThread0();
        Thread t2 = new MyThread0();
        Thread t3 = new MyThread0();

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

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

class MyThread0 extends Thread {
    private static int ticket = 1;//票

    //lock接口不能实例化,这里用的他实现类new对象
    //锁也是被我们三个线程所共享的所以要加上静态
    //如果没有静态,就没有把线程同步加锁,就会出现数据不安全的情况(数据重复啊,导致数据无效)
    static Lock lock = new ReentrantLock();

    //为什么要加线程锁
    //因为三个线程可能同时抢到cpu的执行权,执行这个打印而不执行ticket++
    //这样就会出现三个售票台出售一张票的情况
    @Override
    public void run() {
        while (true) {
            //如果不加try finally那么锁就不会打开,导致程序还会一直运行中
            //因为到判断成功后跳出循环了,却漏掉unlock开锁的方法
            //这就导致,其他线程,比如线程2和线程3都还在门外等着,所以程序不会停止
            lock.lock();
            try {
                if (ticket <= 100) {
                    //因为父类run方法没有抛出异常,所以重写的run方法是不能抛出异常的
                    Thread.sleep(10);
                    System.out.println(getName() + "正在卖第" + ticket + "张票");
                    ticket++;
                } else {
                    break;
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }

        }
    }
}

4.扩展:死锁

​ 情况就是两把锁,必须同时拿到两把锁才能释放锁,但是A和B同时拿了一把锁,都在等着对方将锁给放下来,然后拿到锁。一直保存就会造成死锁。

5.扩展StringBuilder与StringBuffer的区别

StringBuilder:适用于单线程,因为他的方法没有加锁,所以线程不安全(单线程就不用担心不安全),但是效率更快
StringBuffer:适用于多线程,因为他的方法加了同步锁,所以线程安全(就是数据不会出现异常),但是相对效率慢一点

七、生产消费模式
1.思路

​ 生产者:厨师

​ --判断桌子有没有食物

​ --有:等待 没有:生产食物

​ --生产后,唤醒消费者开始消费

​ 消费者:食客

​ --判断桌子有没有食物

​ --如果没有就等待

​ --如果有就开吃

​ --吃完后就唤醒厨师

2.常见方法
方法名称说明
void wait()More Actions当前线程等待,直到被其他线程唤醒
void notify()随机唤醒单个线程
void notifyAll()唤醒所有线程
3.代码实现
package itheima.threadmode;

public class ModeTest {
    public static void main(String[] args) {
        Cook c = new Cook();
        Foodie f = new Foodie();

        c.setName("厨师");
        f.setName("食客");

        c.start();
        f.start();
    }
}
/**
 * 厨师
 */
  
class Cook extends Thread {
    /*
     * 步骤:
     * --循环
     * --同步线程锁
     * --判断共享数据到了末尾的情况(就是吃够10碗)
     * --判断共享数据还没到末尾的情况(就是没吃够,还想要继续)
     *
     */

    @Override
    public void run() {
        while (true) {
            synchronized (Desk.lock){
                //判断共享数据到末尾了没(这里就是碗数到了没)
                if (Desk.count==0){
                    //碗数到了就结束线程
                    break;
                }else{
                    //碗数未到就判断桌子上有没有食物
                    if (Desk.foodflag==1){
                        //有就等待食客吃完后唤醒线程
                        try {//因为父类run方法没有抛出异常,重写的也是没办法抛出的,只能try
                            Desk.lock.wait();//绑定线程锁,方便只唤醒这个线程
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }else {
                        //没有就生产食物
                        Desk.foodflag = 1;
                        System.out.println("厨师生产一份食物");
                        //生产完后就唤醒再等待的食客线程
                        Desk.lock.notifyAll();
                    }

                }
            }
        }
    }
}

/**
 * 桌子
 */
class Desk {
    //用来判断是到生产还是消费

    //判断有没有面条 0:没有  1:有
    public static int foodflag = 0;

    //总碗数
    public static int count = 10;

    //线程锁
    public static Object lock = new Object();

}

/**
 * 食客
 */
class Foodie extends Thread{
    /*
     * 步骤:
     * --循环
     * --同步线程锁
     * --判断共享数据到了末尾的情况(就是吃够10碗)
     * --判断共享数据还没到末尾的情况(就是没吃够,还想要继续)
     *
     */

    @Override
    public void run() {
        while (true){
            //加上同步线程的锁
            synchronized (Desk.lock){
                //判断是否吃够总限额数,10碗
                if (Desk.count==0){
                    //这里面表示吃完了
                    //结束线程
                    break;
                }else {
                    //还没吃完,就判断桌子上有没有食物
                    //有就吃
                    //没有就唤醒厨师生产食物
                    if (Desk.foodflag==1){
                        //有就吃
                        Desk.count--;
                        //吃完还剩几碗
                        System.out.println("吃货正在吃面条,还能再吃"+Desk.count+"碗!!!");
                        //吃完桌子的状态就变成没有食物了
                        Desk.foodflag = 0;

                        //吃完就唤醒厨师继续生产
                        Desk.lock.notifyAll();//唤醒与所有lock锁绑定的等待中的线程
                    }else {
                        //没有就等待
                        try {
                            Desk.lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
}

  • 29
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java2实用教程 rar 第1章Java入门 1 1Java的诞生 1 2Java的特点 1 3安装Sun公司的SDK 1 4一个Java程序的开发过程 1 5一个简单的Java应用程序的开发过程 1 6一个简单的Java小应用程序 1 7什么是JSP 习题 第2章标识符 关键字和数据类型 2 1标识符和关键字 2 2Java的基本数据类型 2 3Java的数组 习题 第3章运算符 表达式和语句 3 1运算符与表达式 3 2语句概述 3 3控制语句 3 4 循环语句 3 5break和continue语句 习题 第4章类 对象和接口 4 1编程语言的几个发展阶段 4 1 1机器语言 4 1 2过程语言 4 1 3面向对象编程 4 2类 4 2 1类声明 4 2 2类体 4 2 3成员变量和局部变量 4 2 4方法 4 2 5方法重载 4 2 6构造方法 4 2 7类方法和实例方法 4 2 8值得注意的问题 4 3对象 4 3 1创建对象 4 3 2使用对象 4 3 3于象的引用和实体 4 3 4参数传值 4 4static关键字 4 4 1实例变量和类变量的区别 4 4 2实例方法和类方法的区别 4 5this关键字 4 6包 4 6 1包语句 4 6 2import语句 4 6 3将类打包 4 7访问权限 4 7 1私有变量和私有方法 4 7 2共有变量和共有方法 4 7 3友好变量和友好方法 4 7 4受保护的成员变量和方法 4 7 5public类与友好类 4 8类的继承 4 8 1子类 4 8 2子类的继承性 4 8 3成员变量的隐藏和方法的重写 4 8 4final关键字 4 9对象的上转型对象 4 10多态性 4 11abstract类和abstract方法 4 12super关键字 4 13接口 4 13 1接口的声明与使用 4 13 2理解接口 4 13 3接口回调 4 13 4接口做参数 4 14内部类 4 15匿名类 4 15 1和类有关的匿名类 4 15 2和接口有关的匿名类 4 16异常类 4 16 1try catch语句 4 16 2自定义异常类 4 17Class类 4 17 1获取类的有关信息 4 17 2使用Class实例化一个对象 4 18基本类型的类包装 4 18 1Double类和Float类 4 18 2Byte Integer Short 工 ong类 4 18 3Character类 4 19反编译和文档生成器 4 20JAR文件 4 20 1将应用程序压缩为JAR文件 4 20 2将类压缩成JAR文件 4 20 3更新 查看JAR文件 习题 第5章字符串 5 1字符串 5 2字符串的常用方法 5 3字符串与基本数据的相互转化 5 4对象的字符串表示 5 5StringTokenizer类 5 6字符串与字符 字节数组 5 7StringBuffer类 5 8正则表达式 习题 第6章时间 日期和数字 6 1Date类 6 2Calendar类 6 3Math类 6 4BigInteger类 习题 第7章AWT组件及事件处理 7 1Java窗口 7 1 1 Frame常用方法 7 1 2菜单条 菜单 菜单项 7 1 3窗口与屏幕 7 2文本框 7 2 1TextField类的主要方法 7 2 2文本框上的ActionEvent事件 7 3内部类实例做监视器 7 4按钮与标签 7 4 1标签组件 7 4 2按钮组件 7 5菜单项 7 6文本区 7 6 1TextArea类主要方法 7 6 2文本区上的TextEvent事件 7 7面板 7 7 1Panel类 7 7 2ScrollPane类 7 8布局 7 8 1FlowLayout布局 7 8 2BorderLayout布局 7 8 3CardLayout布局 7 8 4GridLayout布局 7 8 5BoxLayout布局 7 8 6null布局 7 9画布 7 10选择型组件 7 10 1选择框 7 10 2下拉列表 7 10 3滚动列表 7 11Component类的常用方法 7 12窗口事件 7 13鼠标事件 7 14焦点事件 7 15键盘事件 7 16使用剪贴板 7 17打印 7 18综合实例 习题 第8章建立对话框 8 1Dialog类 8 2文件对话框 8 3消息对话框 8 4确认对话框 8 5颜色对话框 习题 第9章Java多线程机制 9 1Java中的线程 9 2Thread类的子类创建线程 9 3使用Runnable接口 9 4线程的常用方法 9 5GUI线程 9 6线程同步 9 7在同步方法中使用wait notif 和nodf3 All 方法 9 8挂起 恢复和终止线程 9 9计时器线程Timer 9 10线程联合 9 11守护线程 习题 第10章输入输出流 10 1File类 10 2FileInputStream类 10 3FileOutputStream类 10 4FileReader类和FileWriter类 10 5使用文件对话框打开和保存文件 10 6RandornAccessFile类 10 7数据流 10 8数组流 10 9对象流 10 10序列化与对象克隆 10 11文件锁FileLock 10 12Process类中的流 10 13带进度条的输入流 习题 第11章Java网络的基本知识 11 1使用URL 11 2读取URL中的资源 11 3显示URL资源中的HTML文件 11 4处理超链接 11 5InetAdress类 11 6套接字 11 7网络中的数据压缩与传输 11 8UDP数据报 11 9广播数据报 习题 第12章JavaApplet基础 12 1JavaApplet的运行原理 12 2网页向JavaApplet传值 12 3JavaApplet扣使用URL 12 4JavaApplet中建立新线程 12 5JavaApplet中使用套接字 习题 第13章常见数据结构的Java实现 13 1链表 13 2栈 13 3树集 13 4树映射 13 5散列集 13 6散列表 13 7向量 习题 第14章图形与图像 14 1绘制文本 14 2绘制基本图形 14 3建立字体 14 4清除 14 5Java2D 14 6图形的布尔运算 14 7绘制钟表 14 8绘制图像 14 9制作JPG图像 14 10XOR绘图模式 14 11打印图形 图像 习题 第15章Java数据库连接 JDBC 15 1创建数据源 15 2JDBC ODBC桥接器 l5 3顺序查询 15 4可滚动结果集 15 5排序查询 15 6模糊查询 15 7随机查询 15 8更新 添加 删除记录 l5 9预处理语句 15 10数据库访问中的套接字技术 习题 第16章Java与多媒体 16 1在小程序中播放音频 16 2在另一个线程中创建音频对象 16 3在应用程序中播放音频 16 4Java媒体框架 JMF 习题 第17章JavaSwing基础 17 1几个重要的类 17 2中间容器 17 3各种组件 习题">Java2实用教程 rar 第1章Java入门 1 1Java的诞生 1 2Java的特点 1 3安装Sun公司的SDK 1 4一个Java程序的开发过程 1 5一个简单的Java应用程序的开发过程 1 6一个简单的Java小应用程序 1 7什么是JSP 习题 第2章标识符 关键字和数据类型 2 1标识 [更多]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值