高并发与多线程学习笔记一

高并发与多线程目录



前言

高并发与多线程学习记录笔记


一、线程、进程的概念

线程:线程是进程的一个实体,是进程的一条执行路径。线程是CPU独立运行和独立调度的基本单位。
进程:系统中正在运行的一个应用程序,程序一旦运行就是进程。进程是系统进行资源分配的独立实体, 且每个进程拥有独立的地址空间。

二、启动线程的方式

1.严格意义上来说线程的创建只有两种方式

① 从Thread类继承,继承之后重写run方法。

② 定义一个类去实现Runnable接口,重写run方法

③ 为第二种的一种变形,使用lambda表达式的方式

代码如下(示例):

public class X02_HowToCreateThread {

    //01 继承Thread
    static class MyThread extends Thread{
        @Override
        public void run(){
            System.out.println("How Create Thread 01");
        }
    }

    //02 实现Runnable接口
    static class MyRun implements Runnable{
        @Override
        public void run(){
            System.out.println("How Create Thread 02");
        }
    }

    public static void main(String[] args) {
        new MyThread().start();
        new Thread(new MyRun()).start();
        
        //03 lambda表达式
        new Thread(() -> {
            System.out.println("Lambda");
        }).start();
    }
}

④ 线程池方式,但实际上线程池启动方式也是上面的两种之一。

代码还未补充

2.Thread的几种方法

sleep:让线程睡眠一段时间,到了规定的时间自动复活,sleep完也会回到就绪。

(java.util.current下的TimeUnit)

yield:让线程回到等待队列里(让出一下cpu)。

join: 用来等待另外一个线程的结束。

getstate:获得一个线程的状态

public class X03_Sleep_Yield_Join {


    /**
     * 测试sleep方法
     */
    static void testSleep(){
        new Thread(() -> {
            for (int i=0;i<100;i++){
                System.out.println("A" + i);
                try {
                    Thread.sleep(500);
//                    TimeUnit.MILLISECONDS.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    /**
     * 测试yield方法
     */
    public static class TestYield extends Thread{


        public TestYield(String name){
            super(name);
        }

        @Override
        public void run(){
            for(int i=0; i<100; i++) {
                System.out.println(this.getName() +" " + i);
                if(i%10 == 0) {
                    Thread.yield();
                }
            }
        }
    }

    /**
     * 测试join方法
     */
    static void testJoin(){
        Thread t1 = new Thread(() -> {
            for(int i=0;i<100;i++){
                System.out.println("A" + i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread t2 = new Thread(() -> {

            try {
                t1.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            for (int i=0;i<100;i++){
                System.out.println("B" + i);
            }
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

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


    }

    public static void main(String[] args) {
//        testSleep();
//        TestYield ty1 = new TestYield("张三");
//        TestYield ty2 = new TestYield("李四");
//        ty1.start();
//        ty2.start();
        testJoin();
    }
}

3.Thread的六种状态

线程的执行过程

线程的六种状态分别为
new:刚创建出来还没到start就是new状态
Runnable:Ready和Running统称为Runnable
<< 1. Ready 线程被扔到cpu的等待队列里,等待运行
<< 2. Running 扔到cpu里运行即Running
Terminated:顺利执行完毕进入Terminated
Blocked: synchronized进入Blocked状态 ,加了同步代码块没有得到这个锁的时候,Blocked状态,获得锁跑到就绪状态去运行。
Waitting:调用了o.wait()、t.join()、LockSupport.park()方法进入Waitting状态
TimedWaitting:调用了Thread.sleep(time)、o.wait(time)、t.join(time)、LockSupprt.ParkNanos()、LockSupprt.ParkUtil()方法进入到TimedWaitting状态

ps:关于stop,stop已经不建议用,它太粗暴,容易产生状态的不一致,所以不要关闭线程,让线程正常结束。

4. Synchronized

多个线程访问一个字段的时候需要对这个字段上锁

synchronized底层实现,jvm并不规定,都是hotspot实现的

Synchronized 锁的是对象不是代码
方法上不加任何指令的话锁的是this,锁静态锁的是XX.class
Synchronized(Object)不能用String常量、Integer、Long类型

Synchronized可重入

一个方法可以调用另外一个同步方法,一个线程已经拥有某个对象的锁,再次申请的时候仍会得到该对象的锁,也就是说synchronized获得的锁是可重入的。并且它必须可重入,如果不可得话,父子类得继承就会死锁了。

并发处理问题中小心异常

程序在执行过程中,如果出现异常,默认情况下锁会被释放,所以,在并发处理得过程中,有异常要多加小心,不然可能会发生不一致的情况。
比如在一个Web app处理过程中,多个servlet线程共同去访问同一个资源,这时,如果异常处理不合适,在第一个线程中会抛出异常,其他线程会进入同步代码区,有可能会访问到异常产生的数据,因此要非常小心的处理业务逻辑中的异常。

问题记录
问题:同步方法和非同步方法是否可以同时调用
回答:可以,m1加锁了,其他线程调用这个方法在没得到这个锁之前是不行的,但是m2没有锁,m1和m2同时调用是没有任何问题的。

在这里插入代码片

问题:对业务写方法加锁,对业务读方法不加锁,行不行
回答:不行,容易产生脏读问题

在这里插入代码片

5.锁升级

jdk早期用的是重量级的锁,后来改进,出现了锁升级的概念。
首先是偏向锁,对象markword记录这个线程的Id。
当有多个线程征用的时候,升级为自旋锁
10次之后,升级为重量级锁–os(操作系统)
当执行时间短(加锁代码),线程少的时候,用自旋锁。
当执行时间长,线程多的时候,用系统锁。

总结

第一天大概就学了这么多,概念上面,感觉自己大概理解了,但是还没有记住。代码的话,示例代码还没有开始敲。纯当记笔记了,会经常回头来看的!!要努力!!要坚持!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值