-----------多线程-----------
什么是多线程???
-
多线程的概念:
如果在一个进程中同时运行了多个线程,
用来完成不同的工作,则称之为“多线程”
多个线程交替占用CPU资源,而非真正的并行执行 -
多线程好处
充分利用CPU的资源
简化编程模型
带来良好的用户体验 -
主线程
main()方法即为主线程入口
产生其他子线程的线程
必须最后完成执行,因为它执行各种关闭动作 -
在Java中创建线程的两种方式
继承java.lang.Thread类
实现java.lang.Runnable接口使用线程的步骤:
(1)定义线程
(2)创建线程对象
(3)启动线程
(4)终止线程 -
继承Thread类创建线程
步骤:
定义MyThread类继承Thread类
重写run()方法,编写线程执行体测试类中: 创建线程对象,调用start()方法启动线程 public class Test11 { public static void main(String[] args) { MyThread mt1 = new MyThread(); MyThread mt2 = new MyThread(); mt1.start(); mt2.start(); } } class MyThread extends Thread{ @Override public void run() { for (int i = 1; i <=20; i++) { System.out.println("你好 ,"+ Thread.currentThread().getName()); } } } 直接结果分析: 多个线程交替执行,不是真正的“并行” 线程每次执行时长由分配的CPU时间片长度决定 并行: 两个程序都执行,同时进行,抢占CPU资源。 并发: 做完一件事,立马在做另一件事,称为并发。
-
启动线程调用 线程对象的start 方法,和run方法的区别。
调用start() :子线程执行run()方法 多条执行路径,主线程和子线程并行交替执行 调用run() :主线程执行run() 只有主线程一条执行路径
-
实现Runnable接口创建线程
步骤: 定义MyRunnable类实现Runnable接口 实现run()方法,编写线程执行体 测试类: 创建线程对象,调用start()方法启动线程 public class Test3 { public static void main(String[] args) { MyRunnable mr = new MyRunnable(); //创建实现了线程接口的对象。 Thread t = new Thread(mr); //创建线程对象,把线程接口对象传递 t.start(); //开启线程。 } } class MyRunnable implements Runnable{ //创建MyRunnable类,实现Runnable接口 @Override public void run() { //重写run方法 for (int i = 0; i <= 100; i++) { System.out.println(Thread.currentThread().getName() +"---" + i); } } }
-
比较两种创建线程的方式
继承Thread类 编写简单,可直接操作线程 适用于单继承 实现Runnable接口 避免单继承局限性 便于共享资源 推荐使用实现Runnable接口方式创建线程
-
线程中,常见的线程调度方法:
线程调度指按照特定机制为多个线程分配CPU的使用权 setPriority(int newPriority) 更改线程的优先级 线程优先级由1~10表示,1最低,默认优先级为5 优先级高的线程获得CPU资源的概率较大 static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠 让线程暂时睡眠指定时长,线程进入阻塞状态 睡眠时间过后线程会再进入可运行状态 millis为休眠时长,以毫秒为单位 调用sleep()方法需处理InterruptedException异常 void join() 等待该线程终止 使当前线程暂停执行,等待其他线程结束后再继续执行本线程 join(millis) millis:以毫秒为单位的等待时长 nanos:要等待的附加纳秒时长 需处理InterruptedException异常 static void yield() 暂停当前正在执行的线程对象,并执行其他线程 暂停当前线程,允许其他具有相同优先级的线程获得运行机会 该线程处于就绪状态,不转为阻塞状态 void interrupt() 中断线程 boolean isAlive() 测试线程是否处于活动状态 Thread.currentThread() 获取当前线程对象 获取线程名字,设置线程名字: getName() setName()
-
守护线程 setDaemon();
守护线程,不会单独执行,
当被守护的线程执行完, 那么守护线程也就不再单独执行,发现了就结束。
练习: 创建两个线程。
分别让两个线程执行循环输出。 A线程,循环5次。 (被守护线程) B线程,循环50次。(设置为守护线程)
-
实现代码同步的两种方式
(1)用同步代码块的形式实现。
语法:
synchronized(锁对象){
希望被同步的代码;
}
锁对象: 可以是任何一个类的对象,但是不能是匿名对象。
通常,可以用一个 this ,this 代表当前类对象。
但是注意,如果多个线程子对象操作一个需要同步的代码块的时候
this 不是同一个锁对象,所以不能产生同步效果
常用的三种锁对象
this 当前类对象。
随便创建一个类的对象用。
当前类的字节码对象,有且只有一个(推荐使用)
(2) 通过执行被synchronized 修饰的方法,来实现同步。
在方法的权限修饰词后面 加上 synchronized 即可。
(3) 测试,验证,两种同步代码方式
其中同步方法用的锁对象是什么!!!
经过测试,得出结论.
: 同步方法,用的锁对象,是this
-
多线程共享数据的实现。
变量,被static 修饰,进入常量池,就不会重复创建。
这样几个子线程,即会共享该变量。 -
购票练习:
定义一个购票类,100张票。
测试类里面,创建四个线程,让他们抢票。
每次抢票输出自己的线程名字,和剩余票数。
public class Test22 {
public static void main(String[] args) {
Ticket2 t2 = new Ticket2();
new Thread(t2,“张三售票口:”).start();
new Thread(t2,“李四售票口:”).start();
new Thread(t2,“王五售票口:”).start();
new Thread(t2,“赵六售票口:”).start();
}
}class Ticket2 implements Runnable{ //定义售票窗口类,继承多线程,
public static int tickets = 100; public void run(){ //重写run方法,里面的代码,在子线程中执行. while(true){ //锁对象用,当前类的字节码对象的问题,创建完第一个字节码对象后,就不再创建了 synchronized (Ticket2.class) { // this 可以不可以? 为什么?this不是同一个对象 if (tickets <= 0) { //由于线程不同步的问题,造成 可能会卖负票. break; } try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " 窗口,卖出第[" + tickets-- + "]火车票"); } } }
}
-
多线程操作中,锁对象,产生死锁的问题。
在多线程嵌套的 代码情况下,如果用了相同的锁对象
那么就会出现互斥锁,也称死锁的问题。
有可能无法同步。所以开发中,一定要注意使用锁对象,避免死锁问题出现。
-
单例模式
通过封装一个方法。给你返回本类对象。 不让调用者来创建对象。 好处: 这样这个类,创建的对象,是唯一的,谁要用,就给谁用。 大家不需要创建。
实现单例的步骤:
(1) 私有构造方法
(2) 提供一个方法,创建本类对象,并返回。
饿汉式 : 直接在类中的属性声明处,创建好对象,
获取对象方法中,直接返回该对象。获取对象的时候效率高,空间换时间。 懒汉式 : 属性声明位置,先声明对象,不创建对象赋值。 获取对象的方法中,判断对象为null。再创建。 并返回。 单例设计比较节省空间, 时间换空间。 public class Test4 { public static void main(String[] args) { Single s =Single.getInstance() ; } } class Single { //创建一个 单例类. 懒汉式. static Single s ; //null private Single(){} //1 私有构造方法 //2 创建一个方法,对外提供本类对象. public static Single getInstance(){ if (s ==null) { s = new Single(); } return s; } } class Single2{ //饿汉式,创建单例类. static final Single2 s2 = new Single2(); //被final修饰,唯一一个对象,不重复创建 private Single2(){} //1 私有构造方法 //2 创建一个方法,对外提供本类对象. public static Single2 getInstance(){ return s2; } }