Java总结11 Java中的多线程体系 Thread / Runnable / 同步数据处理 等概念与应用

1.多线程之Thread

一/创建

一/调用类之格式

1/
以空参构造创建,如果命名则通过setName方法命名.
格式:Thread线程继承类 自定义子线程对象 = new Thread线程继承类();
列如:ThreadClass threadClassObj = new ThreadClass();//

2/
以有参构造创建,直接通过括号内的String字符串命名.
格式:Thread线程继承类 自定义子线程对象 = new Thread线程继承类(String字符串);
列如:ThreadClass threadClassObjThree = new ThreadClass(“世界大同”);
二/继承类之格式:
格式:

public class Thread线程继承类 extends Thread {
    @Override
    public void run(){
       //方法体
    }
}

public class ThreadClass extends Thread {
@Override
public void run(){
//方法体
}
}

二/线程名称相关

一.通过get/set方法设置.
1.在Main方法内
通过set/get设置线程名
格式:自定义子线程对象.setName();
列如:threadClassObj.setName(“中华有为 - 华为”);
在Thread线程继承类内

2.通过继承方法来获取线程名

格式:getName();

列如:System.out.println(“线程名为[”+getName()+"]的数值为"+i);

二.通过有参构造方法设置.
1.在Main方法内

以有参构造创建线程,并给线程命名
格式:Thread线程继承类 自定义子线程对象 = new Thread线程继承类("自定义子线程名");
列如:ThreadClass threadClassObjThree = new ThreadClass(“世界大同”);

获取线程名

格式:自定义子线程对象.getName()

列如:System.out.println(threadClassObjThree.getName());

2.在Thread线程继承类内

通过有参构造并配合super给其父类传递名字.
格式:

public Thread线程继承类(String name){
                super();
            }

列如:
public ThreadClass(String name){
super();
}

三/方法应用

1/
启动线程

格式:对象名.strat();
列如:threadClassObj.start();

2/
等待线程死亡

格式:对象名.Join();
列如: try {
threadJoinObj.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
先让某个子线程的代码(即Run方法)全部执行完毕后,再去运行其它代码或运行其它子程序. 调用该方法需要处理异常.

3/
守护线程
格式:对象名.setDaemon(true / false);
列如:threadDaemonObj.setDaemon(true);
在子线程运行期间,如果主线程(即调用了子线程的方法,如’Main’)的代码全都执行完了,就停止当前所有在运行的子线程.
在这里插入图片描述

4/
暂停线程

格式:Thread线程继承类之Run方法{sleep(毫秒数);}
列如:public class ThreadSleep extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + “:” + i);
}
}
}
让当前线程在其Run方法内暂停一段时间(以毫秒为单位).再去执行接下来的代码. 一般用在Thread线程继承类的Run方法里头;用在主线程的话则需要异常处理.

5/
获取线程优先级

格式:线程对象名.getPriority();
列如:threadClassObj.getPriority();

6/
设置线程优先级

格式:线程对象名.setPriority(数值);
列如:threadClassObj.setPriority(5);
优先级数值表示范围为1-10 1最优先,10为最次.

2.多线程之Runnable(优先用它)

Runnable 这一种线程方式 推荐多用该方式,少用第一个方式
在这里插入图片描述
创建:
步骤1/
格式:Runnable线程实现类 自定义Runnable实现对象名 = new Runnable线程实现类();
列如:RunnableImplements runnableImplementsObj = new RunnableImplements();
步骤2/
格式:Thread 自定义Thread对象名 = new Thread(自定义Runnable实现对象名);
列如:Thread threadObj = new Thread(runnableImplementsObj);

注意:即使两个Thread对象指向同一个Runnable线程实现类对象,那也是两个独立的子线程.
如:
RunnableImplements runnableImplementsObj = new RunnableImplements();
Thread threadObj = new Thread(runnableImplementsObj);
Thread threadObjTwo = new Thread(runnableImplementsObj);

其中的’threadObj’和’threadObjTwo’尽管都指向了同一个Runable线程实现类对象’runnableImplementsObj’,但实际是两个互不冲突的子线程,他们之间并不会冲突,他们是个各自独立的子线程.

3.数据同步-解决多线程数据出错.

概念

