文章目录
博主简介:
博客主页:Java知识分享博主
Java零基础入门专栏:Java零基础入门专栏
Java交流社区:飞鸟社区
欢迎阅读,如果文章对你有帮助点赞,支持一下!
什么是多线程
程序、进程、线程
-
程序:从所周知的,编写代码组成的。
-
进程:可以理解为运行中的程序,比如启动QQ,就启动一个进程,操作系统会为进程分配内存空间。
-
线程:由进程创建的,是进程的一个实体。
单线程和多线程的区分:
-
同一个时刻,只允许执行一个线程
-
同一个时刻,可以执行多个线程,比如QQ可以同时打开多个聊天窗口
并发和并行(理解)
- 一个多个任务交替执行,一个多个任务同时执行
创建线程的两种方式
通过继承 Thread 的方法和实现 Runnable 接口的方式创建多线程,哪个好?
实现Runnable接口好,原因有两个:
- ①、避免了Java单继承的局限性
- ②、适合多个相同的程序代码去处理同一资源的情况,把线程、代码和数据有效的分离,更符合面向对象的设计思想。
①、继承 Thread 的方法
- 当一个类(cat类)继承了 Thread 类, 该类就可以当做线程使用
- 我们会重写 run 方法,写上自己的业务代码
- run Thread 类 实现了 Runnable 接口的 run
package threaduse;
public class Thread01 {
public static void main(String[] args) {
Cat cat = new Cat();
cat.start();//启动线程
}
}
class Cat extends Thread{
int times=0;
public void run () { //重写 run 方法,写上自己的业务逻辑
while(true) {
System.out.println("猫会跑"+times++);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(times==8){ //当时间到第8秒时,自动跳出
break;
}
}
}
}
②、runnable接口实现run方法
1、dog.start();这里不能调用 start
2、创建了 Thread 对象,把 dog 对象(实现 Runnable),放入 Thread
package threaduse;
public class Thread02 {
public static void main(String[] args) {
Dog dog = new Dog();
// dog.start();这里不能调用 start
//创建了 Thread 对象,把 dog 对象(实现 Runnable),放入 Thread
Thread thread = new Thread(dog);
thread.start();
}
}
class Dog implements Runnable{//通过实现 Runnable 接口,开发线程
int count=0;
public void run(){
while (true){
System.out.println("小狗会汪汪叫。。"+(++count)+Thread.currentThread().getName());
try {
Thread.sleep(1000); //休眠1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
if(count==8){
break;
}
}
}
}
为什么要重写run方法
因为run方法是用来封装被线程执行的代码。
run()
方法和start()
方法有什么区别?
run()
:封装线程执行的代码,直接调用相当于调用普通方法。start()
:启动线程,然后由JVM 调用此线程的run()
方法。
设计模式
package threaduse;
public class Thread02 {
public static void main(String[] args) {
// Dog dog = new Dog();
// // dog.start();这里不能调用 start
// //创建了 Thread 对象,把 dog 对象(实现 Runnable),放入 Thread
// Thread thread = new Thread(dog);
// thread.start();
tiger tiger = new tiger();
ThreadProxy threadProxy = new ThreadProxy(tiger);
threadProxy.start();
}
}
class Animal{}
class tiger extends Animal implements Runnable{
public void run(){
System.out.println("老虎嗷嗷叫");
}
}
//线程代理类 , 模拟了一个极简的 Thread
class ThreadProxy implements Runnable{
private Runnable target =null;//属性,类型是Runnable
public void run() {
if(target!=null){
target.run();//动态绑定(运行类型 Tiger)
}
}
public ThreadProxy(Runnable target){
this.target=target;//构造方法
}
public void start(){
start0();//这个方法时真正实现多线程方法
}
public void start0(){
run();
}
}
class Dog implements Runnable{//通过实现 Runnable 接口,开发线程
int count=0;
public void run(){
while (true){
System.out.println("小狗会汪汪叫。。"+(++count)+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(count==8){
break;
}
}
}
}
手动实践编程题
编写一个程序,创建两个线程,一个线程每隔1秒输出“helloworld”,输出10次退出, 一个线程每隔1秒输出“hi”,输出5次退出(可以手动敲一下.狗头)
package threaduse;
//编写一个程序,创建两个线程,一个线程每隔1秒输出“helloworld”,输出10次退出,
// 一个线程每隔1秒输出“hi”,输出5次退出
public class Thread03 {
public static void main(String[] args) {
T1 t1 = new T1();
T2 t2 = new T2();
Thread thread1 = new Thread(t1);
Thread thread2 = new Thread(t2);
thread1.start();
thread2.start();
}
}
class T1 implements Runnable{
int count=0;
public void run(){
while (true){
System.out.println("helloworld"+(++count)+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(count==10){
break;
}
}
}
}
class T2 implements Runnable{
int count=0;
public void run(){
while (true){
System.out.println("hi"+(++count)+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(count==5){
break;
}
}
}
}
编程模拟三个售票窗口售票100张
package threaduse;
//编程模拟三个售票窗口售票100张,
// 分别使用继承Thread和实现Runnable方式,并分析有什么问题?
public class Thread04 {
public static void main(String[] args) {
SellTicket sellTicket = new SellTicket();
new Thread(sellTicket).start();//第一个售票窗口
new Thread(sellTicket).start();//第二个售票窗口
new Thread(sellTicket).start();//第三个售票窗口
}
}
class SellTicket extends Thread{
private static int num=100;
public void run(){
while (true){
if(num<=0){
System.out.println("售票结束。。");
break;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("窗口"+Thread.currentThread().getName()+"售出一张票"+"剩余票数="+(--num));
}
}
}
手动实践编程题
启动一个线程t,要求main线程中去停止线程t,关键代码块
成员内部类中
package exit;
public class ThreadExit {
public static void main(String[] args) throws InterruptedException {
T t = new T();
new Thread(t).start();
System.out.println("main线程休眠10秒");
Thread.sleep(10*1000);
t.setLoop(false);
}
}
class T implements Runnable{
private boolean loop=true;
private int count=0;
public void run(){
while(loop){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("a运行中。。。"+(++count));
}
}
public void setLoop(boolean loop){
this.loop=loop;
}
}
线程常用方法
interrupt 中断线程
编程题
两个线程同时执行,当主线程执行输出5次时,让子线程执行剩下15次输出,主线程再次执行。
public class ThreadMethodExercise {
public static void main(String[] args) throws InterruptedException {
Thread t3 = new Thread(new T3());//创建子线程
for (int i = 1; i <= 10; i++) {
System.out.println("hi " + i);
if(i == 5) {//说明主线程输出了 5 次 hi
t3.start();//启动子线程 输出 hello... t3.join();
t3.join();// 立即将 t3 子线程,插入到 main 线程,让 t3 先执行
}
Thread.sleep(1000);//输出一次 hi, 让 main 线程也休眠 1s
}
}
}
class T3 implements Runnable {
private int count = 0;
@Override
public void run() {
while (true) {
System.out.println("hello " + (++count));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (count == 10) {
break;
}
}
}
}
Thread.yield;礼让不一定成功
t3.join();插队一定成功
用户线程和守护线程
//设t为子线程,即设为守护线程,当所有线程结束后,t也自动结束
//如果没有设置,那么main线程执行完毕,t也不退出
//前提是子线程无限循环
t.setDaemon(true);//关键字
t.start();
6种线程
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
package state;
public class ThreadState {
public static void main(String[] args) throws InterruptedException {
T t = new T();
System.out.println(t.getName() + " 状态 " + t.getState());
t.start();
while (Thread.State.TERMINATED != t.getState()) {
System.out.println(t.getName() + " 状态 " + t.getState());
Thread.sleep(1000);
}
System.out.println(t.getName() + " 状态 " + t.getState());
}
}
class T extends Thread {
@Override
public void run() {
while (true) {
for (int i = 0; i < 10; i++) {
System.out.println("hi " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
}
}
}
线程同步机制(重点)
- 编程模拟三个售票窗口售票100张,
- 分别使用继承Thread和实现Runnable方式,并分析有什么问题?
package sys;
public class Thread05 {
public static void main(String[] args) {
SellTicket3 sellTicket3 = new SellTicket3();
new Thread(sellTicket3).start();//第一个售票窗口
new Thread(sellTicket3).start();//第二个售票窗口
new Thread(sellTicket3).start();//第三个售票窗口
}
}
class SellTicket3 implements Runnable{
private int num=100;
private boolean loop =true;
public synchronized void sell(){ //同步线程,只允许一个线程执行
if(num<=0){
System.out.println("售票结束。。");
loop=false;
return;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("窗口"+Thread.currentThread().getName()+"售出一张票"+"剩余票数="+(--num));
}
public void run(){
while (loop){
sell();
}
}
}
互斥锁
//1. public synchronized static void m1() {} 锁是加在 SellTicket03.class
//2. 如果在静态方法中,实现一个同步代码块
/*
synchronized (SellTicket4.class) {
System.out.println("m2");
}
*/
public synchronized static void m1() {
}
public static void m2() {
synchronized (SellTicket4.class) {
System.out.println("m2");
}
- 1、 public synchronized void sell() {} 就是一个同步方法
- 2、这时锁在this对象
- 3、 也可以在代码块上写synchronize,同步代码块,互斥锁还是在this对象
package sys;
//编程模拟三个售票窗口售票100张,
// 分别使用继承Thread和实现Runnable方式,并分析有什么问题?
public class Thread06 {
public static void main(String[] args) {
SellTicket4 sellTicket3 = new SellTicket4();
new Thread(sellTicket3).start();//第一个售票窗口
new Thread(sellTicket3).start();//第二个售票窗口
new Thread(sellTicket3).start();//第三个售票窗口
}
}
class SellTicket4 implements Runnable{
private int num=50;
private boolean loop =true;
Object object=new Object();
public void sell() { //同步方法,同一个时刻,只允许一个sell线程执行
synchronized (object) {
if (num <= 0) {
System.out.println("售票结束。。");
loop = false;
return;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("窗口" + Thread.currentThread().getName() + "售出一张票" + "剩余票数=" + (--num));
}
}
public void run(){
while (loop){
sell();
}
}
}
线程的死锁
多个线程都占用对方的锁资源,但不肯互让,导致了死锁
package DeadLock;
public class DeadLock{
public static void main(String[] args) {
//模拟死锁现象
DeadLockDemo A = new DeadLockDemo(true);
A.setName("A 线程");
DeadLockDemo B = new DeadLockDemo(false);
B.setName("B 线程");
A.start();
B.start();
}
}
class DeadLockDemo extends Thread {
static Object o1 = new Object();// 保证多线程,共享一个对象,这里使用 static
static Object o2 = new Object();
boolean flag;
public DeadLockDemo(boolean flag) {//构造器
this.flag = flag;
}
@Override
public void run() {
//下面业务逻辑的分析
//1. 如果 flag 为 T, 线程 A 就会先得到/持有 o1 对象锁, 然后尝试去获取 o2 对象锁
//2. 如果线程 A 得不到 o2 对象锁,就会 Blocked
//3. 如果 flag 为 F, 线程 B 就会先得到/持有 o2 对象锁, 然后尝试去获取 o1 对象锁
//4. 如果线程 B 得不到 o1 对象锁,就会 Blocked
if (flag) {
synchronized (o1) {//对象互斥锁, 下面就是同步代码
System.out.println(Thread.currentThread().getName() + " 进入 1");
synchronized (o2) { // 这里获得 li 对象的监视权
System.out.println(Thread.currentThread().getName() + " 进入 2");
}
}
} else {
synchronized (o2) {
System.out.println(Thread.currentThread().getName() + " 进入 3");
synchronized (o1) { // 这里获得 li 对象的监视权
System.out.println(Thread.currentThread().getName() + " 进入 4");
}
}
}
}
}
编程题
在main方法中启动两个线程
第1个线程循环随机打印100以内的整数
直到第2个线程从键盘读取了“Q”命令
package Homework01;
import java.util.Locale;
import java.util.Scanner;
public class Homework01 {
public static void main(String[] args) {
A a = new A();
B b = new B(a);
a.start();
b.start();
}
}
class A extends Thread{
private boolean loop =true;
public void run(){
while(loop){
System.out.println((int)(Math.random()*100 +1));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void setLoop(boolean loop) {
this.loop = loop;
}
}
class B extends Thread{
private A a;
private Scanner scanner=new Scanner(System.in);
public B(A a){
this.a=a;
}
public void run() {
while (true) {
//接受用户输入
System.out.println("请输入(Q)指令结束退出:");
char key=scanner.next().toUpperCase().charAt(0);
if(key=='Q'){
//以通知方式结束a线程
a.setLoop(false);
System.out.println("b线程退出");
break;
}
}
}
}
编程题
有2个用户分别从同一个卡上取钱(总额:1000)
每次都取1000,当余款不足时,就不能取款了
不能出现超取现象 线程同步问题
package Homework02;
public class Mon01 {
public static void main(String[] args) {
A a = new A();
Thread thread1 = new Thread(a);
thread1.setName("用户1");
Thread thread2 = new Thread(a);
thread2.setName("用户2");
thread1.start();//第一个用户
thread2.start();//第二个用户
}
}
//涉及多个线程共享资源,使用接口的实现
class A implements Runnable{
private int num=10000;
public boolean loop=true;
public void run(){
while(loop){
synchronized (this){
if(num<0){
System.out.println("余款不足,不能取款");
loop=false;
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
num -=1000;
System.out.println(Thread.currentThread().getName()+" "+"剩余余款:"+num);
}
}
}
}