多线程加强

本文介绍了线程的概念及其与进程的区别,通过两个实例演示了多线程环境下如何解决资源竞争问题,包括使用对象锁、同步方法及等待通知机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第一个问题,什么是线程?
线程是程序执行流的最小单元。
第二个问题,线程和进程的区别
进程是资源分配的基本单位,与进程相对应,线程与资源分配无关,它属于某一个进程,并与进程内的其他线程一起共享进程的资源。

我的理解呢,就好比进程是一个部门,线程是部门里面的工人。

========

线程例子一

★带互斥的共享栈
多线程互斥共享“栈”资源
首先写一个 栈

package cn.hncu.thread.sharedStack2;

public class MyStack {
    // 假设 栈 为6个字符大小的
    private char [] data = new char [6];
     // 指针 ,当要往栈里面添加或者 取出字符时候都需要这个指针
    private int idx =0; 

    // 向栈里面存字符的函数
    public  void Push(char c){
        data[idx] = c;
        idx ++;
        System.out.println("存一个字符"+c);
    }
    // 向栈里面取字符的函数
    public char Pop(){
        idx--;
        System.out.println("存一个字符"+data[idx]);
        return data[idx];
    }

}

然后 一个存入字符的线程 一个取出字符的线程

package cn.hncu.thread.sharedStack2;

// 线程的额写法一:继承Thread  然后写run方法,通过多态来调用我们的run方法
// 线程的额写法二:实现Runnable接口
public class PushThread extends Thread{
    private MyStack Stack = null;
    public PushThread(MyStack Stack){
        this.Stack = Stack;
    }
    @Override
    public void run(){
        for (int i=97; i <103; i++) {
            Stack.Push( (char)i );
        }
    }

}
package cn.hncu.thread.sharedStack2;

import cn.hncu.thread.sharedStack2.MyStack;
public class PopThread extends Thread{
    private MyStack ms=null;
    public PopThread(MyStack ms) {
        this.ms = ms;
    }

    @Override
    public void run() {
        for(int i=97;i<103;i++){
            ms.Pop();
        }
    }

}

结果,挂了
这里写图片描述
首先讲一下运行过程吧。
一开始Java虚拟机运行的是main函数,运行完t1.start()后,就有两个线程在抢资源,当t1抢到就运行ti里面的函数,我们这里是Push,存入字符。值得注意的是t1抢到并不代表t1一定会全部运行完。可能会是 运行一部分,cpu的资源就被其他的线程抢过去了,main 也相当于一个线程,main 当然是运行下一句 t2.start().之后就是t1,t2两个线程抢资源。当t2,比t1运行的多时,就可能出现数组下表越界。 那么怎么办呢?
加对象锁 谁拿到锁 谁就能运行。代码如下

把两个方法锁了 都是 锁this对象

package cn.hncu.thread.sharedStack2;

public class MyStack {
    // 假设 栈 为6个字符大小的
    private char [] data = new char [6];
     // 指针 ,当要往栈里面添加或者 取出字符时候都需要这个指针
    private int idx =0; 

    // 向栈里面存字符的函数
    public  void Push(char c){
        // 对象锁只能锁 对象。
        synchronized (this) {
            data[idx] = c;
            idx++;
            System.out.println("存一个字符" + c);
        }
    }
    // 向栈里面取字符的函数
    // 当锁方法时,就相当于锁的是 方法所在的对象 就相当于前面的this
    public synchronized char Pop(){
        idx--;
        System.out.println("取一个字符"+data[idx]);
        return data[idx];
    }

}

结果还是挂了
这里写图片描述
因为当‘取‘的线程 运行比’存‘的线程快时,还没存就取了,所以就又挂了。 那怎么办?
取的太快了就等一下 加了两个方法 this.wait(); this.notify();

package cn.hncu.thread.sharedStack2;

public class MyStack {
    // 假设 栈 为6个字符大小的
    private char [] data = new char [6];
     // 指针 ,当要往栈里面添加或者 取出字符时候都需要这个指针
    private int idx =0; 

    // 向栈里面存字符的函数
    public  void Push(char c){
        // 对象锁只能锁 对象。
        synchronized (this) {
            data[idx] = c;
            idx++;
            System.out.println("存一个字符" + c);

            // 这边叫一下
            this.notify();
        }
    }
    // 向栈里面取字符的函数
    // 当锁方法时,就相当于锁的是 方法所在的对象 就相当于前面的this
    public synchronized char Pop(){
        if(idx<0){
            try {
                // 这边等一下
                this.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        idx--;
        System.out.println("取一个字符"+data[idx]);
        return data[idx];
    }

}

这个时候就好了
这里写图片描述
**说一下 Thread.sleep(x) 和 this.wait(),的区别
两个都会释放cpu 资源,sleep 不会释放锁,wait 会释放锁。**

线程例子二

多线程互斥共享“基本数据类型数据”资源
多个窗口买票,
票200张1~200号为整数类型,多个窗口同时买,肯定不能 甲窗口卖了 1号票 乙窗口又来卖1号票 所以就要给票加锁,票是正行,而我们的锁只能锁对象怎么办呢? 两个方法
v1 版本 把 票声明为静态的,同时声明一个静态的对象,把那个静态的对象锁了,就相当于把类模板上面的票锁了:

package cn.hncu.thread.ticket.v1;

public class TicketWin implements Runnable{
    private String name=null;

    //基本数据类型变量不能当对象锁,我们可以造一个与它平行的对象来代替它当锁
    private static int num=200;
    private static Object obj=new Object();

    public TicketWin(String name) {
        this.name=name;
    }

    @Override
    public void run() {
        synchronized ( obj ) { //对象锁 ----注意,这里不能用this
            while (true) {
                if (num == 0) {
                    break;
                }
                System.out.println(num--);
            }
        }
    }

}

调用和结果

package cn.hncu.thread.ticket.v1;

public class SaleTicket {
    public static void main(String[] args) {
        Thread t1 = new Thread( new TicketWin("窗口1") );
        Thread t2 = new Thread( new TicketWin("窗口2") );
        Thread t3 = new Thread( new TicketWin("窗口3") );
        Thread t4 = new Thread( new TicketWin("窗口4") );

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

    }
}

这里写图片描述

v2 版本 把票所在的那个内的对象锁了,所有线程调用时,用同一个对象。就像书店系统里面的mainframe 一样:

package cn.hncu.thread.ticket.v2;

public class TicketWin implements Runnable{
    private int num=200;
    //private Object obj=new Object();//可以造一个平行的对象来代替,当作锁。但非静态变量当锁时,通常直接用this即可

    @Override
    public void run() {
        synchronized (this) {//obj可以,但用this更简便
            while (true) {
                if (num == 0) {
                    break;
                }
                System.out.println(num--);
            }
        }
    }
package cn.hncu.thread.ticket.v2;

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

        TicketWin tickets = new TicketWin();

        Thread t1 = new Thread( tickets ); 
        Thread t2 = new Thread( tickets );
        Thread t3 = new Thread( tickets );
        Thread t4 = new Thread( tickets );

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

结果也是没有问题的
总结一下:
对于基本数据类型上锁,的两种方法。1.把基本数据类型声明为静态的放到类模板上。再在类模板上声明一个对象,把这个对象锁了就好。2. 就是所有的线程调用同一个对象,把这个对象锁了就行了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值