数据同步,在多线程处理’共享数据(即:处理同一个run方法)‘时,如果不让其进行’数据同步’之处理,那么就会造成结果达不到预期的情况.
比如共有100张票,让线程去买票,买一次就减少一张.但如果不做’数据同步’的话,那么就会出现三个线程同买第XX张**(如三个都买第65张的票)**的错误情况.
深层次的问题之逻辑原因:是3个线程在处理共享数据时,是在相同时间点有一个以上的线程在共同处理某个数据造成的.
深层次逻辑的解决方案:既然是在相同时间点处理某个共同数据造成的,那么就让其排队.
比如说当’线程A’处理后,再让’线程B’处理,‘线程B’处理后,再让’线程C’,接着再’线程A’,如此循环就不会有数据出错的情况了.是为"排队"
接下来的3个解决方案,无不是运用了这个逻辑.

解决方案1/ 同步代码块

步骤1/ 声明一个Obj类型的成员变量,并为其赋值一个Object对象
格式:权限修饰符 Object 自定义成员变量名 = new Object();
列如:private Object objVariable = new Object();

步骤2/ 在需要进行同步处理的代码块上,加上’synchronized’同步数据声明语句,让其线程们排队执行该块代码
格式:synchronized(自定义成员变量名){需要进行数据同步的代码块};
列如:synchronized (objVariable) { //方案1 同步代码块
if (ticket > 0) {
try {
Thread.currentThread().sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + “当前正在售出第” + this.ticket + “张票”);
ticket–;
}
}

解决方案2/ 同步静态方法

步骤1/ 声明一个同步数据之静态方法
格式:

权限修饰符 static synchronized 返回值类型 方法名(参数可选){
               //需要被同步处理的方法体
            }

列如:public static synchronized void staticMethod(){}
步骤2/ 调用
格式:方法名(参数可选);
列如:staticMethod();

解决方案3/ 同步动态方法

步骤1/ 声明一个同步数据之动态方法
格式:

权限修饰符 synchronized 返回值类型 方法名(参数可选){
            //需要被同步处理的方法体
        }

列如:public synchronized void dyMethod(){}
步骤2/ 调用
格式:方法名(参数可选);
列如:dyMethod();

4.Thread currentThread(重要)

以调用了该代码’Thread currentThread()'的线程为指针,可以通过该代码对当前[调用了该代码]的线程进行操作,即:如果是在main方法内调用了它,则对main线程进行操作.即:谁调用我,我就指向谁
格式:Thread.currentThread() / Thread.currentThread().子方法();
列如1:System.out.println(Thread.currentThread());//返回当前线程的名字
列如2:Thread.currentThread().setName(“中国”); //为执行这行代码的线程设置线程名字.
还有其它不少方法,这里不一一列出.

5.Object最高父类的多线程方法(重要):

在JAVA体系中,当无直接父类时,自动继承最高父类Object.
而Object则有 wait方法–等待线程 notify方法–唤醒线程 notifyAll方法–唤醒所有线程

方法

1/
线程睡眠.
格式:wait();
在这里插入图片描述
当程序运行到了其’wait()'调用处,那么就会让调用了其所处位置之方法的超线程暂停运行,称之为’睡眠.此时只有当其它线程的代码用到了’notifyAll’方法,才会让其恢复运行状态,
在这里插入图片描述
注意:'wait’等待方用于其所处方法为’synchronized’锁的情况下使用,如果其所处方法并非’synchronized’锁,那将会报错.
因为’synchronized’就是让子线程去’排队’,当线程A全部做完后才让线程B做.而’wait’等待方法则是让当前的子线程(如线程A)处于停止等待状态,让其它线程(如线程B)做完一次后,再轮到自己做.
套用在"抽奖案例",就是子线程A调用一次方法就用wait休眠,然后让子线程B调用一次.子线程B调用一次后就休眠从而让子线程A调.
2/ 唤醒线程
格式:notfy();
(?)**唤醒自己,也就是唤醒当前运行了此行代码的线程,**逻辑同wait的睡眠一样,哪个线程执行了它就对哪个线程操作

