zoukankan      html  css  js  c++  java
  • Java线程原理和5种同步方法

    Java线程原理和5种同步方法

    自己开发了一个股票智能分析软件,功能很强大,需要的点击下面的链接获取:

    https://www.cnblogs.com/bclshuai/p/11380657.html

    目录

    1       Java线程原理和两种实现方式... 1

    1.1      java线程原理和源码解析... 1

    1.2      实现 Runnable 接口实现run方法... 2

    1.3      继承Thread类重写run方法... 4

    2       线程的状态变化... 5

    3       线程函数... 6

    4       Java线程同步方法... 7

    4.1      Synchronized修饰方法或代码块... 7

    4.2      Volatile修饰的变量... 7

    4.3      使用重入锁实现线程同步... 7

    1         Java线程原理和两种实现方式

    1.1   java线程原理和源码解析

    线程是为了实现并发运行,java线程实现有两种方式。一种是继承 Thread 类,另一种就是实现 Runnable 接口,实现Runnable接口的run函数。Thread类实际上也是实现了runnable接口,并且在Thread类中实现了Runnable接口的run函数,只是这个run函数是一个Override函数,继承Thread的类要么重写这个run函数,要入以入参的形式传入Runnable 接口实现类对象,也就是下面的target对象。总而言之,就是要实现run方法,要么重写,要么入参传入Runnable实现类对象。

    Public class Thread implements Runnable {
    ……
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }
    ……
    Private Runnable target
    //构造函数1,需要重写run函数
    public Thread(String name) {
        init(null, null, name, 0);
    }
    //构造函数2,需要以入参传入的Runnable接口对象
    public Thread(Runnable target,String name){
        init(null,target,name,0);
    }
    //初始化函数
    private void init(ThreadGroup g,Runnable target,String name,long stackSize){
        ...
        this.target=target;
    }

    ……

    }

    1.2   实现 Runnable 接口实现run方法

    Runnable接口定义

    interface Runnable {
        /**
         * When an object implementing interface <code>Runnable</code> is used
         * to create a thread, starting the thread causes the object's
         * <code>run</code> method to be called in that separately executing
         * thread.
         * <p>
         * The general contract of the method <code>run</code> is that it may
         * take any action whatsoever.
         *
         * @see     java.lang.Thread#run()
         */
       
    public abstract void run();
    }

    class MyThread implements Runnable{ // 实现Runnable接口,作为线程的实现类
       
    private String name ;       // 表示线程的名称
       
    public MyThread(String name){
            this.name = name ;      // 通过构造方法配置name属性
       
    }
        public void run(){  // 覆写run()方法,作为线程的操作主体
           
    for(int i=0;i<10;i++){
                System.out.println(name + "运行,i = " + i) ;
            }
        }

        public static void main(String args[]){
            MyThread mt1 = new MyThread("线程A ") ;    // 实例化对象
           
    MyThread mt2 = new MyThread("线程B ") ;    // 实例化对象
           
    Thread t1 = new Thread(mt1) ;       // 实例化Thread类对象
           
    Thread t2 = new Thread(mt2) ;       // 实例化Thread类对象
           
    t1.start() ;    // 启动多线程
           
    t2.start() ;    // 启动多线程
       
    }
    };

    运行结果为:

    线程B 运行,i = 0

    线程B 运行,i = 1

    线程B 运行,i = 2

    线程B 运行,i = 3

    线程B 运行,i = 4

    线程B 运行,i = 5

    线程B 运行,i = 6

    线程B 运行,i = 7

    线程B 运行,i = 8

    线程B 运行,i = 9

    线程A 运行,i = 0

    线程A 运行,i = 1

    线程A 运行,i = 2

    线程A 运行,i = 3

    线程A 运行,i = 4

    线程A 运行,i = 5

    线程A 运行,i = 6

    线程A 运行,i = 7

    线程A 运行,i = 8

    线程A 运行,i = 9

    1.3   继承Thread类重写run方法

    class MyThread extends Thread{  // 继承Thread类,作为线程的实现类

        private String name ;       // 表示线程的名称

        public MyThread(String name){

            this.name = name ;      // 通过构造方法配置name属性

        }

        public void run(){  // 覆写run()方法,作为线程 的操作主体

            for(int i=0;i<10;i++){

                System.out.println(name + "运行,i = " + i) ;

            }

        }

    };

    public class ThreadDemo02{

        public static void main(String args[]){

            MyThread mt1 = new MyThread("线程A ") ;    // 实例化对象

            MyThread mt2 = new MyThread("线程B ") ;    // 实例化对象

            mt1.start() ;   // 调用线程主体

            mt2.start() ;   // 调用线程主体

        }

    };

    线程A 运行,i = 0

    线程B 运行,i = 0

    线程B 运行,i = 1

    线程B 运行,i = 2

    线程B 运行,i = 3

    线程B 运行,i = 4

    线程B 运行,i = 5

    线程B 运行,i = 6

    线程B 运行,i = 7

    线程B 运行,i = 8

    线程B 运行,i = 9

    线程A 运行,i = 1

    线程A 运行,i = 2

    线程A 运行,i = 3

    线程A 运行,i = 4

    线程A 运行,i = 5

    线程A 运行,i = 6

    线程A 运行,i = 7

    线程A 运行,i = 8

    线程A 运行,i = 9

    2         线程的状态变化

    要想实现多线程,必须在主线程中创建新的线程对象。任何线程一般具有5种状态,即创建,就绪,运行,阻塞,终止。下面分别介绍一下这几种状态:

    • 创建状态 

    在程序中用构造方法创建了一个线程对象后,新的线程对象便处于新建状态,此时它已经有了相应的内存空间和其他资源,但还处于不可运行状态。新建一个线程对象可采用Thread 类的构造方法来实现,例如 “Thread thread=new Thread()”。

    • 就绪状态 

    新建线程对象后,调用该线程的 start() 方法就可以启动线程。当线程启动时,线程进入就绪状态。此时,线程将进入线程队列排队,等待 CPU 服务,这表明它已经具备了运行条件。

    • 运行状态 

    当就绪状态被调用并获得处理器资源时,线程就进入了运行状态。此时,自动调用该线程对象的 run() 方法。run() 方法定义该线程的操作和功能。

    • 阻塞状态 

    一个正在执行的线程在某些特殊情况下,如被人为挂起或需要执行耗时的输入/输出操作,会让 CPU 暂时中止自己的执行,进入阻塞状态。在可执行状态下,如果调用sleep(),suspend(),wait() 等方法,线程都将进入阻塞状态,发生阻塞时线程不能进入排队队列,只有当引起阻塞的原因被消除后,线程才可以转入就绪状态。

    • 死亡状态 

    线程调用 stop() 方法时或 run() 方法执行结束后,即处于死亡状态。处于死亡状态的线程不具有继续运行的能力。

    3         线程函数

    (1)独占CPU启动线程

     join() 方法让一个线程强制运行,线程强制运行期间,其他线程无法运行,必须等待此线程完成之后才可以继续执行。

    (2)线程休眠

    Thread.sleep(500) 即可实现休眠500ms

    (3)线程优先级

    线程将根据其优先级的大小来决定哪个线程会先运行,但是需要注意并非优先级越高就一定会先执行,哪个线程先执行将由 CPU 的调度决定。

    t1.setPriority(Thread.MIN_PRIORITY) ;   // 优先级最低

    t2.setPriority(Thread.MAX_PRIORITY) ;   // 优先级最高

    t3.setPriority(Thread.NORM_PRIORITY) ;  // 优先级最中等

    (4)中断线程

    当一个线程运行时,另外一个线程可以直接通过interrupt()方法中断其运行状态。

    (5)线程礼让

    也可以使用 yield() 方法将一个线程的占用资源暂时让给其他线程执行。

    4         Java线程同步方法

    4.1   Synchronized修饰方法或代码块

    java的每个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。

    public synchronized void save(){} 或者

    synchronized (this)

    { account += money;}

    4.2   Volatile修饰的变量

    如果对声明了volatile的变量进行写操作,JVM就会向处理器发送一条Lock前缀的指令,将这个变量所在缓存行的数据写回到系统内存。多处理器下,其他处理器发现自己缓存行对应的内存地址被修改,就会重新读取数据。在需要同步操作的变量前加上volatile

    private volatile int account = 100;

     

    4.3   使用重入锁实现线程同步

    JavaSE5.0中新增了一个java.util.concurrent包来支持同步。 ReentrantLock类是可重入、互斥、实现了Lock接口的锁。

     ReentrantLock() : 创建一个ReentrantLock实例 
     lock() : 获得锁 

     unlock() : 释放锁 

    只给出要修改的代码,其余代码与上同

            class Bank {

               

                private int account = 100;

                //需要声明这个锁

                private Lock lock = new ReentrantLock();

                public int getAccount() {

                    return account;

                }

                //这里不再需要synchronized

                public void save(int money) {

                    lock.lock();

                    try{

                        account += money;

                    }finally{

                        lock.unlock();

                    }

                }

            }

    4.4   原子变量实现线程同步

    原子操作就是指将读取变量值、修改变量值、保存变量值看成一个整体来操作

    即-这几种行为要么同时完成,要么都不完成。

    在java的util.concurrent.atomic包中提供了创建了原子类型变量的工具类。

    其中AtomicInteger 表可以用原子方式更新int的值AtomicInteger类常用方法:

    AtomicInteger(int initialValue) : 创建具有给定初始值的新的AtomicInteger,addAddGet(int dalta) : 以原子方式将给定值与当前值相加。get() : 获取当前值。

    代码实例

    class Bank {

            private AtomicInteger account = new AtomicInteger(100);

            public AtomicInteger getAccount() {

                return account;

            }

            public void save(int money) {

                account.addAndGet(money);

            }

    }

    4.5   阻塞队列实现线程同步

    (1)LinkedBlockingQueue内部实现是单链表结构。LinkedBlockingQueue插入和读取是有两把ReentrantLock锁的,LinkedBlockingQueue 是插入和拿取数据都是阻塞执行的。内部采用原子变量AtomicInteger统计个数。

    (2)ArrayBlockingQueue,内部实现是数组。插入和读取用的是同一把锁,内部采用int变量统计数量。

    LinkedBlockingQueue类常用方法

    LinkedBlockingQueue() : 创建一个容量为Integer.MAX_VALUE的LinkedBlockingQueue

    put(E e) : 在队尾添加一个元素,如果队列满则阻塞

    size() : 返回队列中的元素个数

    take() : 移除并返回队头元素,如果队列空则阻塞

    BlockingQueue<E>定义了阻塞队列的常用方法,尤其是三种添加元素的方法,我们要多加注意,当队列满时:

    add()方法会抛出异常

    offer()方法返回false

    put()方法会阻塞

     

  • 相关阅读:
    三数之和
    罗马数字与整数
    Oracle 开启或关闭归档
    Oracle RMAN scripts to delete archivelog
    Oracle check TBS usage
    Oracle kill locked sessions
    场景9 深入RAC运行原理
    场景7 Data Guard
    场景4 Data Warehouse Management 数据仓库
    场景5 Performance Management
  • 原文地址:https://www.cnblogs.com/bclshuai/p/13489050.html
Copyright © 2011-2022 走看看