内容为线程,同步,线程状态
- 能够描述Java中多线程运行原理
- 能够使用继承类的方式创建多线程
- 能够使用实现接口的方式创建多线程
- 能够说出实现接口方式的好处
- 能够解释安全问题的出现的原因
- 能够使用同步代码块解决线程安全问题
- 能够使用同步方法解决线程安全问题
- 能够说出线程6个状态的名称
1.多线程的原理:
- 我理解为在主方法中(也就是主线程)开启另一个线程,使得cup执行程序是在多个线程之间高速度切换,以完成多任务,足以满足多用户的需求.
- java程序是多线程的 ,其采用了更传统的顺序语言的基础上提供了对多线程的支持,并且每个任务开启时都会在内存空间中有自己的地址去执行,因此任务之间根本不能相互干涉,并且其也没有相互干涉的必要性,所以安全性较高.
- 基本的线程机制:并发编程使我们可以将程序划分为多个分离的,独立运行的任务.通过使用多线程机制,这些独立任务(在Java中也称为子任务或子线程)中的每一个都将由执行线程来驱动.一个线程就是在进程种的一个单一的顺序控制流,因此单个线程可以拥有多个并发执行任务.就像是每个任务都是有自己的cup一样,但其实底层的机制是切分cup的时间,单个cup快速且随机的执行不同的任务.
下面是一个简单的多线程例子,并打印运行的结果看cpu是如何在两个线程中切换的
//这是一个自定义线程类
public class ThreadRun implements Runnable {
//这里必须继承 Runnable 接口
//并重写run方法 作为此线程的执行方法体
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println("我是子线程" + i);
}
}
}
---------------------------------------------------------------------------
---------------------------------------------------------------------------
//这是一个测试类
public class domain {
public static void main(String[] args) {
//这是main方法 也叫主线程
ThreadRun threadRun = new ThreadRun();
Thread thread = new Thread(threadRun);
//开启一个线程
thread.start();
//开启线程后主方法会继续往下运行,这是会出现子线程和主线程抢夺cup的情况
for (int i = 0; i < 50; i++) {
System.out.println("我是主线程"+ i);
}
}
}
执行结果分析:
2.Thread类
-
使用继承类,并创建线程:
1.Thread中可以去查API了解更多的构造方法
2.两种创建线程的方法:- 一种是继承Thread类方式,
- 一种是实现Runnable接口方式,
3.实现Runnable接口比继承Thread类所具有的优势
- 如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享
-
- 适合多个相同的程序代码的线程去共享同一个资源。
-
- 可以避免java中的单继承的局限性。
-
- 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
-
- 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类
下面要用到资源共享,在这补充
- 资源共享,涉及到创建线程的方式,
- 第一种继承Thread的每一次创建线程都会通过new Thread的继承类,在内存空间中开辟出独立的地址去执行自己的任务
- 第二种实现Runnable接口,每一个线程都实现同一个接口,总共的资源量是不变的,这个不变的总资源量可以是实现类中的成员属性 (例如我下面的买票总共100张),把任务类(Runnable的实现类)交给线程,再开启线程,他们执行的是一个任务,所以共享了这100张票…
4.匿名内部类的创建方法,介入lambda表达式
- 匿名内部类,匿名对象的创建方法
public class NoNameInnerClassThread {
public static void main(String[] args) {
// new Runnable(){
// public void run(){
// for (int i = 0; i < 20; i++) {
// System.out.println("张宇:"+i);
// }
// }
// }; //‐‐‐这个整体 相当于new MyRunnable()
//---这也是一个匿名内部类匿名对象
//这种方法 会在.out文件创建一个.class的字节码文件
//如果想提高性能 可以使用lambda表达式
/**
*lambda表达式注重的是方法体,在乎的是结果,并且不会在.out文件夹中创建.class字节码文件,所以效率更高,书写更为简单
*/
new Thread(() -> {
for (int i = 0; i < 20; i++) {
System.out.println("张宇:" + i);
}
}).start();
//下面是lambda展开的全部样子 较为好理解
/*Runnable r = new Runnable(){
public void run(){
for (int i = 0; i < 20; i++) {
System.out.println("张宇:"+i);
}
}
};
new Thread(r).start();
*/
//下面是主线程
for (int i = 0; i < 20; i++) {
System.out.println("费玉清:"+i);
}
}
}
5.线程安全
- 如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
-
简单举一个例子:卖电影票是一个很好的例子,假设有两个售票员,一个叫小阳阳,一个小檀檀,有许多人在买票入场看电影,如果不出现票座位的重复,保证每个买到票的都有相应座位,则我就可以称这两个售票员,也就是两个线程是安全的
<下面代码块中备注详解怎样使线程安全>
这个是实现Runnable接口的实现类
/**
* 就以买票为例 --一共100张:
* 两个买票员不能卖出同一张票,不能冲突,那么用线程的角度来说,当小檀檀出售A4这个座位的票的过程中,小阳阳不能卖这张票
* 所以当第一线程抢到cpu时,第二线程不能抢夺cpu,需第一线程卖完一张票后释放cpu后,在同一起跑线上和第二线程在抢夺cpu
* 两个线程再次买票,看黄牛顾客(cpu)到哪个窗口买票,这里当然是随机的
*/
public class ThreadRun implements Runnable {
private int ticket = 100;
/*
* 执行卖票操作
*/
@Override
public void run() {
//每个窗口卖票的操作
//窗口 永远开启
while (true) {
if (ticket > 0) {//有票 可以卖
//出票操作
//使用sleep模拟一下出票时间
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//获取当前线程对象的名字
String name = Thread.currentThread().getName();
System.out.println(name + "正在卖:" + ticket--);
}
}
}
}
下面是domain测试类
public class domain {
public static void main(String[] args) {
//创建线程任务对象
ThreadRun ticket = new ThreadRun();
//创建两个窗口对象
Thread t1 = new Thread(ticket, "小檀檀窗口1");
Thread t2 = new Thread(ticket, "小阳阳窗口2");
//同时卖票
t1.start();
t2.start();
}
}
后面的线程的状态和同步,锁独立写出自己的笔记和理解
版权声明:本文为weixin_41675375原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。