3/ 唤醒所有线程
格式:notfyAll
唤醒除自己以外的所有其它处于睡眠状态的线程
在这里插入图片描述
注意:套用在’抽奖案例’这样的相互地’你一次,我一次’排队处理数据和相互唤醒案例里头,唤醒必须要在’wait’等待的前面,不然线程A和B就只能运行一次.之后就会各自处于’等待’状态.
而’notifyALL’唤醒在前面,'wait’等待在后面,那么根据’多线程’的特性,也不过是启动了其它的线程而已,
并不会意味着本线程就会终止运行,既然不会终止运行,那么在’notifyAll’唤醒了其它线程后,本线程就可以运行’wait’来处于等待状态,接着再等待其它线程唤醒,如此循环往复下去.

多线程之Thread演示

public class MianTest {
    public static void main(String[] args) {
        //创建Thread线程继承类

        ThreadClass threadClassObj = new ThreadClass();//线程1
        ThreadClass threadClassObjTwo = new ThreadClass();//线程2

        //创建Thread线程继承类,并通过有参构造直接为线程命名
        ThreadClass threadClassObjThree = new ThreadClass("世界大同");
        //获取线程名
        System.out.println(threadClassObjThree.getName());

        //设置线程名
         threadClassObj.setName("中华有为 - 华为");
         threadClassObjTwo.setName("华夏归真 - 华真");

         //获取线程优先级 对象名.getPriority();
        threadClassObj.getPriority();
        threadClassObjTwo.getPriority();
        threadClassObjThree.getPriority();

        //设置线程优先级 对象名.setPriority();
        threadClassObj.setPriority(5);



        //以Strat方法开启线程,即:通过Strat方法以多线程方式来,接调地用run方法,而不是直接调用run方法.
        threadClassObj.start();
        System.out.println("你好!打印输出!");
        //如果main方法中有[调用线程的代码(start)],跟main方法的代码[列如:System.out.println("你好!打印输出!")]是紧紧挨在一块的,
        // 且main方法的代码就在[调用线程的代码(strat)]下面,那么可能会先执行main方法的代码.
        threadClassObjTwo.start();
        threadClassObjThree.start();


        //Thread currentThread() 返回当前调用了该方法'Thread.currentThread()'的线程名字.如果是Main方法调用它,则返回Main;如果是其它方法调用它,则返回其它方法的名字.
        System.out.println(Thread.currentThread());
        Thread.currentThread().setName("中国"); //为执行这行代码的线程设置线程名字.

        System.out.println("=======================");
        joinRun();
        System.out.println("=======================");
        daemonRun();






    }

