一.线程的概念1.线程线程是程序执行的一条路径, 一个进程中可以包含多条线程多线程并发执行可以提高程序的效率, 可以同时完成多项工作2.多线程的应用场景迅雷开启多条线程一起下载QQ同时和多个人一起视频服务器同时处理多个客户端请求二.开启新线程1.继承Thread定义类继承Thread重写run方法把新线程要做的事写在run方法中创建线程对象开启新线程, 内部会自动执行run方法2.实现Runnable定义类实现Runnable接口实现run方法把新线程要做的事写在run方法中创建自定义的Runnable对象创建Thread对象, 传入Runnable调用start()开启新线程, 内部会自动调用Runnable的run()方法3.两种方式的区别区别一:a.由于子类重写了Thread类的run(), 当调用start()时, 直接找子类的run()方法b.构造函数中传入了Runnable的引用, 成员变量记住了它, start()调用run()方法时内部判断成员变量Runnable是否为空, 不为空则执行Runnable的run()区别二:a.继承Thread只能是单继承,如果自己定义的线程类已经有了父类,就不能再继承了b.实现Runnable接口可以多实现,即使自己定义线程类已经有父类可以实现Runnable接口总结:继承Thread的好处是:可以直接使用Thread类中的方法,代码简单弊端是:如果已经有了父类,就不能用这种方法实现Runnable接口的好处是:即使自己定义的线程类有了父类也没关系,因为有了父类也可以实现接口,而且接口是可以多实现的弊端是:不能直接使用Thread中的方法需要先获取到线程对象后,才能得到Thread的方法,代码复杂4.线程的优先级JVM会对所有的线程,进行默认的级别
分成三个级别
最低 -- 1
默认 -- 5
最高 -- 10
线程的优先级越高,莫一时刻获得CPU的时间片的机会就越多
获取优先级 int getPriority()三.Thread类常用方法1.设置名字通过构造函数可以传入String类型的名字通过setName(String)方法可以设置线程对象的名字2.获取名字通过getName()方法获取线程对象的名字3.获取当前线程对象Thread.currentThread(), 主线程也可以获取4.休眠Thread.sleep(毫秒,纳秒), 控制当前线程休眠若干毫秒1秒= 1000毫秒 1秒 = 1000 * 1000* 1000纳秒 1000000000public class ThreadDemo3 { public static void main(String[] args)throws Exception { /*for(int x = 0 ; x < 20 ;x++){ //线程休眠 Thread.sleep(100); System.out.println("main.."+x); }*/ new SleepThread().start(); } }
5.守护setDaemon(), 设置一个线程为守护线程, 该线程不会单独执行, 当其他非守护线程都执行结束后, 自动退出public static void main(String[] args) throws Exception{ //创建线程对象,变成后台线程 DaemonThread t0 = new DaemonThread(); //setDaemon 必须在start开始前设置 t0.setDaemon(true); t0.start(); }
6.加入join(), 当前线程暂停, 等待指定的线程执行结束后, 当前线程再继续join(int), 可以等待指定的毫秒之后继续public class ThreadDemo4 { public static void main(String[] args) throws Exception{ //创建JoinThread线程对象 JoinThread j0 = new JoinThread(); JoinThread j1 = new JoinThread(); j0.start(); j0.join(); j1.start(); for(int x = 0 ; x < 100; x++) System.out.println(Thread.currentThread().getName()+" "+x); } }
7.让步Thread类的静态方法 yield() 指方法写在哪个线程中,哪个线程就会让出自己的CPU资源public static void main(String[] args) { //创建2个线程对象 YieldThread t0 = new YieldThread(); YieldThread t1 = new YieldThread(); t0.start(); t1.start(); }
四.线程之间的同步1.哪些情况下需要同步当多线程并发, 有多段代码同时执行,或者多线程共享同一数据时,我们希望某一段代码执行的过程中CPU不要切换到其他线程工作. 这时就需要同步.如果两段代码是同步的, 那么同一时间只能执行一段, 在一段代码没执行结束之前, 不会执行另外一段代码.2.同步代码块使用synchronized关键字加上一个锁对象来定义一段代码, 这就叫同步代码块多个同步代码块如果使用相同的锁对象, 那么他们就是同步的3.同步方法使用synchronized关键字修饰一个方法, 该方法中所有的代码都是同步的非静态同步方法默认使用当前对象this作为锁对象静态方法同步默认的锁对象是,所在类的字节码对象4.线程安全问题多线程并发操作同一数据时, 就有可能出现线程安全问题使用同步技术可以解决这种问题, 把操作数据的代码进行同步, 不要多个线程一起操作5.死锁问题多线程同步的时候, 如果同步代码嵌套, 使用相同锁, 就有可能出现死锁尽量不要嵌套使用五.线程的生命周期
六.线程之间的通信针对同一资源的操作有不同种类的线程,需要线程之间的通信.可以通过设置线程和获取线程针对同一个 对象进行操作.如果希望线程等待,可以调用wait()方法;如果希望唤醒等待的线程,可以调用notify()方法; 以上两个方法必须在同步代码中执行,并且使用同步锁对象来调用.注意:notify()方法是随机唤醒一个线程notifyAll()方法是唤醒所有线程JDK5之前无法唤醒指定的一个线程如果多个线程之间通信,需要使用notifyAll()通知所有线程,用while来反复判断条件.JDK5之后的线程控制: 1.同步时,可以使用ReentrantLock类的lock()和unlock()方法进行同步2.通信时,使用ReentrantLock类的newCondition()方法可以获取Conditioon对 象.需要等待的时候使用Condition的await()方法,唤醒的时候用signal()方 法.不同的线程使用不同的Condition,这样就能区分唤醒的时候找哪个线程.下面来代码解释下单例设计模式的懒汉式和饿汉式public class Demo4_Single { /*单例设计模式
<span style="white-space:pre"> </span> *保证类在内存中只有一个对象
八.线程组,线程池,匿名内部类方式使用多线程以及Runtime类和使用<span style="white-space:pre"> </span> */ public static void main(String[] args) { /*Single s1 = Single.s; Single.s = null; Single s2 = Single.s; System.out.println(s1 == s2);*/ /*Single s1 = Single.getInstance(); Single s2 = Single.getInstance(); System.out.println(s1 == s2);*/ Single s1 = Single.s; Single s2 = Single.s; System.out.println(s1 == s2); } } //饿汉式 /*class Single { //第一步,私有构造函数 private Single(){} //第二步,创建本类对象 private static Single s = new Single(); //第三步,定义公有的访问方法将对象返回 public static Single getInstance() { return s; } }*/ //懒汉式 /*class Single { //第一步,私有构造函数 private Single(){} //第二步,声明一个本类的引用 private static Single s; //第三步,定义公有的访问方法将对象返回 public static Single getInstance() { if(s == null) //线程1停,线程2停 s = new Single(); return s; } }*/ /* * 饿汉式和懒汉式的区别 * 1,饿汉式时间换空间,懒汉式空间换时间 * 2,饿汉式在多线程访问没问问题,只创建一个对象 * 懒汉式又名单例的延迟加载形式,在多线程访问的时候,有可能会出现多个实例 * */ class Single { //第一步,私有构造函数 private Single(){} //第二步,创建本类对象 public final static Single s = new Single(); }
1.线程组Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进 行控制.在默认情况下,所有的线程都属于主线程组.声明方法为:public final getThreadGroup() 还可以 给线程设置分组.方法为:Thread(ThreadGroup group,Runnable target,String name)2.线程池程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以很好 的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。线程池里的每一个线程代码结束后,并不会死亡,而是再次会带线程池中称为空闲状态,等待下一个对象来 使用,在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池.有如下几个方 法: public static ExecutorService newCachedThreadPool()
public static ExecutorService newFixedThreadPool(int nThreads)
public static ExecutorService newSingleThreadExecutor()这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象 代表的线程,它提供了以下的方法: Future<?> submit(Runnable task)
<T> Future<T> submit(Callable<T> task)3.匿名内部类方法使用多线程,方法主要有:new Thread(){ 代码......} start();new Thread(new Runnable() {代码......}) start();4.Runtime类每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接。可以通过 getRuntime 方法获取当前运行时。 应用程序不能创建自己的 Runtime 类实例。使用方法为:public Prosess exec(String command)
黑马程序员_java基础学习笔记10_多线程
最新推荐文章于 2024-06-19 11:00:59 发布