[Java多线程]线程创建的三种方式,线程的互斥,线程的同步

前言

多线程:指的是这个程序(一个进程)运行时产生了不止一个线程

并行和并发:

  • 并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。
  • 并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。并发往往在场景中有公用的资源,那么针对这个公用的资源往往产生瓶颈,我们会用TPS或者QPS来反应这个系统的处理能力。

线程创建

最基础的主要分为两种方法

  • 继承Thread类
  • 实现Runnable接口

创建Thread类的子类

用这种方法生成新线程,可以按以下步骤进行:

  1. 定义Thread类的子类
class MyThread extends Thread
  1. 在子类中重写run方法
  2. 实例化MyThread,调用start方法开始运行

实例:

public class test1 {
    
    
    public static void main(String[] argv){
    
    
        testThread t1 = new testThread();
        t1.start();
        while(true){
    
    
            try {
    
    
                Thread.sleep(1);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println("Thread 2");
        }
    }


}

class testThread extends Thread{
    
    
    public void run(){
    
    
        while(true){
    
    
            try {
    
    
                sleep(1);
            }catch (Exception e){
    
    
                System.out.println(e.getMessage());
            }
            System.out.println("Thread 1");
        }

    }
}

在这里插入图片描述

实现Runnable接口

  1. 定义一个类MyThread实现Runnable接口
  2. 实现他的唯一的抽象函数run()
  3. 实例化MyThead
  4. 再实例化Thread(MyThread,name)
  5. 调用start方法
// 用runnable构造线程

public class Book2 {
    
    
    public static void main(String[] argv){
    
    
        System.out.println("开始");
        MyThread2 m1,m2,m3;
        Thread t1,t2,t3;
        m1 = new MyThread2(2,70);
        m2 = new MyThread2(3,70);
        m3 = new MyThread2(5,70);
        t1 = new Thread(m1,"A");
        t2 = new Thread(m2,"B");
        t3 = new Thread(m3,"C");
        t1.start();
        t2.start();
        t3.start();
        System.out.println("当前共有"+Thread.activeCount()+"个线程");
        System.out.println("Main结束");
    }
}

class MyThread2 implements Runnable{
    
    
    private int n,max;

    public MyThread2(int n1,int max1){
    
    
        n = n1;
        max = max1;
    }
    @Override
    public void run() {
    
    
        int i = 1;
        while(n*i<=max){
    
    
            System.out.println(n*i+" ");
            i++;
        }
        System.out.println(Thread.currentThread().getName()+"结束");
    }
}

其他构造线程的方式

这里还有一个更加灵活的方式去实现线程的构造
主要是思路就是在实现Runnable的时候,在析构方法中就创建Thread的对象,再添加一个start方法
这样在mian方法里面,就可以直接实例化MyThread,直接调用start方法,不需要像第二个方法一样再去创建一个Thread对象

  1. 创建MyThread类实现Runnable接口
  2. 在析构方法中实例化Thread对象
  3. 创建start方法
  4. 在mian中实例化MyThread,直接调用MyThread类的start开启线程

实例:


public class test2 {
    
    
    public static void main(String[] argv){
    
    
        System.out.println("开始");
        MyThread2 m1,m2;
        m1 = new MyThread2(1,"第一个");
        m2 = new MyThread2(3,"第二个");
        m1.start();
        m2.start();

        System.out.println("当前共有"+Thread.activeCount()+"个线程");
        System.out.println("Main结束");
    }
}

class MyThread2 implements Runnable{
    
    
    private int n;
    private Thread t;

    public MyThread2(int n1,String name){
    
    
        n = n1;
        t = new Thread(this,name);
    }
    //这里不是对Thread类的start方法的重写
    public void start(){
    
    
        t.start();
    }

    @Override
    public void run() {
    
    
        System.out.println(n);
        System.out.println(Thread.currentThread().getName()+"结束");
    }
}


线程的互斥

  • 由于基本类型难于附加更多机制,因此Java将资源
    限定为引用型对象,并为每一对象自动配备一把锁。
  • 锁初始时,是处于打开状态。
  • synchronized标记的原子代码段在访问资源前,会自动检测资源对象持有的锁是否处于打开状态。如果是,则占用并同时将锁置为锁闭状态。并在该代码段执行完牛后,将锁的状态值设为打开状态。如果自动检测到资源对象持有的锁处于锁闭状态,则持有该代码段的线程因等待资源占用而进入阻塞

synchronized标记的两种方法

