-----------多线程-------------
什么是多线程
1. 多线程的概念:
如果在一个进程中同时运行了多个线程,
用来完成不同的工作,则称之为“多线程”
多个线程交替占用CPU资源,而非真正的并行执行
2. 多线程好处
充分利用CPU的资源
简化编程模型
带来良好的用户体验
3. 主线程
main()方法即为主线程入口
产生其他子线程的线程
必须最后完成执行,因为它执行各种关闭动作
4.在Java中创建线程的两种方式
继承java.lang.Thread类
实现java.lang.Runnable接口
使用线程的步骤:
(1)定义线程
(2)创建线程对象
(3)启动线程
(4)终止线程
5. 继承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资源。
并发: 做完一件事,立马在做另一件事,称为并发。
6. 启动线程调用 线程对象的start 方法,和run方法的区别。
调用start() :子线程执行run()方法
多条执行路径,主线程和子线程并行交替执行
调用run() :主线程执行run()
只有主线程一条执行路径
7. 实现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);
}
}
}
8. 比较两种创建线程的方式
继承Thread类
编写简单,可直接操作线程
适用于单继承
实现Runnable接口
避免单继承局限性
便于共享资源
推荐使用实现Runnable接口方式创建线程
9. 线程中,常见的线程调度方法:
线程调度指按照特定机制为多个线程分配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()
10. 守护线程 setDaemon();
守护线程,不会单独执行,
当被守护的线程执行完,
那么守护线程也就不再单独执行,发现了就结束。
练习: 创建两个线程。
分别让两个线程执行循环输出。
A线程,循环5次。 (被守护线程)
B线程,循环50次。(设置为守护线程)
11. 实现代码同步的两种方式
(1)用同步代码块的形式实现。
语法:
synchronized(锁对象){
希望被同步的代码;
}
锁对象: 可以是任何一个类的对象,但是不能是匿名对象。
通常,可以用一个 this ,this 代表当前类对象。
但是注意,如果多个线程子对象操作一个需要同步的代码块的时候
this 不是同一个锁对象,所以不能产生同步效果
常用的三种锁对象
this 当前类对象。
随便创建一个类的对象用。
当前类的字节码对象,有且只有一个(推荐使用)
(2) 通过执行被synchronized 修饰的方法,来实现同步。
在方法的权限修饰词后面 加上 synchronized 即可。
(3) 测试,验证,两种同步代码方式
其中同步方法用的锁对象是什么!!!
经过测试,得出结论.
: 同步方法,用的锁对象,是this
12.多线程共享数据的实现。
变量,被static 修饰,进入常量池,就不会重复创建。
这样几个子线程,即会共享该变量。
13. 购票练习:
定义一个购票类,100张票。
测试类里面,创建四个线程,让他们抢票。
每次抢票输出自己的线程名字,和剩余票数。
public class Test22 {
public static void main(String[] args) {
Ticket2 t2 = new Ticket2();
new Thread(t2,"111售票口:").start();
new Thread(t2,"222售票口:").start();
new Thread(t2,"333售票口:").start();
new Thread(t2,"444售票口:").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-- + "]火车票");
}
}
}
}
14. 多线程操作中,锁对象,产生死锁的问题。
在多线程嵌套的 代码情况下,如果用了相同的锁对象
那么就会出现互斥锁,也称死锁的问题。
有可能无法同步。
所以开发中,一定要注意使用锁对象,避免死锁问题出现。
15. 单例模式
通过封装一个方法。给你返回本类对象。
不让调用者来创建对象。
好处: 这样这个类,创建的对象,是唯一的,谁要用,就给谁用。
大家不需要创建。
实现单例的步骤:
(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;
}
}