zoukankan      html  css  js  c++  java
  • Java多线程高并发学习笔记(一)——Thread&Runnable

     进程与线程

    首先来看百度百科关于进程的介绍:

    进程是一个具有独立功能的程序关于某个数据集合的一次运行活动。它可以申请和拥有系统资源,是一个动态的概念,是一个活动的实体。它不只是程序的代码,还包括当前的活动,通过程序计数器的值和处理寄存器的内容来表示。 

    直观一点:

     

    windows的任务管理里面,我们看到的eclipse和360等等,都是进程。(想深入了解的可以看看计算机操作系统)

    什么是线程?

    线程是在一个进程独立运行的子任务。线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。

    打个比如说,我们都用的QQ软件,它是一个进程,我们可以和人一边聊天一边传输文件,这就是多线程。

    为什么要使用多线程?

    1.多线程可以充分的利用系统的cpu,达到更快的处理效果

    2.提高系统的运行效率

    想入更深入了解的可以重新学习计算机操作系统,这里就不多介绍了,接下来我们怎么用!(毕竟这才是关键)

     

    Thread and Runnable

    Java中实现线程有两个方式,一个是继承Thread,另一个是实现Runnable接口,首先来看继承Thread的第一个实例:

    package com.chapter1;
    
    public class FirstThread {
    
        public static void main(String[] args) {
            InnerThread thread = new InnerThread();
            // 线程启动
            thread.start();
        }
    
    }
    
    class InnerThread extends Thread {
    
        // Override run方法,写入自己想要实现的业务
        @Override
        public void run() {
            super.run();
            System.out.println("Hello World!");
        }
    }

     继承run方法的时候,我们通常会重写run方法,来实现自己的业务,接下来看,如何使用Runnable实现线程:

    package com.chapter1;
    
    public class FirstRunnable {
    
        public static void main(String[] args) {
            MyRunnable myRunnable;
            myRunnable = new MyRunnable();
            new Thread(myRunnable).start();
        }
    
    }
    
    class MyRunnable implements Runnable {
    
        @Override
        public void run() {
            System.out.println("Hello World!");
        }
    }

    Runnable是一个接口,接口意味着更加灵活一些,也是推荐使用实现Runable接口来写线程的。

     现在我们看到的都是一个单线程的例子,接下来写一个多线程的:

     

    package com.chapter1;
    /**
     * 多个线程实例
     * @author tangj
     *
     */
    public class ManyThread {
    
        public static void main(String args[]) {
            Runnable runnable = new MyRunnable2();
            new Thread(runnable, "a").start();
            new Thread(runnable, "b").start();
            new Thread(runnable, "c").start();
            new Thread(runnable, "d").start();
        }
    }
    
    class MyRunnable2 implements Runnable {
    
        int count = 0;
        @Override
        public void run() {
            autoIncrement();
            System.out.println(Thread.currentThread().getName()+"计算了"+"count:" + count);
        }
        // 执行自增操作
        private void autoIncrement(){
            count++;
        }
    }

    我们来看下运行结果:

     

    再运行一次

    可以看到,多线程的执行是无序的,而且这个结果有点奇怪,难道不是从1增加到4吗,怎么会出现重复的?

    在JVM中,执行自增操作的分为三个步骤:

    1.取得现有值count

    2.执行count+1操作

    3.将count+1赋值给count.

    所以就会遇到这样的情况,一个线程在取得count值的时候,count操作正处于第二个步骤,上一个线程执行的还未进行赋值操作,这就涉及到线程安全问题。

    下面给出上一个例子的解决方案                                                               

    package com.chapter1;
    
    /**
     * 多个线程实例
     * 
     * @author tangj
     *
     */
    public class ManyThread {
    
        public static void main(String args[]) {
            Runnable runnable = new MyRunnable2();
            new Thread(runnable, "a").start();
            new Thread(runnable, "b").start();
            new Thread(runnable, "c").start();
            new Thread(runnable, "d").start();
        }
    }
    
    class MyRunnable2 implements Runnable {
    
        int count = 0;
    
        // 注意在这里增加了关键字 synchronized
        @Override
        synchronized public void run() {
            autoIncrement();
            System.out.println(Thread.currentThread().getName() + "计算了" + "count:" + count);
        }
    
        // 执行自增操作
        private void autoIncrement() {
            count++;
        }
    }

    来看输出结果(多执行几次):

    我们可以看到:不管哪个线程先执行,最后的打印顺序肯定是递增的顺序。

     接下来我们看另外一中实现方式:使用重入锁(ReentrantLock)

    package com.chapter1;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class ManyThread2 {
    
        public static void main(String[] args) {
            Runnable runnable = new MyRunnable3();
            new Thread(runnable, "a").start();
            new Thread(runnable, "b").start();
            new Thread(runnable, "c").start();
            new Thread(runnable, "d").start();
        }
    }
    
    class MyRunnable3 implements Runnable{
        
        int count = 0;
        // 使用重入锁ReentrantLock
        Lock lock = new ReentrantLock();
        
        @Override
        public void run() {
            // 锁住
            lock.lock();
            count++;
            System.out.println(Thread.currentThread().getName() + "计算了" + "count:" + count);
            // 解锁
            lock.unlock();
        }
        
    }

    得到的结果也是整齐的递增顺序,说明它也是线程安全的。

    总结

    所以以后开发多线程的业务的时候,首先应该考虑的问题应该是这样的一个流程:

    1.能否用单线程解决?(单线程本身就是线程安全的)

    2.我开发的多线程业务是否线程安全?

    线程安全是保证程序正常运行的关键,所以应该把线程安全作为开发多线程对于考虑的首要问题

     

    最后说两句:

    本文所以代码都更新到我的github中,大家可以去clone或者Fork,我会持续更新的。

    点击这里进入我的Github
    喜欢的朋友可以点击下方的推荐,或者写个评论我们共同探讨Java高并发!!!

     

     

     

  • 相关阅读:
    JS动态添加事件
    Asp.Net验证控件浅析
    word 文档如何加密
    scp 自动带密码参数复制文件到主机
    Zabbix监控Dell服务器相关硬件资源
    Zabbix的history相关数据表数据太大,执行表分区操作过程
    mysql日常操作
    linux下利用tcpdump抓包工具排查nginx获取客户端真实IP实例
    解决ssh登录很慢的问题以及jumpserver登录主机出现:Authentication timeout
    keepalived启动后报错:(VI_1): received an invalid passwd!的解决办法
  • 原文地址:https://www.cnblogs.com/superfj/p/7488364.html
Copyright © 2011-2022 走看看