zoukankan      html  css  js  c++  java
  • Thread类的常用方法----多线程基础练习

    创建多线程程序的第一种方式----继承Thread类

    常用API

    构造方法
    public Thread() :分配一个新的线程对象。
    public Thread(String name) :分配一个指定名字的新的线程对象。
    public Thread(Runnable target) :分配一个带有指定目标新的线程对象。
    public Thread(Runnable target,String name) :分配一个带有指定目标新的线程对象并指定名字。
    常用方法
    public String getName() :获取当前线程名称。
    public void start() :导致此线程开始执行; Java虚拟机调用此线程的run方法。
    public void run() :此线程要执行的任务在此处定义代码。
    public static void sleep(long millis) :使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
    public static Thread currentThread() :返回对当前正在执行的线程对象的引用。

    获取线程的名称

    继承Thread类重写run方法。

    /**
     * 获取线程的名称
     *  1:使用Thread类中的方法getName()
     *      String getName()    //放回当前线程的名称
     *  2:先获得当前执行的线程,使用线程中的getName()方法返回线程的名称
     *      static Thread currentThread()     //返回当前正在执行的线程
     */
    public class myThread extends Thread {
        @Override
        public void run() {
            System.out.println(getName());
            System.out.println(currentThread().getName());
        }
    }
    

    主线程

    public class Main {
        public static void main(String[] args) {
            myThread thread = new myThread();
            thread.start();
            new myThread().start();
            new myThread().start();
        }
    }
    
    

    效果
    在这里插入图片描述

    设置线程的名称

    继承Thread类重写run方法。

    /**
     * 设置线程的名字
     *  1:setName("线程名字")
     *  2:使用带参数的构造方法::创建一个带参数的构造方法,参数传递线程的名称;调用父类带参构造方法,让父类(Thread)给子线程起一个名字。
     */
    public class myThread extends Thread {
        public myThread(){}
        public myThread(String name){
            super(name);
        }
        @Override
        public void run() {
            System.out.println(getName());
        }
    }
    

    主线程

    public class Main {
        public static void main(String[] args) {
            myThread thread = new myThread();
            thread.setName("zhang");
            thread.start();
            myThread thread2 = new myThread("guang");
            thread2.start();
        }
    }
    

    效果
    在这里插入图片描述

    Sleep方法

    继承Thread类重写run方法。

    /**
     * public static void sleep(long millis) :使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
     * 毫秒结束后线程继续执行
     */
    public class myThread extends Thread {
        @Override
        public void run() {
            System.out.println("1");
            try {
                sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(2);
        }
    }
    

    主线程

    public class Main {
        public static void main(String[] args) {
            myThread thread = new myThread();
            thread.start();
            System.out.println(".....");
        }
    }
    

    效果
    在这里插入图片描述

    实现多线程的第二种方式----实现Runnable接口 java.lang.Runnable

    介绍

    1. 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
    2. 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正
      的线程对象。
    3. 调用线程对象的start()方法来启动线程。

    通过Runnable接口创建多线程

    /**
     * 创建多线程的第二种方式,实现Runnable接口
     * java.lang.runnable
     *      Runnable接口应该由那些通过某一线程执行其实例的类来实现。其实例的类必须定义一个称为run的无参数方法
     * java.lang.Thread类的构造方法
     *      Thread(Runnable target)     分配新的 Thread 对象。
     *      Thread(Runnable target, String name)    分配新的 Thread 对象。
     * 实现步骤:
     *      1、创建一个Runnable接口的实现类
     *      2、在实现类中重写Runnable接口的run方法,设置线程任务
     *      3、创建一个Runnable接口的实现类对象
     *      4、创建Thread类对象,构造方法中传递Runnable接口的实现类对象
     *      5、调用Thread类中的start方法,开启新的线程执行run方法
     */
    public class myThread implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                System.out.println(Thread.currentThread().getName()+i);
            }
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            myThread th = new myThread();
            Thread thread = new Thread(th);
            thread.start();
            for (int i = 0; i < 20; i++) {
                System.out.println(Thread.currentThread().getName()+i);
            }
        }
    }
    

    效果
    在这里插入图片描述

    继承Thread和实现Runnable接口的区别

    实现Runnable接口比继承Thread类所具有的优势:

    1. 适合多个相同的程序代码的线程去共享同一个资源。
    2. 可以避免java中的单继承的局限性。
    3. 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
    4. 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。

    匿名内部类实现线程的创建

    使用线程的内匿名内部类方式,可以方便的实现每个线程执行不同的线程任务操作。

    /**
     * 匿名内部类方式实现线程的创建
     *
     * 匿名:没有名字
     * 内部类:写在其他类内部的类
     *
     * 匿名内部类作用:简化代码
     *      把子类继承父类,重写父类的方法,创建子类对象合一步完成
     *      把实现类实现接口,重写接口中的方法,创建实现类兑现合成一步完成
     * 匿名内部类的最终产物:子类/实现类对象,而这个类没有名字
     *
     * 格式
     *      new 父类/接口(){
     *          重写父类/接口中的方法
     *      }
     */
    public class Main {
        public static void main(String[] args) {
            //使用Thread类创建
            new Thread(){
                @Override
                public void run() {
                    for (int i = 0; i < 20; i++) {
                        System.out.println(Thread.currentThread().getName()+i);
                    }
                }
            }.start();
            //使用Runnable接口创建
            Runnable r = new Runnable(){
                @Override
                public void run() {
                    for (int i = 0; i < 20; i++) {
                        System.out.println(Thread.currentThread().getName()+i);
                    }
                }
            };
            new Thread(r).start();
            //优化Runnable接口方式
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 20; i++) {
                        System.out.println(Thread.currentThread().getName()+i);
                    }
                }
            }).start();
            //主线程
            for (int i = 0; i < 20; i++) {
                System.out.println(Thread.currentThread().getName()+i);
            }
        }
    }
    

    效果
    在这里插入图片描述

    线程安全问题

    多线程访问了共享数据,会产生线程安全问题

    线程不安全案例

    /**
     * 卖票案例,三个线程同时卖票
     */
    public class RunnableImp implements Runnable {
        private int ticket = 100;
        @Override
        public void run() {
            while (true){
                if(ticket>0){
                    System.out.println(Thread.currentThread().getName()+"售出了"+ticket+"号票");
                    ticket--;
                }
            }
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            RunnableImp runnableImp = new RunnableImp();
            new Thread(runnableImp).start();
            new Thread(runnableImp).start();
        }
    }
    

    效果
    在这里插入图片描述
    发现程序出现了两个问题:

    1. 相同的票数,比如5这张票被卖了两回。
    2. 不存在的票,比如0票与-1票,是不存在的。
      这种问题,几个窗口(线程)票数不同步了,这种问题称为线程不安全。
      当我们使用多个线程访问同一资源的时候,且多个线程中对资源有写的操作,就容易出现线程安全问题。
      要解决上述多线程并发访问一个资源的安全性问题:也就是解决重复票与不存在票问题,Java中提供了同步机制
      (synchronized)来解决。
      java中有三种方式实现同步操作
    3. 同步代码块。
    4. 同步方法。
    5. 锁机制。

    第一种解决方式----同步代码块

    同步代码块: synchronized 关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。
    格式

    synchronized(同步锁){
    		需要同步操作的代码
    }
    

    同步锁:
    对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁.

    1. 锁对象 可以是任意类型。
    2. 多个线程对象 要使用同一把锁。
      案例
    /**
     * 出现了线程安全问题,卖出了重复的和不存在的票
     *
     * 解决线程安全的一种方式:使用同步代码块
     * 格式:
     *      synchronized(锁对象){
     *          可能会出现线程安全问题的代码
     *      }
     */
    public class RunnableImp implements Runnable {
        private int ticket = 100;
        //创建一个锁对象,,随便一个对象
        Object object = new Object();
        @Override
        public void run() {
            while (true){
                //锁对象只有一个,每次运行时一个线程执行会拿走锁对象,其他线程到了,如果没有锁则进入阻塞状态
                synchronized (object){
                    if(ticket>0){
                        System.out.println(Thread.currentThread().getName()+"售出了"+ticket+"号票");
                        ticket--;
                    }
                }
            }
        }
    }
    
    
    public class Main {
        public static void main(String[] args) {
            RunnableImp runnableImp = new RunnableImp();
            new Thread(runnableImp).start();
            new Thread(runnableImp).start();
        }
    }
    

    效果
    在这里插入图片描述

    第二种方式----同步方法

    同步方法:使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外
    等着。
    格式

    public synchronized void method(){
    可能会产生线程安全问题的代码
    }
    
    /**
     * 卖票案例出现了线程安全问题
     * 卖出了不存在的票和重复的票
     *
     * 解决线程安全问题的第二种方案:使用同步方法
     * 使用步骤:
     *      1、把访问量共享数据的代码抽取出来,放到一个方法中
     *      2、在方法上添加synchronized修饰符
     */
    public class RunnableImp implements Runnable {
        private int ticket = 100;
        @Override
        public void run() {
            while (true){
                payTiclet();
            }
        }
        public synchronized void payTiclet(){
            if(ticket>0){
                try {
                    //线程休眠,效果更明显
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"售出了"+ticket+"号票");
                ticket--;
            }
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            RunnableImp runnableImp = new RunnableImp();
            new Thread(runnableImp).start();
            new Thread(runnableImp).start();
            new Thread(runnableImp).start();
        }
    }
    

    效果
    在这里插入图片描述
    同步锁是谁?
    对于非static方法,同步锁就是this。
    对于static方法,我们使用当前方法所在类的字节码对象(类名.class)。class文件对象

    第三种方式----Lock锁

    java.util.concurrent.locks.Lock 机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作,
    同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象。
    Lock锁也称同步锁,加锁与释放锁方法化了,如下:

    public void lock() :加同步锁。
    public void unlock() :释放同步锁。
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class RunnableImp implements Runnable {
        private int ticket = 100;
        Lock lock = new ReentrantLock();
        @Override
        public void run() {
            while (true){
                try {
                    lock.lock();//在可能出现线程安全问题的代码前打开锁
                    if(ticket>0) {
                        Thread.sleep(10);
                        System.out.println(Thread.currentThread().getName() + "售出了" + ticket + "号票");
                        ticket--;
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    lock.unlock();//放在finally中一定会关掉锁
                }
            }
        }
    }
    
    
    public class Main {
        public static void main(String[] args) {
            RunnableImp runnableImp = new RunnableImp();
            new Thread(runnableImp).start();
            new Thread(runnableImp).start();
            new Thread(runnableImp).start();
        }
    }
    
  • 相关阅读:
    js中实现继承的几种方式
    js中prototype,__proto__,constructor之间的关系
    圣杯布局
    BFC详解
    Altium制作DC002的PCB封装和3D模型
    直流供电电路中,关于电源并联二极管、电容作用的思考与总结
    AltiumDesigner17快捷键
    AltiumDesigner17学习指南
    AltiumDesigner元器件搜索中英文对照
    智慧树自动刷课脚本
  • 原文地址:https://www.cnblogs.com/zhangguangxiang/p/14232585.html
Copyright © 2011-2022 走看看