• java 多线程学习


    一、概念

    程序、进程、线程

    程序   是计算机指令的集合。

    进程   是一个运行中的程序,它指的是从代码加载,执行到执行结束这样一个完整过程。每个进程占用不同的内存空间。

    线程   是进程中某个单一顺序的控制流,它被操作系统调度,并在处理器或内核上运行。同一个进程的多个线程共享一个内存空间。

    二、线程的生命周期

    概念

    线程生命周期有五种状态

    示例代码

    三、线程的作用

    多线程是为了使多个线程并行工作以完成多项任务,来提高系统的效率。

    CPU在某一个时间点只能处理一个线程。cpu是通过时间片段的方式来分配CPU处理时间,通过多线程将程序划分成多个独立的任务,可以使CPU处理的效率大大提高。

    总之一句话,提高程序运行效率。

    四、实现多线程的方法

    概念

    实现多线程的方式有两种,一种是继承Thread(java.lang.Thread)类并重写run()方法,另一种是实现Runnable接口((java.lang.Runnable)。

    示例代码

    public class ThreadTest extends Thread{
        
        @Override
        public void run() {
            System.out.println("线程正在运行!!!");
        }
        
        
        public static void main(String[] args) {
            
            ThreadTest thread1=new ThreadTest();
            ThreadTest thread2=new ThreadTest();
            ThreadTest thread3=new ThreadTest();
            
            thread1.start();
            thread2.start();
            thread3.start();
            
        }
    
    }
    public class RunnableTest implements Runnable {
        
        public void run() {
            System.out.println("线程正在运行!!!");
        }
        
        public static void main(String[] args) {
            //第一种使用方式
            RunnableTest runTest1=new RunnableTest();
            RunnableTest runTest2=new RunnableTest();
            RunnableTest runTest3=new RunnableTest();
            Thread thread1=new Thread(runTest1);
            Thread thread2=new Thread(runTest2);
            Thread thread3=new Thread(runTest3);
            
            thread1.start();
            thread2.start();
            thread3.start();
            
            //第二种方式
            RunnableTest runTest4=new RunnableTest();
            Thread thread4=new Thread(runTest4);
            Thread thread5=new Thread(runTest4);
            Thread thread6=new Thread(runTest4);
            
            thread4.start();
            thread5.start();
            thread6.start();
            
            //其实以上两种没区别 ,建议使用第二种,代码简洁
    
        }
    }

    五、多线程同步的问题

    概念

    多线程访问共享资源的不同步问题。

    示例代码

    经典的多窗口售票案例

    package com.haiqin;
    
    public class RunnableTest implements Runnable {
        
        private int ticket=10;
        public void run(){
            while(true) {
                if(ticket>0) {
                    try {
                        //是对当前线程操作么?
                        Thread.sleep(10);
                        //Thread.currentThread().sleep(10);
                        System.out.println(Thread.currentThread().getName()+"正在售票----->"+(this.ticket--));    
                    } catch (InterruptedException e) {
                        
                        e.printStackTrace();
                    }
                        
                }
            }    
        }
        
        public static void main(String[] args) {
            
            
            //第二种方式
            RunnableTest runTest=new RunnableTest();
            Thread thread1=new Thread(runTest,"窗口1");
            Thread thread2=new Thread(runTest,"窗口2");
            Thread thread3=new Thread(runTest,"窗口3");
            
            thread1.start();
            thread2.start();
            thread3.start();
            
            //其实以上两种没区别 ,建议使用第二种,代码简洁
    
        }
    }

    运行结果

     实现多线程同步有两种方式,一种是synchoronized 关键字  另一种是Lock方式

    synchoronized 关键字 

    概念:java虚拟机通过给每个对象(类对象或者实例对象)加锁的方式来实现多线程的同步。java虚拟机通过锁来确保某一对象在任何同一时刻最多只有一个线程能够运行与该对象相关联的同步语句块和同步方法。

    //同步语句块
    synchronized(this){
       //代码
    }
    
    //同步方法
    public  synchronized void fun {
       //代码
    }

    示例代码

    同步语句块

    public class RunnableTest implements Runnable {
        
        private int ticket=10;
        public void run(){
            while(true) {
                synchronized (this) {
    if(ticket>0) {
                        try {
                            //是对当前线程操作么?
                            Thread.sleep(10);
                            //Thread.currentThread().sleep(10);
                            System.out.println(Thread.currentThread().getName()+"正在售票----->"+(this.ticket--));    
                        } catch (InterruptedException e) {
                            
                            e.printStackTrace();
                        }        
                    }else {
                        break;
                    }
                }
                
            }    
        }
        
        public static void main(String[] args) {
            
            //保证runTest这个实例唯一(单例模式),同步语句快中的this指的是调用这个语句块的对象,对这个对象上锁,如果对象不唯一,则同步语句块会失效。
            RunnableTest runTest=new RunnableTest();
            Thread thread1=new Thread(runTest,"窗口1");
            Thread thread2=new Thread(runTest,"窗口2");
            Thread thread3=new Thread(runTest,"窗口3");
            
            thread1.start();
            thread2.start();
            thread3.start();
            
            //其实以上两种没区别 ,建议使用第二种,代码简洁
    
        }
    }

    同步方法

    public class RunnableTest implements Runnable {
        
        private int ticket=10;
        public void run(){
            while(ticket>0) {
                shell();        
            }    
        }
        
        public synchronized void shell() {
    if(ticket>0) {
                    try {
                        //是对当前线程操作么?
                        Thread.sleep(10);
                        //Thread.currentThread().sleep(10);
                        System.out.println(Thread.currentThread().getName()+"正在售票----->"+(this.ticket--));    
                    } catch (InterruptedException e) {
                        
                        e.printStackTrace();
                    }        
                }
        }
        
        public static void main(String[] args) {
            
            //要保证 实例对象唯一
            RunnableTest runTest=new RunnableTest();
            Thread thread1=new Thread(runTest,"窗口1");
            Thread thread2=new Thread(runTest,"窗口2");
            Thread thread3=new Thread(runTest,"窗口3");
            
            thread1.start();
            thread2.start();
            thread3.start();
            
            //其实以上两种没区别 ,建议使用第二种,代码简洁
    
        }
    }

    说白了就是 synchronized 锁的是对象不是代码。想要实现锁可以对象使用单例【锁对象】,或者Synchronized(xx.class)[这个是锁代码]

    Lock方式(锁代码)

    概念:

    锁提供了对共享资源的独占访问。一次只能有一个线程获得锁,对共享资源的所有访问都需要首先获得锁。(和synchronized意思一样,实现方式不同。)

    示例代码

    package com.haiqin;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class LockTest{
        
        private int ticket=10;
    
        private Lock lock = new ReentrantLock();
    
        
        public  void shell(Thread thread){
    
            if(lock.tryLock()){
                try {
                    System.out.println("线程名"+thread.getName() + "获得了锁");
                    
                    while(ticket>0) {
                        if(ticket>0) {
                            try {
                                //是对当前线程操作么?
                                //Thread.sleep(30);
                                Thread.currentThread().sleep(10);
                                System.out.println(thread.getName()+"正在售票----->"+(this.ticket--));    
                            } catch (InterruptedException e) {
                                
                                e.printStackTrace();
                            }        
                        }
                    }
                    
                    
                }catch(Exception e){
                    
                    e.printStackTrace();
                    
                } finally {
                    
                    System.out.println("线程名"+thread.getName() + "释放了锁");
                    
                    lock.unlock();
                }
            }else{
                
                System.out.println("我是"+Thread.currentThread().getName()+"有人占着锁,我就不要啦");
            }
        }
    
    
        
        
        public static void main(String[] args) {
            
            
            final LockTest lockTest = new LockTest();
    
            //线程1
            Thread t1 = new Thread(new Runnable() {
    
                public void run() {
                    lockTest.shell(Thread.currentThread());
                }
            }, "窗口1");
    
          //线程2
            Thread t2 = new Thread(new Runnable() {
    
                public void run() {
                    lockTest.shell(Thread.currentThread());
                }
            }, "窗口2");
            
          //线程3
            Thread t3 = new Thread(new Runnable() {
    
                public void run() {
                    lockTest.shell(Thread.currentThread());
                }
            }, "窗口3");
            
            
            t1.start();
            t2.start();
            t3.start();
            
    
        }
    
    }

    运行结果

    Synchronized和Lock的区别

    六、线程控制方法(略)

    七、守护线程(后台进程)

     概念:

    在后台执行服务的线程。如java的垃圾自动回收线程等。

    在一个进程中,只要所有前台进程已退出,那么后台线程会自动结束。

    示例代码

    package com.haiqin;
    
    import java.io.IOException;
    
    public class TestDaemon extends Thread{
        
        public void run() {
            for(int i=0;i<=100;i++) {
                try {
                    Thread.sleep(100);
                    System.out.println(i);
                } catch (InterruptedException e) {
                    
                    e.printStackTrace();
                }
                
            }
        }
        
        public static void main(String[] args) {
                    
            try {
                TestDaemon testDaemon=new TestDaemon();
                //线程变为守护线程
                testDaemon.setDaemon(true);
                testDaemon.start();
                //随意按键盘,main线程结束
                System.in.read();
            } catch (IOException e) {
                
                e.printStackTrace();
            }
            
        }
    
    }

    七、Thread类的源码和Runnable接口的源码

    1 继承层次关系

    2 源码

    java.lang.Runnable 接口

    public  interface Runnable {
    
    
        public abstract void run();
    }

    java.lang.Thread 实现类

    八、Lock的类型(5种)

     

    九、Lock的源码(继承层次图和常用类)

    1 继承层次图

    2 源码

    java.util.concurrent.locks.Lock  接口

    public interface Lock {
    
        /**
         * 获取锁。
         * 如果锁不可用,出于线程调度目的,将禁用当前线程,并且在获得锁之前,该线程将一直处于阻塞状态(锁池里面)。
         */
        void lock();
    
        /**
         * 获取锁。
         * 如果获取锁了,就成功获取锁。如果没有获取锁,则中断(等待)。
         */
        void lockInterruptibly() throws InterruptedException;
    
        /**
         * 获取锁。仅在调用时锁为空闲状态才获取该锁。
         * 如果锁可用,则获取锁,并立即返回值 true。如果锁不可用,则此方法将立即返回值 false。 
         */
        boolean tryLock();
    
        /**
         * 获取锁。
         * 如果没有获取锁,先等待一定时间(阻塞),过了这个时间,则不继续等待。
         */
        boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    
        /**
         * 释放锁。
         */
        void unlock();
    
        /**
         * 返回绑定到此 Lock 实例的新 Condition 实例。
         * 在等待条件前,锁必须由当前线程保持。调用 Condition.await() 将在等待前以原子方式释放锁,并在等待返回前重新获取锁。
         */
        Condition newCondition();
    }

    java.util.concurrent.locks.ReentrantLock 实现类

    package java.util.concurrent.locks;
    
    
    public class ReentrantLock implements Lock, java.io.Serializable {
       
        /**
         * 
         */
        abstract static class Sync extends AbstractQueuedSynchronizer 
        /**
         * 
         */
        static final class NonfairSync extends Sync
    
        /**
         * 
         */
        static final class FairSync extends Sync 
    
        /**
         * 创建一个 ReentrantLock 的实例。这等同于使用 ReentrantLock(false)
         */
        public ReentrantLock()
        
        
        /**
         * 创建一个具有给定公平策略的 ReentrantLock。(公平锁)
         */
        public ReentrantLock(boolean fair)
    
        /**
         * 获取锁。 如果获取不到锁,阻塞状态(等待锁)
         */
        public void lock()
    
        /**
         * 获取锁。如果获取不到锁,不等待。
         */
        public void lockInterruptibly()
    
        /**
         * 获取锁,如果获取不到锁,不等待。不支持公平锁。
         */
        public boolean tryLock()
        
        /**
         * 获取锁,如果获取不到锁,等待一定时间。支持公平锁。
         */
        public boolean tryLock(long timeout, TimeUnit unit)
        
    
        /**
         * 试图释放此锁。
         */
        public void unlock()
        
    
        /**
         * 返回用来与此 Lock 实例一起使用的 Condition 实例。
         */
        public Condition newCondition()
    
        /**
         * 查询当前线程保持此锁的次数。 调试用
         */
        public int getHoldCount()
    
        /**
         * 查询当前线程是否保持此锁。 调试用   
         */
        public boolean isHeldByCurrentThread()
    
        /**
         * 查询此锁是否由任意线程保持。此方法用于监视系统状态,不用于同步控制。
         * 如果任意线程保持此锁,则返回 true;否则返回 false
         */
        public boolean isLocked()
    
        /**
         * 如果此锁的公平设置为 true,则返回 true。
         */
        public final boolean isFair()
        
        /**
         * 返回目前拥有此锁的线程,如果此锁不被任何线程拥有,则返回 null。
         */
        protected Thread getOwner()
        
        /**
         * 查询是否有些线程正在等待获取此锁。
         */
        public final boolean hasQueuedThreads()
    
    
        /** 
         * 查询给定线程是否正在等待获取此锁。
         * 如果给定线程已加入队列并且正在等待此锁,则返回 true
         */
        public final boolean hasQueuedThread(Thread thread)
    
        /**
         * 返回正等待获取此锁的线程估计数。该值仅是估计的数字,
         * 因为在此方法遍历内部数据结构的同时,线程的数目可能动态地变化。此方法用于监视系统状态,不用于同步控制。
         */
        public final int getQueueLength()
    
        /**
         * 返回一个 collection,它包含可能正等待获取此锁的线程。因为在构造此结果的同时实际的线程 set 可能动态地变化,
         * 所以返回的 collection 仅是尽力的估计值。
         * 所返回 collection 中的元素没有特定的顺序。此方法用于加快子类的构造速度,以提供更多的监视设施。
         */
        protected Collection<Thread> getQueuedThreads()
        
        /**
         * 查询是否有些线程正在等待与此锁有关的给定条件。
         * 注意,因为随时可能发生超时和中断,所以返回 true 并不保证将来某个 signal 将唤醒线程。此方法主要用于监视系统状态。
         * 如果有任何等待的线程,则返回 true
         */
        public boolean hasWaiters(Condition condition)
    
        /**
         * 返回等待与此锁相关的给定条件的线程估计数。注意,因为随时可能发生超时和中断,
         * 所以只能将估计值作为实际等待线程数的上边界。此方法用于监视系统状态,不用于同步控制。
         */
        public int getWaitQueueLength(Condition condition)
    
        /**
         * 返回一个 collection,它包含可能正在等待与此锁相关给定条件的那些线程。因为在构造此结果的同时实际的线程 set 可能动态地变化,
         * 所以返回 collection 的元素只是尽力的估计值。
         * 所返回 collection 中的元素没有特定的顺序。此方法用于加快子类的构造速度,提供更多的条件监视设施。、
         */
        protected Collection<Thread> getWaitingThreads(Condition condition) 
       
    }

     java.util.concurrent.locks.ReadWriteLock 接口

    public interface ReadWriteLock {
        
        /**
         * 返回用于读取操作的锁。
         */
        Lock readLock();
    
        /**
         * 返回用于写入操作的锁。
         */
        Lock writeLock();
    }
    java.util.concurrent.locks.ReentrantReadWriteLock 实现类
    public class ReentrantReadWriteLock
            
            
        //使用默认(非公平)的排序属性创建一个新的 ReentrantReadWriteLock。    
        public ReentrantReadWriteLock() {
            this(false);
        }
    
        /**
         * 使用给定的公平策略创建一个新的 ReentrantReadWriteLock。
         * 如果此锁应该使用公平排序策略,则该参数的值为 true
         */
        public ReentrantReadWriteLock(boolean fair)
        
        
    
        //返回用于写入操作的锁。
        public ReentrantReadWriteLock.WriteLock writeLock() 
        
        //返回用于读取操作的锁。
        public ReentrantReadWriteLock.ReadLock  readLock() 
    
    
        /**
         * 如果此锁将公平性设置为 ture,则返回 true。
         */
        public final boolean isFair()
    
        /**
         * 返回当前拥有写入锁的线程,如果没有这样的线程,则返回 null。
         */
        protected Thread getOwner()
    
        /**
         * 查询为此锁保持的读取锁数量。此方法设计用于监视系统状态,而不是同步控制。
         */
        public int getReadLockCount()
    
        /**
         * 查询是否某个线程保持了写入锁。此方法设计用于监视系统状态,而不是同步控制。
         */
        public boolean isWriteLocked()
    
        /**
         * 查询当前线程是否保持了写入锁。
         */
        public boolean isWriteLockedByCurrentThread() 
        
        /**
         * 查询当前线程在此锁上保持的重入写入锁数量。对于与解除锁操作不匹配的每个锁操作,writer 线程都会为其保持一个锁。
         */
        public int getWriteHoldCount() 
        
        /**
         * 查询当前线程在此锁上保持的重入读取锁数量。对于与解除锁操作不匹配的每个锁操作,reader 线程都会为其保持一个锁
         */
        public int getReadHoldCount()
        
        /**
         * 返回一个 collection,它包含可能正在等待获取写入锁的线程。
         */
        protected Collection<Thread> getQueuedWriterThreads()
        
        /**
         * 返回一个 collection,它包含可能正在等待获取读取锁的线程。
         */
        protected Collection<Thread> getQueuedReaderThreads()
    
        /**
         * 如果有其他线程正等待获取锁,则返回 true
         */
        public final boolean hasQueuedThreads()
    
        /**
         * 如果将给定的线程加入等待此锁的队列,则返回 true
         */
        public final boolean hasQueuedThread(Thread thread)
    
        /**
         * 返回等待获取读取或写入锁的线程估计数目。
         */
        public final int getQueueLength()
    
        /**
         * 返回一个 collection,它包含可能正在等待获取读取或写入锁的线程。
         */
        protected Collection<Thread> getQueuedThreads()
    
        /**
         * 查询是否有些线程正在等待与写入锁有关的给定条件。
         */
        public boolean hasWaiters(Condition condition)
    
        /**
         * 返回正等待与写入锁相关的给定条件的线程估计数目。
         */
        public int getWaitQueueLength(Condition condition)
    
        /**
         * 返回一个 collection,它包含可能正在等待与写入锁相关的给定条件的那些线程。
         */
        protected Collection<Thread> getWaitingThreads(Condition condition)
    
       
    
    }
  • 相关阅读:
    [linux] unzip把压缩文件解压到指定目录下 d
    在线客服系统全渠道接入不限制坐席、域名、服务器GOFLY在线客服系统
    [html] link 标签 rel=canonical 属性的用法
    [javascript] 获取当前时间日期和时间戳
    [css] 加载本地文件出现跨域Cross origin requests are only supported for protocol schemes: http, data, chrome, chromeextension, chromeuntrusted, https.
    [git] git stash 暂存未提交的修改文件
    [MySQL系列] mysql find_in_set搜索以逗号分隔的字符串
    [Linux系列] Vim命令清空文件删除所有内容
    [Golang系列] gorm执行like模糊查询
    [Linux系列] 在线客服系统代码脚本getconf LONG_BIT获取当前系统的位数
  • 原文地址:https://www.cnblogs.com/haiqin/p/9147903.html
Copyright © 2020-2023  润新知