java 多线程总结

线程是程序执行的一条路径, 一个进程中可以包含多条线程

多线程并发执行可以提高程序的效率, 可以同时完成多项工作

多线程并行和并发的区别
* 并行就是两个任务同时运行,就是甲任务进行的同时,乙任务也在进行。(需要多核CPU)
* 并发是指两个任务都请求运行,而处理器只能按受一个任务,就把这两个任务安排轮流进行,由于时间间隔较短,使人感觉两个任务都在运行。

例如,图A为进程的并发性,图B为进程的并行性.

Java程序运行原理
    * Java命令会启动java虚拟机,启动JVM,等于启动了一个应用程序,也就是启动了一个进程。该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 main方法。

JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的。

 

多线程程序实现的方式:

1.继承Thread
    * 定义类继承Thread
    * 重写run方法
    * 把新线程要做的事写在run方法中
    * 创建线程对象
    * 开启新线程, 内部会自动执行run方法

2.实现Runnable
    * 定义类实现Runnable接口
    * 实现run方法
    * 把新线程要做的事写在run方法中
    * 创建自定义的Runnable的子类对象
    * 创建Thread对象, 传入Runnable
    * 调用start()开启新线程, 内部会自动调用Runnable的run()方法

实现Runnable的原理:
    * 1,看Thread类的构造函数,传递了Runnable接口的引用 
    * 2,通过init()方法找到传递的target给成员变量的target赋值
    * 3,查看run方法,发现run方法中有判断,如果target不为null就会调用Runnable接口子类对象的run方法

创造新线程的两种方式的区别:

* 查看源码的区别:
    * a.继承Thread : 由于子类重写了Thread类的run(), 当调用start()时, 直接找子类的run()方法
    * b.实现Runnable : 构造函数中传入了Runnable的引用, 成员变量记住了它, start()调用run()方法时内部判断成员变量Runnable的引用是否为空, 不为空编译时看的是Runnable的run(),运行时执行的是子类的run()方法
    
* 继承Thread
    * 好处是:可以直接使用Thread类中的方法,代码简单
    * 弊端是:如果已经有了父类,就不能用这种方法
* 实现Runnable接口
    * 好处是:即使自己定义的线程类有了父类也没关系,因为有了父类也可以实现接口,而且接口是可以多实现的
    * 弊端是:不能直接使用Thread中的方法需要先获取到线程对象后,才能得到Thread的方法,代码复杂

 

匿名内部类实现线程的两种方式:

* 继承Thread类
         
        new Thread() {                                                    //1,new 类(){}继承这个类
            public void run() {                                            //2,重写run方法
                for(int i = 0; i < 1000; i++) {                            //3,将要执行的代码,写在run方法中
                    System.out.println("a");
                }
            }
        }.start();
* 实现Runnable接口
            
        new Thread(new Runnable(){                                        //1,new 接口(){}实现这个接口
            public void run() {                                            //2,重写run方法
                for(int i = 0; i < 1000; i++) {                            //3,将要执行的代码,写在run方法中
                    System.out.println("b");
                }
            }
        }).start(); 

获取线程名字和设置名字:

* 1.获取名字
    * 通过getName()方法获取线程对象的名字
* 2.设置名字
    * 通过构造函数可以传入String类型的名字或者setName()方法

获取当前线程的对象:

Thread.currentThread();

休眠线程:

Thread.sleep(), 控制当前线程休眠若干毫秒

守护线程:

setDaemon(), 设置一个线程为守护线程, 该线程不会单独执行, 当其他非守护线程都执行结束后, 自动退出

加入线程:

* join(), 当前线程暂停, 等待指定的线程执行结束后, 当前线程再继续
* join(int), 可以等待指定的毫秒之后继续

礼让线程:

yield让出cpu

设置线程的优先级:

 setPriority()设置线程的优先级

同步代码块:
    * 使用synchronized关键字加上一个锁对象来定义一段代码, 这就叫同步代码块
    * 多个同步代码块如果使用相同的锁对象, 那么他们就是同步的

锁对象可以是任意对象,但是被锁的代码需要保证是同一把锁,不能用匿名对象

同步方法:

使用synchronized关键字修饰一个方法, 该方法中所有的代码都是同步的

锁对象可以是任意对象,但是被锁的代码需要保证是同一把锁,不能用匿名对象

 非静态同步函数的锁是:this
 静态的同步函数的锁是:字节码对象

线程安全问题:

* 多线程并发操作同一数据时, 就有可能出现线程安全问题
* 使用同步技术可以解决这种问题, 把操作数据的代码进行同步, 不要多个线程一起操作