  1. 插入代码块
synchronized(竞争临界资源){
    
    原子操作}
  1. 作为函数的声明
public synchronized void f(){
    
    ...}
等价于
public void f(){
    
    synchronized (this){
    
    ...} }

实例

共享打字机

public class Work1 {
    
    
    public static void main(String[] args){
    
    
        String flag = "flag";
        String[] work1 = {
    
    "1","3","5","7","9"};
        String[] work2 = {
    
    "2","4","6","8","10"};
        String[] work3 = {
    
    "3","6","9","12"};
        Work_1 w1 = new Work_1(flag,"甲",work1);
        Work_1 w2 = new Work_1(flag,"乙",work2);
        Work_1 w3 = new Work_1(flag,"丙",work3);
        w1.start();
        w2.start();
        w3.start();

    }
}

class Work_1 extends Thread{
    
    
    private String flag;
    private String name;
    private String[] content;
    public Work_1(String f,String n,String[] c){
    
    
        flag = f;
        name = n;
        content = c;
    }
    public void run(){
    
    
        synchronized (flag){
    
    
            System.out.print(name+":");
            for(String x:content) System.out.print(" "+x);
            System.out.println();
        }
    }
}

多窗口售票

import java.util.Arrays;

public class Work3 {
    
    
    public static void main(String[] args){
    
    
        int[] data = new int[100];
        for(int i = 0;i<100;i++){
    
    
            data[i] = i;
        }
        Pos pos = new Pos(data.length);
        Buy w1 = new Buy(pos,data,"一号");
        Buy w2 = new Buy(pos,data,"二号");
        Buy w3 = new Buy(pos,data,"三号");
        Buy w4 = new Buy(pos,data,"四号");
        w1.start();
        w2.start();
        w3.start();
        w4.start();

    }
}

class Buy extends Thread{
    
    
    private int data[];
    private int over[];
    private String name;
    private Pos pos;

    public Buy(Pos p,int[] d,String n){
    
    
        pos = p;
        data = d;
        name = n;
        over = new int[data.length];
    }

    public void run(){
    
    
        while (true){
    
    
            try {
    
    
                sleep(10);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            synchronized (pos){
    
    
                int x = pos.getPos();
                if(x==-1) return;
                int tic = data[x];
                System.out.println(name+"窗口售出"+tic+"号票");
            }
        }
    }

}

class Pos{
    
    
    private int max,pos;
    public Pos(int m){
    
    
        max = m;
        pos=0;
    }
    public int getPos(){
    
    
        if (pos==max) return -1;
        int x = pos; pos++; return x;
    }
}

线程的同步

对于线程的同步思想,可以用一个例子来理解

一群人去登山,大家分头走,走到集合点的时候就先等待着,等到所有人都到达了集合点后,再进行下一步的行动

这里体现了同步的策略:互斥+通信

互斥,就是线程之间有着临界资源,这个资源的状态决定线程是否能执行
通信,就是对临界资源的wait(),notify(),notifyAll()的调用,决定线程走到这里是阻塞还是放行

再看这几个函数

  • wait()函数,让该线程阻塞住,一般是来等待所有线程的同步
  • notify(),notifyAll(),唤醒线程,若有多个线程,则使用notifyAll去唤醒所有线程
  • join(),在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。这个函数可以用于main方法中,线程.join()后,main线程会阻塞,当所有线程结束,main线程会被唤醒

实例1

有三个数组如下

{1,3,5,7,9}
{2,4,6,8,0}
{a,b,c,d,e}

要求通过三个线程,分别按顺序输出内容,如下所示

12a 24b 36c 。。。。

这就要求了,三个线程在运行的时候,输出完第一个元素,要先等着,等到所有线程都输出完了第一个元素,再放行去输出第二个元素

可以这样去实现

思想是利用一个count,每次输出加一,通过计算count%3来判断是否输完一轮

public class Work1 {
    
    
    static int count = 1;

    public static void main(String[] args){
    
    
        String[] a = {
    
    "1","3","5","7","9"};
        String[] b = {
    
    "2","4","6","8","0"};
        String[] c = {
    
    "a","b","c","d","e"};
        Source out = new Source();
        OutPut out1 = new OutPut(out,a);
        OutPut out2 = new OutPut(out,b);
        OutPut out3 = new OutPut(out,c);
        out1.start();
        out2.start();
        out3.start();
        try {
    
    
            out1.join();
            out2.join();
            out3.join();
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("结束");
    }
}


class Source {
    
    
    public void outStr(String data){
    
    
        System.out.print(data);
        if (Work1.count % 3 == 0) {
    
    
            Work1.count++;
            notifyAll();
        }else{
    
    
            try {
    
    
                Work1.count++;
                wait();
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        }
    }
}


class OutPut extends Thread {
    
    
    private Source sou;
    private String[] data;
    private final int turn = 5;
    public OutPut(Source s,String[] a){
    
    
        sou = s;
        data = a;
    }

    public void run(){
    
    
        for(int i = 0;i<turn;i++){
    
    
            synchronized(sou){
    
    
                sou.outStr(data[i]);
            }
        }
    }

}

在这里插入图片描述

也可以这样实现他

猜你喜欢

转载自blog.csdn.net/m0_51078229/article/details/121485006