——出现编写错误,请评论区留下反馈,谢谢!


多线程的引入

  • 线程是程序执行的一条路径,一个进程中可以包含多条线程。
  • 多线程并发执行可以提高程序的效率,可以同时完成多项工作。

应用场景:

  • 红蜘蛛同时共享屏幕给多个电脑
  • 迅雷开启多条线程一起下载
  • QQ同时和多个人一起视频
  • 服务器同时处理多个客户端请求

多线程并行和并发的区别

  • 并行是两个任务同时运行(需要多核CPU)
  • 并发是指两个任务都请求运行,而处理器只能接受一个任务(两个任务安排轮流进行,由于时间间隔较短,使人感觉两个任务都在运行)

例如:

  • 并行 一个人与两个网友聊天,左手一个电脑跟甲聊,同时右手另一台电脑跟乙聊
  • 并发一个电脑先给甲发个消息,再给乙发个消息…

Java程序运行原理和JVM的启动

Java程序运行原理

  • Java命令会启动java虚拟机(JVM),等于启动一个应用程序,也就是启动一个进程. 该进程会自动启动一个"主线程",然后主线程会去调用类的main方法.

JVM的启动是多线程吗?

  • JVM启动至少启动垃圾回收线程主线程(main),因此是多线程.

示例:

public class DemoThread {
/**
     * @param args
     * 证明JVM是多线程
     */
    public static void main(String[] args) {
        for (int i = 0; i < 1000000 ; i++) {
            new Demo();
        }

        for (int i = 0; i < 100 ; i++) {
            System.out.println(i + "我是主线程的执行代码!");
        }
    }
}

class Demo {
  @Override
    protected void finalize(){
        System.out.println("垃圾清扫!");
    }
}

多线程实现方式

继承Thread

    public class Demo2_Thread {
      /**
     * @param args
     * Thread继承实现方式
     */
    public static void main(String[] args) {
        //4、创建Thread子类对象
        MyThread mt = new MyThread();
        //5、开启线程
        mt.start();
    }
}
//1、继承Thread
class MyThread extends Thread {
    //2、重写run()方法
    @Override
    public void run() {
        //3、将要执行的代码
        System.out.println("线程代码!");
        }
    }

实现Runnable接口

注意:

  • Runnable接口只存在run()方法
    public class Demo3_Thread {
         /**
         * @param args
         * Runnable接口实现方式
         */
        public static void main(String[] args) {
            //4、创建Runnable子类对象
            MyRunnable mr = new MyRunnable();
            //5、子类对象传递给Thread的构造函数
            Thread t = new Thread(mr);
            //6、启动线程
            t.start();
        }
    }
    //1、定义一个类实现Runnable接口
    class MyRunnable implements Runnable {
         //2、重写run方法
        @Override
        public void run() {
            //3、运行代码
            System.out.println("线程测试!");
        }
    }

源码解析:

  • 第五步作为参数传递给Thread构造方法,为何第六步的start()调用的是子类的run()方法
  • 调用Thread(Runnable target)构造方法
public Thread(Runnable target) {
       init(null, target, "Thread-" + nextThreadNum(), 0);
}
  • 调用init(…….)方法,传递target等参数
private void init(ThreadGroup g, Runnable target, String name,long stackSize) {
        init(g, target, name, stackSize, null, true);
    }
  • 将局部变量赋值与全局变量
private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc,boolean inheritThreadLocals) {
......
this.target = target;
.....
}
  • 查看源码中的run()方法,当target不为null时,调用run()方法
 public void run() {
     if (target != null) {
       target.run();
     }
 }

多线程实现方式的区别

  • 继承Thread:子类重写Thread类的run(),当调用start()时,直接找子类的run()方法(代码简洁)
  • 实现Runnable:Thread构造函数传入Runnable的引用,赋值与成员变量,start()调用run()方法时判断内部成员变量的Runnable的引用是否为空,不空时,调用Runnable的run()方法(扩展性好)

匿名内部类的实现方式

注意:

  • 匿名内部类使代码简洁
public class DemoThread {
    /**
     * @param args
     * 线程的匿名内部类实现方式
     */
    public static void main(String[] args) {
       // 继承Thread
       new Thread() {
           public void run() {
               System.out.println("继承Thread的匿名内部类!");
           }
       }.start();

       // 实现Runnable
       new Thread(new Runnable() {
            public void run() {
                System.out.println("实现Runnable的匿名内部类!");
            }
        }).start();
    }
}