如果用引用数据类型成员变量当作锁对象,必须是静态的

死锁:

 * 多线程同步的时候, 如果同步代码嵌套, 使用相同锁, 就有可能出现死锁
 * 尽量不要嵌套使用

线程安全的类:

Vector,StringBuffer,Hashtable,Collections.synchronized(xxx)(将线程不安全的类转变成线程安全的类)

* Vector是线程安全的,ArrayList是线程不安全的
* StringBuffer是线程安全的,StringBuilder是线程不安全的
* Hashtable是线程安全的,HashMap是线程不安全的

两个线程间的通信:

1.什么时候需要通信
    * 多个线程并发执行时, 在默认情况下CPU是随机切换线程的
    * 如果我们希望他们有规律的执行, 就可以使用通信, 例如每个线程执行一次打印

2.怎么通信
    * 如果希望线程等待, 就调用wait()
    * 如果希望唤醒等待的线程, 就调用notify();
    * 这两个方法必须在同步代码中执行, 并且使用同步锁对象来调用

三个或三个以上间的线程通信:

* 多个线程通信的问题
    * notify()方法是随机唤醒一个线程
    * notifyAll()方法是唤醒所有线程
    * JDK5之前无法唤醒指定的一个线程
    * 如果多个线程之间通信, 需要使用notifyAll()通知所有线程, 用while来反复判断条件

在同步代码块中,用哪个对象锁,就用哪个对象调用wait方法
为什么wait方法和notify方法定义在Object类中?

因为锁对象可以是任意对象,Object是所有的类的基类,所以wait方法和notify方法需要定义在Object这个类中
sleep方法和wait方法的区别?
sleep方法必须传入参数,参数就是时间,时间到了自动醒来
wait方法可以传入参数也可以不传入参数,传入参数就是在参数的时间结束后等待,不传入参数就是直接等待
sleep方法在同步函数或同步代码块中,不释放锁,睡着了也抱着锁睡
wait方法在同步函数或者同步代码块中,释放锁

JDK1.5的新特性互斥锁:

* 1.同步
    * 使用ReentrantLock类的lock()和unlock()方法进行同步
* 2.通信
    * 使用ReentrantLock类的newCondition()方法可以获取Condition对象
    * 需要等待的时候使用Condition的await()方法, 唤醒的时候用signal()方法
    * 不同的线程使用不同的Condition, 这样就能区分唤醒的时候找哪个线程了

线程组的概述和使用:

线程组概述
    * Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。
    * 默认情况下,所有的线程都属于主线程组。
        * public final ThreadGroup getThreadGroup()//通过线程对象获取他所属于的组
        * public final String getName()//通过线程组对象获取组的名字
    * 我们也可以给线程设置分组
        * 1,ThreadGroup(String name) 创建线程组对象并给其赋值名字
        * 2,创建线程对象
        * 3,Thread(ThreadGroup?group, Runnable?target, String?name) 
        * 4,设置整组的优先级或者守护线程

线程的五种状态:

新建,就绪,运行,阻塞,死亡

 

线程池的概述和使用:

线程池概述
    * 程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池
内置线程池的使用概述
    * JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法
        * public static ExecutorService newFixedThreadPool(int nThreads)
        * public static ExecutorService newSingleThreadExecutor()
        * 这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。它提供了如下方法
        * Future<?> submit(Runnable task)
        * <T> Future<T> submit(Callable<T> task)
    * 使用步骤:
        * 创建线程池对象
        * 创建Runnable实例
        * 提交Runnable实例
        * 关闭线程池

提交的是Runnable:

// public static ExecutorService newFixedThreadPool(int nThreads)
        ExecutorService pool = Executors.newFixedThreadPool(2);

        // 可以执行Runnable对象或者Callable对象代表的线程
        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());

        //结束线程池
        pool.shutdown();

提交的是Callable:

// 创建线程池对象
        ExecutorService pool = Executors.newFixedThreadPool(2);

        // 可以执行Runnable对象或者Callable对象代表的线程
        Future<Integer> f1 = pool.submit(new MyCallable(100));
        Future<Integer> f2 = pool.submit(new MyCallable(200));

        // V get()
        Integer i1 = f1.get();
        Integer i2 = f2.get();

        System.out.println(i1);
        System.out.println(i2);

        // 结束
        pool.shutdown();

        public 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 x = 1; x <= number; x++) {
                    sum += x;
                }
                return sum;
            }
        
        }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值