    //ThreadJoin演示   对象名.Join(); 等待线程死亡
    //先让某个子线程的代码(即Run方法)全部执行完毕后,再去运行其它代码或运行其它子程序.
    public static void joinRun(){
        ThreadJoin threadJoinObj = new ThreadJoin();
        ThreadJoin threadJoinObjTwo = new ThreadJoin();
        ThreadJoin threadJoinObjThree = new ThreadJoin();
        threadJoinObj.setName("刘备");
        threadJoinObjTwo.setName("张飞");
        threadJoinObjThree.setName("关羽");

        //启动线程
        threadJoinObj.start();
        //让子线程'threadJoinObj'调用Join方法,以让其该子线程的代码全部执行完毕后再去做其它事情.
        try {
            threadJoinObj.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        threadJoinObjTwo.start();
        threadJoinObjThree.start();
    }

    //线程守护演示  对象名.setDaemon();  守护线程
    // 在子线程运行期间,如果主线程(即调用了子线程的方法,如'Main')的代码全都执行完了,就停止当前所有在运行的子线程.
    public static void daemonRun(){

        ThreadDaemon threadDaemonObj = new ThreadDaemon();
        ThreadDaemon threadDaemonObjTwo = new ThreadDaemon();

        threadDaemonObj.setName("线程守护 - 关于");
        threadDaemonObjTwo.setName("线程守护 - 张飞");
        threadDaemonObj.setDaemon(true);
        threadDaemonObjTwo.setDaemon(true);
        threadDaemonObj.start();
        threadDaemonObjTwo.start();

        for (int i = 0; i < 100; i++) {
            System.out.println("主线程demonRun的数值为:"+i);
        }
    }

    //暂停线程 Run方法内{sleep(毫秒数)}
    //让当前线程在其Run方法内暂停一段时间(以毫秒为单位).再去执行接下来的代码.
    public static void sleepRun() throws InterruptedException {
        ThreadDaemon threadDaemonObj = new ThreadDaemon();
        ThreadDaemon threadDaemonObjTwo = new ThreadDaemon();
        threadDaemonObj.setName("暂停线程 - 关于");
        threadDaemonObjTwo.setName("暂停线程 - 张飞");
        threadDaemonObj.start();
        threadDaemonObjTwo.start();
    }


}

多线程之Runnable演示

main主方法

public class MainClass {
    public static void main(String[] args) {
        RunnableImplements runnableImplementsObj = new RunnableImplements();
        Thread threadObj = new Thread(runnableImplementsObj);
        Thread threadObjTwo = new Thread(runnableImplementsObj);


    }
}

线程实现类


public class RunnableImplements implements Runnable {
    @Override
    public void run() {
        System.out.println("🐏");
    }
}

数据同步演示

买票案例
Main主方法

public class BuyTicket {
    public static void main(String[] args) {
        BuyTicketImplements buyTicketImplements = new BuyTicketImplements();
        Thread threadObj = new Thread(buyTicketImplements,"窗口1");
        Thread threadObjTwo = new Thread(buyTicketImplements,"窗口2");
        Thread threadObjThree = new Thread(buyTicketImplements,"窗口3");

        //启动窗口
        threadObj.start();
        threadObjTwo.start();
        threadObjThree.start();
    }

线程实现类

public class BuyTicketImplements implements Runnable{
    private static int ticket = 100;
    private Object objVariable = new Object();
    @Override
    public void run() {
        while (true){
                    synchronized (objVariable) {  //方案1 同步代码块
                        if (ticket > 0) {
                            try {
                                Thread.currentThread().sleep(100);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            System.out.println(Thread.currentThread().getName() + "当前正在售出第" + this.ticket + "张票");
                            ticket--;
                        }
                    }
                 //方案2 同步动态方法
           dyMethod();
                    //方案3 同步静态方法
            staticMethod();

        }


    }
    //方案2 同步静态方法
    public static synchronized void staticMethod(){
        if (ticket > 0) {
            try {
                Thread.currentThread().sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "当前正在售出第" + ticket + "张票");
            ticket--;
        }

    }
    //方案3 同步动态方法
    public synchronized void dyMethod(){
        if (this.ticket > 0) {
            try {
                Thread.currentThread().sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "当前正在售出第" + this.ticket + "张票");
            this.ticket--;
        }

    }


}

唤醒/睡眠演示案例

奶箱案例
在这里插入图片描述

分别有4个类
1/Test.java 测试类
2/Box.java 奶箱类
3/Customer.java 消费者类
4/Producer.java 生产者类

1/Test.java 测试类


public class Test {
    public static void main(String[] args) {
        Box boxObj = new Box();
        //创建生产者对象 把奶箱当作参数传入
        Producer producerObj = new Producer(boxObj);
        //创建消费者对象 把奶箱当作参数传入
        Customer customerObj = new Customer(boxObj);
        //创建消费者和生产者的线程对象
        Thread producerThreadObj = new Thread(producerObj);
        Thread customerThreadObj =  new Thread(customerObj);
        producerThreadObj.start();
        customerThreadObj.start();
    }
}

2/Box.java 奶箱类

public class Box {
    private int boxNum;
    private boolean boxInfo = false;
    public synchronized void boxPut(int boxNum) {
        if (boxInfo) { //如果boxInfo 为true
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.boxNum = boxNum;
        System.out.println("生产第" + this.boxNum + "瓶牛奶");
        boxInfo = true;
        notifyAll();
    }

    public synchronized void boxGet() {
        if (boxInfo!=true) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("用户拿到第" + this.boxNum + "瓶牛奶");
        this.boxInfo = false;
        notifyAll();
    }
}

3/Customer.java 消费者类

public class Customer implements Runnable {
    private Box boxParamObj;
    public Customer(Box boxObj) {
      this.boxParamObj = boxObj;
    }
    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {
            boxParamObj.boxGet();
        }
      }
    }

4/Producer.java 生产者类

public class Producer implements Runnable {
    private Box boxObjOfParam;
    public Producer(Box boxObj) {
        this.boxObjOfParam = boxObj;
    }
    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {
            boxObjOfParam.boxPut(i);
        }
    }
}

重要!自创抽奖案例!

【编号2004】有一个抽奖池,里面存放了一些固定金额的奖金
int[] prizePool = {100, 200, 300, 400, 500, 600, 700, 800, 900, 1000};
此时,有两个人A和B轮流在奖箱中随机抽奖(即A抽一次–>B抽一次–>A抽一次–>B抽一次…)
,直到所有的奖金都被抽出,每次抽奖耗时1秒。请编写程序,使用两个线程模拟两人的抽奖过程,输出结果如下(金额的顺序随机):
A抽出奖金600
B抽出奖金1000
A抽出奖金900
B抽出奖金300
A抽出奖金500
B抽出奖金200
A抽出奖金700
B抽出奖金100
A抽出奖金800
B抽出奖金400

主方法类Prize.java

public class Prize {
    public static void main(String[] args) {
        Box boxObj = new Box();
        PersonA psA = new PersonA(boxObj);
        PersonB psB = new PersonB(boxObj);
        Thread tOne = new Thread(psA);
        Thread tTwo = new Thread(psB);
        tOne.setName("A");
        tTwo.setName("B");
        tOne.start();
        tTwo.start();



    }
}

线程A类PersonA.java

package code.study.school0104prize;

public class PersonA implements Runnable {
    private Box boxvariable;
    private int count;
    public PersonA(Box boxObj) {
        this.boxvariable = boxObj;
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            boxvariable.method();
        }
    }
}

线程B类PersonB.java

package code.study.school0104prize;

public class PersonB implements Runnable{
    private Box boxvariable;
    private int count;
    public PersonB(Box boxObj) {
        this.boxvariable = boxObj;
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            boxvariable.method();

        }
    }
}