实现Callable | 实现线程

public class DemoThread {
    /**
     * @param args
     * Callable实现方式
     */

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService pool = Executors.newFixedThreadPool(2);
        // 提交线程
        Future<Integer> f1 = pool.submit(new MyCallable(100));
        Future<Integer> f2 = pool.submit(new MyCallable(50));

        System.out.println("f1:" + f1.get() + ",f2:" + f2.get());
        // 终止线程池
        pool.shutdown();
    }
class MyCallable implements Callable<Integer> {
    private int  number;
    public MyCallable(int number) {
        this.number = number;
    }
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 0; i < number ; i++) {
            sum += i;
        }
        return sum;
    }
}

同步代码块

  • 当多线程并发,有多段代码同时执行时,我们希望某一段代码执行过程中CPU不进行切换到其他线程工作,此为同步。
  • 如果两段代码是同步的,那么同一时间只能执行一段,在一段没执行结束之前,不会执行另外一段代码。
public class DemoThread {
    /**
     * @param args
     * 同步代码块
     */
    public static void main(String[] args)  {
        final Printer p = new Printer();

        new Thread() {
            public void run() {
                while(true) {
                    p.print1();
                }
            }
        }.start();

        new Thread() {
            public void run() {
                while(true) {
                    p.print2();
                }
            }
        }.start();
    }
}
class Printer {
    Demo d = new Demo();
    public void print1() {
        synchronized (d) {
            System.out.print("小");
            System.out.print("龙");
            System.out.print("人");
            System.out.print("\r\n");
        }
    }
    public void print2() {
        synchronized (d) {
            System.out.print("小");
            System.out.print("龙");
            System.out.print("人");
            System.out.print("嘿");
            System.out.print("嘿");
            System.out.print("\r\n");
        }
    }
}
class Demo {}

注意:

  • 同步代码块,锁机制,锁对象可以是任意的(锁对象必须为同一个对象)
  • 锁对象不能为匿名对象(因匿名对象为两个不同的对象)

同步方法

非静态同步函数

非静态同步方法锁对象是this

class Printer {
    public synchronized void print1() {
        System.out.print("小");
        System.out.print("龙");
        System.out.print("人");
        System.out.print("\r\n");
    }
    public void print2() {
        synchronized (this) {
            System.out.print("小");
            System.out.print("龙");
            System.out.print("人");
            System.out.print("嘿");
            System.out.print("嘿");
            System.out.print("\r\n");
        }
    }
}

静态同步函数

静态同步函数锁对象是字节码对象

class Printer {
    public static synchronized void print1() {
        System.out.print("小");
        System.out.print("龙");
        System.out.print("人");
        System.out.print("\r\n");
    }
    public static void print2() {
        synchronized (Printer.class) {
            System.out.print("小");
            System.out.print("龙");
            System.out.print("人");
            System.out.print("嘿");
            System.out.print("嘿");
            System.out.print("\r\n");
        }
    }
}

死锁

  • 多线程同步的时候,如果同步代码嵌套,使用相同锁,就有可能出现死锁
public class DemoThread {
    /**
     * @param args
     * 死锁
     */
    private static String s1 = "筷子左";
    private static String s2 = "筷子右";
    public static void main(String[] args) {
        new Thread() {
            public void run() {
                while (true) {
                    synchronized (s1) {
                        System.out.println(this.getName() + "拿到" + s1 + ",等待" + s2);
                        synchronized (s2) {
                            System.out.println(this.getName() + "拿到"+ s2 +",开吃!");
                        }
                    }
                }
            }
        }.start();
        new Thread() {
            public void run() {
                while (true) {
                    synchronized (s2) {
                        System.out.println(this.getName() + "拿到" + s2 + ",等待" + s1);
                        synchronized (s1) {
                            System.out.println(this.getName() + "拿到"+ s1 +",开吃!");
                        }
                    }
                }
            }
        }.start();
    }
}

线程通信

两个线程间通信

