• java多线程--多线程基础小结


    什么是线程?

       在同一个进程中可以执行多个任务,每一个任务可以看做一个线程.

      线程是程序的执行单元,执行路径,使程序使用cpu的最基本单位

      一个进程如果只有一条执行路径,那么就是单线程的

    一个进程如果有多个执行路径,那么就是多线程的

    多线程的意义:

       多进程的存在是为了提高CPU的利用率,多线程的存在,不是为了提高程序的执行速度,而是为了提高应用程序的使用率.

       程序的执行其实都是在抢CPU的资源,即CPU的执行权.

       多个进程在抢这个资源,而其中一个进程如果有多个执行路径,即多个线程,那么就有更高的几率抢到CPU的执行权

    JVM的启动是单线程的还是多线程的?

      是多线程的,启动jvm是就相当于启动了一个进程,接着该进程会创建一个主线程来调用main方法,而同时还有启动其他的线程,比如说垃圾回收的线程.

    两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口.以第一种为例:

    package com.wang.reflect;
    
    class MyThread extends Thread{
    
        
    
        @Override
    
        public void run() {
    
            for(int i=0;i<10;i++){
    
                //获取当前正在执行的线程的名称,相当于this.getName();
    
                System.out.println(Thread.currentThread().getName()+"::"+i);
    
            }
    
        }
    
    }
    
    public class TestReflect {
    
        public static void main(String[] args) throws Exception {
    
            MyThread mThread1=new MyThread();
    
            MyThread mThread2=new MyThread();
    
            //设置线程的名字,也可以直接通过带参构造函数设置线程名称
    
            mThread1.setName("张无忌:");
    
            mThread2.setName("赵敏:");
    
            mThread1.start();
    
            mThread2.start();
    
            for(int i=0;i<10;i++){
    
                System.out.println(Thread.currentThread().getName()+"::"+i);
    
            }
    
        }
    
        
    
        
    
    }

    一般我们使用第二种方式实现多线程,实现Runnable接口的方式相比较继承Thread类的方式有两个好处:

        1.避免由于java单继承带来的局限性.

       2.适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码,数据,有效分离,较好地体现了面向对象的思想.

    线程调度:

    线程有两种调度模型:

        1.分式调度模型,所有的线程轮流使用CPU的使用权,平均分配每个线程占用的CPU时间片.

        2.抢占式调度模型,优先让优先级高的线程先执行,如果优先级相同,那么随机选择一个线程,优先级高的线程获得CPU的执行权的几率大一些,并不是严格按照优先级由高到低执行.

    java使用的是抢占式调度模型.

    方法介绍:

    获取当前线程对象的优先级,默认优先级为5,范围是1~10 int  getPriority()
    设置当前线程的优先级,范围1~10    void  setPriority()
    在指定毫秒内让正在运行的线程休眠(暂停执行) static void sleep(1000)
    ----------------------------------------------------- -----------------------------------
    等待当前线程执行结束,该方法一般紧随start()方法之后 final void join()
    暂停当前正在执行的对象,执行其他线程,(礼让线程) static void yield()

    将该线程标记为守护线程或用户线程。

    当正在运行的线程都是守护线程时,Java 虚拟机退出。

    该方法必须在启动线程前调用。

    final void setDaemon(true)

    看这张图,来更好的理解守护线程:

    Image 2

    假设现在有三个线程分别叫关羽,刘备,张飞.将关羽,张飞设置为守护线程,一旦刘备这个线程执行完毕(死掉),那么关羽,张飞也就没有活着的意义了(不再执行,退出).

    中断线程,把线程状态终止,并抛出一个InterruptedException         void interrupt()

    线程的生命周期:

    Image 3

    案例练习:

    电影院卖票,假设有三个窗口同时卖票,共有100张票,用多线程模拟这个过程(要解决的关键问题是:线程同步).

    package com.wang.reflect;
    
    
    
    class SellTicket implements Runnable {
    
        // 共享变量
    
        private int tickets = 100;
    
        // 创建锁对象
    
        private Object obj = new Object();
    
    
    
        @Override
    
        public void run() {
    
            while (true) {
    
                /**
    
                 * 解决线程同步问题的 方案:
    
                 *     synchronized(锁对象){
    
                 *        //操作共享变量的代码
    
                 *  }
    
                 * 
    
                 */
    
                synchronized (obj) {
    
                    if (tickets > 0) {
    
                        System.out.println(Thread.currentThread().getName()
    
                                + "正在出售第" + (tickets--) + "张票");
    
    
    
                    }
    
                    try {
    
                        Thread.sleep(100);
    
                    } catch (InterruptedException e) {
    
                        // TODO Auto-generated catch block
    
                        e.printStackTrace();
    
                    }
    
                }
    
                
    
            }
    
        }
    
    }
    
    
    
    public class SellTicketDemo {
    
    
    
        public static void main(String[] args) {
    
            SellTicket st = new SellTicket();
    
            Thread t1 = new Thread(st, "窗口1");
    
            Thread t2 = new Thread(st, "窗口2");
    
            Thread t3 = new Thread(st, "窗口3");
    
            t1.start();
    
            t2.start();
    
            t3.start();
    
    
    
        }
    
    }
    

    打印结果如下:

    Image 4

    synchronized是一个关键字.可以加在一段代码外部,也可以放在一个方法前.它的锁对象可以是任意对象,当然也可以写成当前类的对象this.

    • 同步代码块的锁对象:任意对象
    • 同步方法的锁对象:当前对象this
    • 同步静态方法的锁对象:当前类的字节码对象,类名.class;

    java类库中线程安全的类有:StringBuffer,HashTable,Vector.

    注意Vector是线程安全的,但是当我们需要一个线程安全的集合的时候,我们一般也不用它,在Collections类中获取线程安全的list集合的方法,假设我们现在需要获得一个线程安全的List<String>集合,可以这样写:

    List<String> list=Collections.synchronizedList(new ArrayList<String>());

         虽然说我们可以理解同步代码块和同步方法的锁对象问题,但我们并没有直接看到在哪儿加上锁又在哪儿释放锁,为了更清晰的表达如何加锁和释放锁,JDK1.5以后提供了一个新的锁对象Lock.下面通过Lock锁实现上述卖票的过程.

    package com.wang.reflect;
    
    
    
    import java.util.ArrayList;
    
    import java.util.Collections;
    
    import java.util.concurrent.locks.Lock;
    
    import java.util.concurrent.locks.ReentrantLock;
    
    
    
    class SellTicket implements Runnable {
    
        // 共享变量
    
        private int tickets = 100;
    
        // Lock是一个接口,ReentrantLock是他的一个实现类
    
        private Lock lock=new ReentrantLock();
    
    
    
        @Override
    
        public void run() {
    
            while (true) {
    
    
    
                lock.lock();//加锁
    
                if (tickets > 0) {
    
                    System.out.println(Thread.currentThread().getName() + "正在出售第"
    
                            + (tickets--) + "张票");
    
    
    
                }
    
                lock.unlock();//释放锁
    
                try {
    
                    Thread.sleep(100);
    
                } catch (InterruptedException e) {
    
                    e.printStackTrace();
    
                }
    
            }
    
    
    
        }
    
    }
    
    
    
    public class SellTicketDemo {
    
    
    
        public static void main(String[] args) {
    
            SellTicket st = new SellTicket();
    
            Thread t1 = new Thread(st, "窗口1");
    
            Thread t2 = new Thread(st, "窗口2");
    
            Thread t3 = new Thread(st, "窗口3");
    
            t1.start();
    
            t2.start();
    
            t3.start();
    
    
    
        }
    
    }
    

    线程同步有两个很明显的弊:一是效率低,二是容易产生死锁.

    死锁问题:

    两个或两个以上的线程,在争夺资源的过程中,发生的一种相互等待的过程.下面通过代码演示死锁问题的存在:

    package com.wang.reflect;
    
    
    
    
    
    class DieLock extends Thread{
    
        private boolean flag;
    
        public DieLock(boolean flag){
    
            this.flag=flag;
    
        }
    
        public static final Object o1=new Object();
    
        public static final Object o2=new Object();
    
        @Override
    
        public void run() {
    
            if(flag){
    
                synchronized(o1){
    
                    System.out.println("if o1");
    
                    synchronized (o2) {
    
                        System.out.println("if o2");
    
                    }
    
                }
    
                
    
            }else{
    
                synchronized (o2) {
    
                    System.out.println("else o2");
    
                    synchronized (o1) {
    
                        System.out.println("else o1");
    
                    }
    
                }
    
            }
    
        }
    
    }
    
    public class DieLockDemo {
    
    
    
        public static void main(String[] args) {
    
            DieLock dl1=new DieLock(true);
    
            DieLock dl2=new DieLock(false);
    
            dl1.start();
    
            dl2.start();
    
        }
    
    }
    

    细读代码,可以发现,理想状态下,应该会打印:

    if o1

    if o2

    else o2

    else o1

    但是运行之后,发现打印结果大多为:

    if o1             或者     else o2

    else o2                    if  o1

    其实这里就发生了线程互相等待,也就是死锁问题.

    生产者-消费者模式描述:

    package com.wang.reflect;
    
    
    
    //学生实体类作为共享资源
    
    class Student{
    
        String name;//姓名
    
        int age;//年龄
    
        boolean flag;//标记变量,判断当前学生对象是否已创建赋值好
    
        
    
    }
    
    //模拟生产者线程类
    
    class SetStudent implements Runnable{
    
    
    
        //共享资源s
    
        private Student s;
    
        private int x=0;
    
        
    
        public SetStudent(Student s) {
    
            this.s=s;
    
        }
    
        
    
        @Override
    
        public void run() {
    
            while(true){
    
                
    
                synchronized (s) {
    
                    if(s.flag){
    
                        try {
    
                            s.wait();
    
                        } catch (InterruptedException e) {
    
                            e.printStackTrace();
    
                        }
    
                    }
    
                    
    
                    if(x%2==0){
    
                    s.name="郭靖";
    
                    s.age=24;
    
                    }else{
    
                        s.name="黄蓉";
    
                        s.age=18;
    
                    }
    
                    x++;
    
                    //生产完之后,将标记置为true,通知消费者来消费
    
                    s.flag=true;
    
                    //唤醒线程
    
                    s.notify();
    
                }
    
            }
    
        }
    
        
    
    }
    
    
    
    //模拟消费者线程类
    
    class GetStudent implements Runnable{
    
    
    
            //共享资源s
    
            private Student s;
    
            
    
            
    
            public GetStudent(Student s) {
    
                this.s=s;
    
            }
    
        @Override
    
        public void run() {
    
            while(true){
    
                synchronized (s) {
    
                    if(!s.flag){
    
                        try {
    
                            s.wait();
    
                        } catch (InterruptedException e) {
    
                            e.printStackTrace();
    
                        }
    
                    }
    
                    System.out.println(s.name+":::"+s.age);
    
                    
    
                    //消费完以后将标记之置于false,通知生产者来生产
    
                    s.flag=false;
    
                    //唤醒线程
    
                    s.notify();
    
                }
    
            }
    
        }
    
    }
    
    //测试类
    
    public class StudentDemo {
    
    
    
        public static void main(String[] args) {
    
            Student s =new Student();
    
            
    
            SetStudent ss=new SetStudent(s);
    
            GetStudent gs=new GetStudent(s);
    
            
    
            Thread t1=new Thread(ss, "生产者");
    
            Thread t2=new Thread(gs, "消费者");
    
            
    
            t1.start();
    
            t2.start();
    
        }
    
    }
    

    在上面的代码中,生产者就是代表对Student属性进行赋值,消费者就是代表对Studnet属性进行打印,代码设置了等待唤醒机制.

    上述代码可以优化的,可以讲生产者的赋值行为,和消费者的打印行为,封装到Student类中,写成两个同步方法.实现这两个功能.代码如下:

    package com.wang.reflect;
    
    
    
    //学生实体类作为共享资源
    
    class Student {
    
        private String name;// 姓名
    
        private int age;// 年龄
    
        boolean flag;// 标记变量,判断当前学生对象是否已创建赋值好
    
    
    
        public synchronized void set(String name, int age) {
    
    
    
            if (this.flag) {
    
                try {
    
                    this.wait();
    
                } catch (InterruptedException e) {
    
                    e.printStackTrace();
    
                }
    
            }
    
            this.name = name;
    
            this.age = age;
    
    
    
            this.flag = true;
    
            this.notify();
    
        }
    
    
    
        public synchronized void get() {
    
            if (!this.flag) {
    
                try {
    
                    this.wait();
    
                } catch (InterruptedException e) {
    
                    e.printStackTrace();
    
                }
    
            }
    
    
    
            System.out.println(name + ":::" + age);
    
    
    
            this.flag = false;
    
            this.notify();
    
        }
    
    
    
    }
    
    
    
    // 模拟生产者线程类
    
    class SetStudent implements Runnable {
    
    
    
        // 共享资源s
    
        private Student s;
    
        private int x = 0;
    
    
    
        public SetStudent(Student s) {
    
            this.s = s;
    
        }
    
    
    
        @Override
    
        public void run() {
    
            while (true) {
    
                if (x % 2 == 0) {
    
                    s.set("郭靖", 27);
    
                } else {
    
                    s.set("黄蓉", 18);
    
                }
    
                x++;
    
            }
    
        }
    
    
    
    }
    
    
    
    // 模拟消费者线程类
    
    class GetStudent implements Runnable {
    
    
    
        // 共享资源s
    
        private Student s;
    
    
    
        public GetStudent(Student s) {
    
            this.s = s;
    
        }
    
    
    
        @Override
    
        public void run() {
    
            while (true) {
    
                s.get();
    
            }
    
        }
    
    
    
    }
    
    
    
    // 测试类
    
    public class StudentDemo {
    
    
    
        public static void main(String[] args) {
    
            Student s = new Student();
    
    
    
            SetStudent ss = new SetStudent(s);
    
            GetStudent gs = new GetStudent(s);
    
    
    
            Thread t1 = new Thread(ss, "生产者");
    
            Thread t2 = new Thread(gs, "消费者");
    
    
    
            t1.start();
    
            t2.start();
    
        }
    
    
    
    }
    
  • 相关阅读:
    Maven pom.xml中添加指定的中央仓库
    命令行远程链接MySQL
    A required class was missing while executing org.apache.maven.plugins:maven-war-plugin:2.1.1:war
    mvn deploy命令上传包
    保存好你的密码
    PuTTY免输密码自动登录Linux
    ActiveMQ无法启动
    linux控制台批量杀进程
    dubbo入门之微服务客户端服务端配置
    dubbo入门之helloWorld
  • 原文地址:https://www.cnblogs.com/fingerboy/p/5347314.html
Copyright © 2020-2023  润新知