
(以下内容全部来自上述课程)

多线程
线程(功能):线程是操作系统能够进行运算调度的最小单元。它被包含在进程之中,是进程中的实际运作单位。
进程(软件):进程是程序的基本执行实体。
线程的简单理解:应用软件中互相独立,可以同时运行的功能。
有了多线程,我们就可以让程序同时做多件事情
作用:提高效率
应用场景:只要你想让多个事情同时运行就需要用到多线程
1. 并发和并行
并发:在同一时刻,有多个指令在单个CPU上交替执行。
并行:在同一时刻,有多个指令在多个CPU上同时执行。
2. 实现方式
2.1 继承Thread类的方式进行实现
具体类可看API帮助文档
package com.itheima.a01threadcase1;
public class ThreadDemo {
public static void main(String[] args) {
/*
* 多线程的第一种启动方式:
1.自己定义一个类继承Thread
2.重写run方法
* 3.创建子类的对象,并启动线程
* */
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
}
package com.itheima.a01threadcase1;
public class MyThread extends Thread{
@Override
public void run() {
//书写线程要执行代码
for (int i = 0; i < 100; i++){
System.out.println(getName()+ "HelloWorld");
}
}
}
2.2 实现Runnable接口的方式进行实现
package com.itheima.a02threadcase2;
public class ThreadDemo {
public static void main(String[] args){
/*
* 多线程的第二种启动方式:
* 1.自己定义一个类实现Runnable接口
* 2.重写里面的run方法
* 3.创建自己的类的对象
* 4.创建一个Thread类的对象,并开启线程
* */
//创建MyRun的对象
//表示多线程要执行的任务
MyRun mr = new MyRun();
//创建线程对象
Thread t1 = new Thread(mr);
Thread t2 = new Thread(mr);
//给线程设置名字
t1.setName("线程1");
t2.setName("线程2");
//开启线程
t1.start();
t2.start();
}
}
package com.itheima.a02threadcase2;
public class MyRun implements Runnable{
@Override
public void run() {
//书写线程要执行的代码
for (int i = 0; i< 100;i++){
//获取到当前线程的对象
/*Thread t = Thread.currentThread();
System.out.println(t.getName() + "HelloWorld!");*/
System.out.println(Thread.currentThread().getName() + "HelloWorld")
}
}
}
2.3 利用Callable接口和Future接口方式实现
特点:可以获取多线程运行的结果
package com.itheima.a03threadcase3;
public class ThreadDemo{
public static void main(String[] args){
/*
多线程的第三种实现方式:
特点:可以获取到多线程运行的结果
* 1.创建一个类MyCallable实现Callable接口
* 2.重写call (是有返回值的,表示多线程运行的结果)
* 3.创建MyCallable的对象(表示多线程要执行的任务)
* 4.创建FutureTask的对象(作用管理多线程运行的结果)
* 5.创建Thread类的对象,并启动(表示线程)
* */
//创建MyCallable的对象(表示多线程要执行的任务)
MyCallable mc = new MyCallable();
//创建FutureTask的对象(作用管理多线程运行的结果)
FutureTask<Integer>ft = new FutureTask<>(mc);
//创建线程的对象
Thread t1 = new Thread(ft);
//启动线程
t1.start();
//获取多线程运行的结果
Integer result = ft.get();
System.out.println(result);
}
}
package com.itheima.a03threadcase3;
import java.util.concurrent.Callable;
public class MyCallable implements Callable<Integer> {
@override
public Integer call() throws Exception {
//求1~100之间的和
int sum = 0;
for (int i = 1; i<= 100; i++) {
sum = sum + i;
}
return sum;
}
}
2.4 对比

3. 常见成员方法

3.1 常用方法的细节

- String getName() 返回此线程的名称
void setName(String name) 设置线程的名字(构造方法也可以设置名字)
细节:
- 如果我们没有给线程设置名字,线程也是有默认的名字的
格式:Thread-X(X序号,从0开始的) - 如果我们要给线程设置名字,可以用set方法进行设置,也可以构造方法设置
- static Thread currentThread() 获取当前线程的对象
细节:
当JVM虚拟机启动之后,会自动的启动多条线程其中有一条线程就叫做main线程
他的作用就是去调用main方法,并执行里面的代码
在以前,我们写的所有的代码,其实都是运行在main线程当中 - static void sleep(long time) 让线程休眠指定的时间,单位为毫秒
细节:
- 哪条线程执行到这个方法,那么哪条线程就会在这里停留对应的时间
- 方法的参数:就表示睡眠的时间,单位毫秒
1 秒=1000毫秒 - 当时间到了之后,线程会自动的醒来,继续执行下面的其他代码
3.2 线程的优先级
抢占式调度:随机性
范围:(1~10)
默认值是5

3.3 守护线程(应用)

3.4 礼让线程(了解)

3.5 插入线程(了解)
土豆(插入的)执行完了再执行main线程

4. 线程的生命周期

5. 线程的安全问题
可能出现的问题(以多线程卖票为例):
- 相同的票出现了多次
- 出现了超出范围的票
5.1 同步代码块
把操作共享数据的代码锁起来

特点:
- 锁默认打开,有一个线程进去了,锁自动关闭。
- 里面的代码全部执行完毕了,线程出来,锁自动打开
package com.itheima.a09threadsafe1;
public class MyThread extends Thread{
//表示这个类所有的对象,都共享ticket数据
static int ticket = 0;//0 ~99
//锁对象,一定要是唯一的
static 0bject obj = new 0bject();
@Override
public void run() {
while (true){
//同步代码块
synchronized (obj){
if(ticket < 100){
try {
Thread.sleep( millis:10);
} catch (InterruptedException e){
e.printStackTrace();
}
ticket++;
System.out.println(getName()+"正在卖第"+ ticket +"张票!!!");
}else{
break;
}
}
}
}
}
细节:
- 同步代码块不能在while的外面,否则相当于单线程执行。
- 锁对象,一定要是唯一的。
5.2 同步方法
就是把synchronized关键字加到方法上
格式:修饰符 synchronized 返回值类型 方法名(方法参数){…}
特点:
- 同步方法是锁住方法里面所有的代码
- 锁对象不能自己指定
- 非静态:this
- 静态:当前类的字节码文件对象
技巧:先写同步代码块,再抽取为同步方法
测试类:
package com.itheima.a10threadsafe2;
public class ThreadDemo {
puNlic static void main(String[] args){
/*
需求:
某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票利用同步方法完成技巧:同步代码块
*/
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(mr);
Thread t2 = new Thread(mr);
Thread t3 = new Thread(mr);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
同步代码块:
package com.itheima.a10threadsafe2;
public class MyRunnable implements Runnable{
int ticket = 0;
@Override
public void run(分{
//1.循环
while(true){
//2.同步代码块(同步方法)
synchronized (MyRunnable.class){
//3.判断共享数据是否到了末尾,如果到了末尾
if(ticket == 100){
break;
}else{
//4.判断共享数据是否到了末尾,如果没有到末尾
ticket++;
System.out.println(Thread.currentThread().getName()+"在卖第"+ ticket + "张票!!!");
}
}
}
}
}
选中同步代码块–ctrl + alt + M:
@override
public void run() {
//1.循环
while (true) {
//2.同步代码块(同步方法)
if (method()) break;
}
}
//this
private synchronized boolean method(){
//3.判断共享数据是否到了末尾,如果到了末尾
if (ticket == 100) {
return true;
} else {
//4.判断共享数据是否到了末尾,如果没有到末尾
try{
Thread.sleep(millis: 10);
} catch (InterruptedException e){
e.printStackTrace();
}
ticket++;
System.out.println(Thread.currentThread().getName()+"在卖第"+ ticket +"张票!!!");
}
return false;
}

8235

被折叠的 条评论
为什么被折叠?