public class DemoThread {
    /**
     * @param args
     * 线程间通信
     */
    public static void main(String[] args) {
        final Printer p = new Printer();
        new Thread() {
            public  void  run() {
                while(true) {
                    try {
                        p.print1();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
        new Thread() {
            public  void  run() {
                while(true) {
                    try {
                        p.print2();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
    }
}
class Printer {
    private int flag = 1;
    public void print1() throws InterruptedException {
        synchronized (this) {
            if(flag != 1) {
                this.wait();
            }
            System.out.print("小");
            System.out.print("龙");
            System.out.print("人");
            flag = 2;
            this.notify();
        }
    }

    public void print2() throws InterruptedException {
        synchronized (this) {
            if(flag != 2) {
                this.wait();
            }
            System.out.print("嘿");
            System.out.print("嘿");
            System.out.print("\r\n");
            flag = 1;
            this.notify();
        }
    }
}
  • notify()随机唤醒一个线程程
  • wait()当前线程等待

三个或三个线程以上线程通信

JDK1.5之后ReentrantLock类可以替代synchronized使用

public class DemoThread {
    /**
     * @param args
     * 死锁
     */

    public static void main(String[] args) {
        final Printer p = new Printer();
        new Thread() {
            public  void  run() {
                while(true) {
                    try {
                        p.print1();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();

        new Thread() {
            public  void  run() {
                while(true) {
                    try {
                        p.print2();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();

        new Thread() {
            public  void  run() {
                while(true) {
                    try {
                        p.print3();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
    }
}

class Printer {
    private ReentrantLock r = new ReentrantLock();
    private Condition c1 = r.newCondition();
    private Condition c2 = r.newCondition();
    private Condition c3 = r.newCondition();
    private int flag = 1;
    public void print1() throws InterruptedException {
        r.lock();
            if(flag != 1) {
                c1.await();
            }
            System.out.print("小");
            flag = 2;
            c2.signal();
        r.unlock();
    }

    public void print2() throws InterruptedException {
        r.lock();
            if(flag != 2) {
                c2.await();
            }
            System.out.print("龙");
            flag = 3;
            c3.signal();
        r.unlock();
    }

    public void print3() throws InterruptedException {
        r.lock();
            if(flag != 3) {
                c3.await();
            }
            System.out.print("人");
            System.out.print("\r\n");
            flag = 1;
            c1.signal();
        r.unlock();
    }
}

线程组

  • Java中使用ThreadGroup表示线程组,可以对一批线程进行分类管理,java允许程序直接对线程组进行控制
  • 默认情况下,所有的线程都属于主线程

线程生命周期

enter image description here


线程池

  • 程序启动一个新线程成本比较高,因其设计要与操作系统交互。而使用线程池可以很好提高性能,尤其是当程序要创建大量生存期很短的线程时,更应考虑使用线程池。
  • 线程池中每一个线程代码结束后,并不会死亡,而是处于空闲状态,等待下一个对象使用
 // 创建一个线程池
 ExecutorService pool = Executors.newFixedThreadPool(2);
 // 提交线程
 pool.submit(new MyRunnable());
 pool.submit(new MyRunnable());
 // 关闭线程池
 pool.shutdown();

常见线程安全类

  • Vector是线程安全,ArrayList是线程不安全
  • StringBuffer是线程安全,StringBuilder**是线程不安全
  • HashTabke是线程安全,HashMap是线程不安全

常用函数

  • getName()获取线程名
  • setName()设置线程名
  • currentThread()获取当前线程
  • sleep()休眠多少毫秒
  • setDaemon()设置为守护线程(当非守护线程结束后,守护线程结束)
  • join()当前线程暂停,等指定线程结束后再执行
  • yield()让出CPU
  • setPriority()设置线程优先级

注意问题

  • 在同步代码块中,用哪个对象锁,就用那个对象调用wait方法

为什么wait方法和notify方法定义在Object类中?

  • 因为锁对象为任意对象,Object是所有类的基类,所以wait方法和notify方法需要定义在Object这个类中

sleep方法和wait方法区别?

  • sleep方法必须传入参数,时间到后自动结束休眠;wait方法可以传入参数或不传入参数,传入参数是在时间结束后进入等待,不传入参数是立即等待
  • sleep方法在同步函数或同步代码块中休眠,不释放锁;wait方法是等待并释放锁
最后修改日期:2019年4月30日

留言

撰写回覆或留言

发布留言必须填写的电子邮件地址不会公开。