多线程是什么(上)

在一切开始之前,我们需要明白什么是进程和线程。

化繁为简:一个内存里运行的应用程序,并且本身都有个独立内存空间就是进程

把进程当作意大利,当你坐飞机进入并且降落到进程这个国度的空间,你可能会看到许多执行路径(线程)。这里条条大路通罗马,可以自由切换你要走的线程,也可以分身同时走不同的线程(并发执行)。当然某条路径/线程也可以拥有其它的小径(路径/线程)。

但是,当你的脑子在想象这些线程的时候,同时模拟出所有路径的精确走向是比较困难的。所以调度方式这个概念就出来了。

分时调度:线程们轮流使用 CPU ,平均分配每个线程占用 CPU 的时间。

抢占式调度:优先级高的线程先用CPU,如相同优先级猜拳(随机)决定顺序。

所以,假如电脑一年是我们的一秒钟,那抢占调度模式在多个线程间几天一换对我们就是高速的切换,这让我们产生一切在同时发生的错觉。总而言之,前后者时间的总量是没有变化的,但是效率调高了(我更觉得是整体感观质量提升了)。

再讲两个基础概念区别:

同步:排队执行 , 效率低但数据安全。 异步:同时执行 , 效率高但数据不安全。

并发:指多件事在同一个时间段内发生。 并行:多件事同时发生。

线程安全:

举一个最简单的例子:

因为count不是局部变量,假设俩线程A和B同时进来,那count都是0变成1,但是事实上应该有序地去0+1,1+1,2+1 ...... 这样自增。

Integer count = 0; // 不是局部变量

public void getCount() {

        count ++;

        System.out.println(count);

}

所以,下面延伸出对于线程安全的概念:(上才艺)

注意:建议看一下thread的初始化博客甚至是源代码或者API,我也会后面专门补充基础的Thread类调用的常用方法。最好将下列代码放进去IDEA或者eclipse里去慢慢耐心地一行行感受与理解消化。都很基础但化繁为简。​​​​​​​ 

import com.sun.source.tree.SynchronizedTree;

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

/**
 * 多线程不安全问题
 * 排队卖票案例
 * 下面solution 1 和 2 是隐式锁 // 代表修饰符层面的解决方案,JVM层面的锁,源码的monitorexit来负责退出锁
 * 3 显示锁 精准唤醒thread,可中断,性能高,手动开和放,API层面的锁 (对象.lock OR unlock)需要手动调试
 * 123都是默认非公平锁,也就是大家抢
 * 显示锁可以在构造方法(设置为true)变成公平锁,也就是大家排队先到先得
 */
public class Security {
    public static void main(String[] args) {
        //solution1,同步代码块 synchronized
        //格式:synchronized(和你的锁对象)
        //check锁对象有没有被某个线程标记了,有的话其它等待,然后等那个自动解锁后
        //其它争夺这个锁,抢到执行line47(synchronized (o) {//排队,就像试衣间一把锁轮流开锁解锁这样。。})那行下面的👇代码,以此类推。。
        Runnable run = new Ticket();
        new Thread(run).start(); //-> run对象 is the lock锁
        new Thread(run).start(); //-> run对象 is the lock锁
        new Thread(run).start(); //-> run对象 is the lock锁

        //solution2
        //请跳到👇的 Ticket2 类
        Runnable run2 = new Ticket2();
        new Thread(run2).start(); //-> run2对象 is the lock锁
        new Thread(run2).start(); //-> run2对象 is the lock锁
        new Thread(run2).start(); //-> run2对象 is the lock锁
    }
}

class Ticket implements Runnable {
    private int count = 10;
    private Object o = new Object(); //一把锁

// Solution3: 自己创造一个锁(显示锁),同一把
    private Lock l = new ReentrantLock();
    private Lock l2 = new ReentrantLock(true);//表示为公平锁

    @Override
    public void run() {
        while (true) {
            //l.lock();// sol3:
            //同步代码块
            synchronized (o) {//排队,就像试衣间一把锁轮流开锁解锁这样
                if (count > 0) {
                    //卖票
                    System.out.println("正在准备卖票");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    count--;
                    System.out.println("卖票成功 余票:" + count);
                }
            l.unlock(); // sol3解锁
            }
        }
    }
}

//solution2:同步方法(其实就是语法的改变)
class Ticket2 implements Runnable {
    private int count = 10;
    // 因为不需要语法synchronized代码块去定一个锁对象的格式,直接就用synchronized方法也可以做到锁的这个功能
    @Override
    /**
     * task
     */
    public void run() {
        // 如果有另一个锁,其它锁用不了,因为之前这个锁已经锁上了
//        synchronized (this){
//            //...
//        }
        while (true) {
            boolean tag = sale();
            if (!tag) {
                break;
            }
        }
    }

    /**
     * 同理,你再创建一个n个锁
     * 只有一个能实行,其它锁都不行
     * 相当于n个房间只有一个door
     * 那你肯定是第一个进去锁了,其它用也用不了
     * @return
     */
    //public synchronized boolean sale2() {return true;}

    /**
     * solution2 同步方法
     * @return
     */
    public synchronized boolean sale() { //boolean是为了让while能够break 不至于死循环
        //排队,就像试衣间一把锁轮流开锁解锁这样
        //写英文了,锻炼一下哈
//        this - > run2 (in this case) this is the lock
//        if this method is static, then the lock itself is the class (Ticket2) which implements this method
        if (count > 0) {
            //卖票
            System.out.println("正在准备卖票");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            count--;
            System.out.println(Thread.currentThread().getName() + "卖票成功 余票:" + count);
            return true; // 票还有
        }
        //票卖完了
        return false;
    }
}

好了就先粗略地写到这,写多了你们看久也烦。下期除了刚刚说的要说一下Thread线程和自定义Thread,开始进入线程池和lambda再去简化这个匿名内部类的知识点了。

感谢阅读~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值