抽奖箱类Box.java

package code.study.school0104prize;

import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.Currency;
import java.util.Random;
/*
纯手工打造!
 */

public class Box {
    private int[] prizePool = {100, 200, 300, 400, 500, 600, 700, 800, 900, 1000};
    private int count = 0;
    private ArrayList<Integer> arr;
    private boolean okAndNo = true;

    public Box() {
        arr = new ArrayList<>();
        for (int i = 0; i < prizePool.length; i++) {
            arr.add(prizePool[i]);
        }
    }

    public synchronized void method() {



        //注意:'wait'等待方用于其所处方法为'synchronized'锁的情况下使用,如果其所处方法并非'synchronized'锁,那将会报错.
        //因为'synchronized'就是让子线程去'排队',当线程A全部做完后才让线程B做.而'wait'等待方法则是让当前的子线程(如线程A)处于停止等待状态,让其它线程(如线程B)做完一次后,再去做.
        //套用在这里,就是子线程A调用一次方法就用wait休眠,然后让子线程B调用一次.子线程B调用一次后就休眠从而让子线程A调.



        Random r = new Random();
        int num;
        Integer deleteNum = null;
        if (arr.size() > 0) {
            num = r.nextInt(arr.size());
            deleteNum = arr.remove(num);
       //     System.out.println("集合长度"+arr.size());
        }

        System.out.println(Thread.currentThread().getName() + "抽中了" + deleteNum + "元");
        try {
            notifyAll();//唤醒  唤醒必须要在'wait'等待的前面,不然线程A和B就只能运行一次.之后就会各自处于'等待'状态.
                        //而'notifyALL'唤醒在前面,'wait'等待在后面,那么根据'多线程'的特性,也不过是启动了其它的线程而已,
            // 并不会意味着本线程就会终止运行,既然不会终止运行,那么在'notifyAll'唤醒了其它线程后,本线程就可以运行'wait'来处于等待状态,接着再等待其它线程唤醒,如此循环往复下去.

            if (arr.size()!=0){//如果集合长度为0,即:集合没有元素,则视为抽奖完毕,什么都不做线程就可以结束了.如果在抽奖完毕的情况下仍然去用'wait'等待,那线程将无法结束.
                                //因此此处
                wait();
            }

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

        //     System.out.println(Thread.currentThread().getName() + " " + count);
    }


}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值