Thread类 多线程

刚入职场的小菜鸟,深知知识需要积累,需要记录,博文主要目的就是方便自己复习和记录遇到的问题及解决办法。
对于知识点写作思路就是:基础概念(解决是什么,是为了解决什么),例子(怎么用),细节(具体使用过程中碰到的问题以及与其他的联系)
声明:基本都是自己概括而成,深知有些话要究竟,无底洞,很耗时,针对我的情况我会间而化之!

线程

基础概念

进程: 操作系统中运行的程序就是进程,多个就是多进程。
线程: 进程中可以划分成多个并行执行的过程,一个就是线程,多个就是多线程。
常识:

  • 一个计算机中,只有一个cpu,同一个时刻,只能处理一个运算,但由于计算机的运行速度极快,会不停的切换处理的任务,看起来就行同时执行一样!
  • 我们平时写的程序就是在jvm中执行,jvm也是进程,在其中开辟多个线程,执行并发的任务, 这就是java的多线程技术

类:
万物皆对象,在java中也是用对象来代表底层的物理线程 - Thread类
在这里插入图片描述
启动线程的两种方式:
a. 启动线程的方式1:
i. 创建一个类 实现Runnable接口 在其中的run()方法中编写启动的线程要执行的代码
ii. 创建改类的对象,传入Thread的构造方法 ,创建Thread对象
iii. 调用Thread对象的start()方法,启动线程
b. 启动线程的方式2:
i. 写一个类继承Thread,覆盖其中的run()方法,在其中编写启动的线程要执行的代码
ii. 创建该Thread类的子类的对象
iii. 调用Thread类的子类的对象的start()方法,启动线程
两种线程启动方式的比较:

  • java是单继承的,继承的方式创建线程,将会占用了extends关键字,这在类本身需要继承其他类的情况下无法使用,影响扩展性。
  • java是多实现的,实现接口的数量没有限制,所以实现接口创建线程的方式并不会受到单继承的限制

线程并发的细节:
a. 主线程和其他线程比起来,唯一特殊的地方是它是程序的入口,除此之外没有任何高低 先后 差别。
b. 多个线程并发的过程中,线程在不停的无序的争夺cpu,某一时刻哪个线程抢夺到,哪个线程就执行,由于cpu运行速度非常快,看起来似乎这些线程都在并发的执行。
c. 线程是在进程内部执行的,进程内部只要有任意一个非守护线程存活,进程就不会结束。

关闭线程:
官方的建议是,应该由程序本身提供相应的开关,来控制线程的运行和停止。通常由一个静态的布尔类型来作为这种开关

Thread中的其他常用方法:
线程优先级相关的字段,本质上是int类型的常量值,取值范围为1 - 10 ,值越大优先级越高
在这里插入图片描述

多线程并发安全问题:

  • 多个线程并发修改共享变量

多线程并发安全问题产生的条件:
a. 有共享资源
b. 有多线程并发操作了共享资源
c. 有多线程并发操作了共享资源 且涉及到了修改操作

解决多线程并发安全问题:
解决多线程并发安全问题的关键,就是破坏产生多线程并发安全问题的条件
禁止共享资源 – ThreadLocal
禁止多线程并发操作 – Syncronized
禁止修改 – ReadWriteLock
Syncronized同步代码块:
同步代码块的结构:
Syncronized(锁对象){
要同步的代码
}
锁对象的选择的原则:
任意对象都可以作为锁对象使用
但要保证,所有的并发线程都应该能够访问到该所对象,且访问的必须是同一个锁对象
常用的锁对象:
自己创建一个专用的对象
使用共享资源作为锁对象
使用类的字节码对象作为锁对象

例子:
创建线程 - Runnable
package cn.tedu.thread;

/**
 * 创建线程的方式一:实现Runnable接口的方式
 */
//--步骤1:写一个类实现Runnable接口
class MyRunnable01 implements Runnable{
	@Override
	public void run() {
		while(true){
			System.out.println("看电影。。。");
		}
	}
}

public class ThreadDemo01 {
	public static void main(String[] args) {
		//--步骤2:创建Thread对象,并传入Runnable接口实现类的对象
		Thread t1 = new Thread(new MyRunnable01());
		//--步骤3:调用Thread的start()方法 真正启动底层线程 运行指定线程代码
		t1.start();
		
		//--主线程并发执行其他任务
		while(true){
			System.out.println("写代码。。。");
		}
	}
}

目录,再往上一拉

创建线程 - Thread
package cn.tedu.thread;
/**
 * 创建线程的方式一:继承Thread方式
 */

//--步骤1:写一个类继承Thread
class MyThread extends Thread{
	@Override
	public void run() {
		while(true){
			System.out.println("看电影。。。");
		}
	}
}

public class ThreadDemo02 {
	public static void main(String[] args) {
		//--步骤2:创建MyThread类的对象
		MyThread t1 = new MyThread();
		//--步骤3:调用t1的start()方法 启动线程
		t1.start();

		//--主线程并发执行其他任务
		while(true){
			System.out.println("写代码。。。");
		}
	}
}

目录,再往上一拉

线程类中的常用方法
package cn.tedu.thread;

/**
 * 线程类中的常用方法
 */

public class ThreadDemo04 {
	public static void main(String[] args) throws Exception {
//		//--getId() getName() setName()
//		Thread t1 = new Thread(new Runnable(){
//			@Override
//			public void run() {
//				
//			}
//		});
//		System.out.println(t1.getId());
//		System.out.println(t1.getName());
//		t1.setName("PQThread-01");
//		System.out.println(t1.getName());
//		
//		//--setPriority(int newPriority)  getPriority()
//		Thread t1 = new Thread(new Runnable() {
//			private int i = 0;
//			@Override
//			public void run() {
//				while(true)
//					System.out.println(Thread.currentThread().getId()+":"+(++i));
//			}
//		});
//		Thread t2 = new Thread(new Runnable() {
//			private int i = 0;
//			@Override
//			public void run() {
//				while(true)
//					System.out.println(Thread.currentThread().getId()+":"+(++i));
//			}
//		});
//		t1.setPriority(10);
//		t2.setPriority(1);
//		System.out.println(t1.getPriority());
//		System.out.println(t2.getPriority());
//		t1.start();
//		t2.start();
		
		//--sleep
		System.out.println("aaaa");
		Thread.sleep(3000);
		System.out.println("bbbb");
	}
}

多线程并发安全问题

目录,再往上一拉

package cn.tedu.thread;

/**
 * 多线程并发安全问题
 */
public class ThreadDemo05 {
	public static String name = "马冬梅";
	public static String gender = "女";
	
	public static void main(String[] args) {
		new Thread(new PrintThread()).start();
		new Thread(new ChangeThread()).start();
	}
}

class ChangeThread implements Runnable{
	@Override
	public void run() {
		while(true){
			synchronized (ThreadDemo05.class) {
				if("马冬梅".equals(ThreadDemo05.name)){
					ThreadDemo05.name = "夏洛";
					ThreadDemo05.gender = "男";
				}else{
					ThreadDemo05.name = "马冬梅";
					ThreadDemo05.gender = "女";
				}
			}
		}
	}
}

class PrintThread implements Runnable{
	@Override
	public void run() {
		while(true){
			synchronized (ThreadDemo05.class) {
				System.out.println("姓名:" + ThreadDemo05.name + ",性别:" + ThreadDemo05.gender);
			}
		}
	}
}

目录,再往上一拉

package cn.tedu.thread;

public class ThreadDemo06 {
	public static int tickets = 10;
	public static void main(String[] args) {
		new Thread(new Saler()).start();
		new Thread(new Saler()).start();
	}
}

class Saler implements Runnable{
	@Override
	public void run() {
		while(ThreadDemo06.tickets>0){
			synchronized (ThreadDemo06.class) {
				ThreadDemo06.tickets--;
				System.out.println(Thread.currentThread().getId() + "窗口,卖出去了一张票。。剩余票数为" + ThreadDemo06.tickets);
			}
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

目录,再往上一拉

死锁问题
概念:
  1. 死锁是一种并发锁定的特殊状态,指的是,当具有多个共享资源时 一部分线程持有一部分资源的锁 要求另外的线程持有的另外的资源的锁 形成了各自持有各自的锁而要求对方的锁的状态 这样 进入了一个互相等待的状态 都无法继续执行 则称之为产生了死锁
  2. 死锁并不是一种真正的锁,而是一种特殊状态,会造成程序无法继续运行或退出,所以要尽力的解决死锁
死锁产生的条件

        a. 多把锁
        b. 多个线程
        c. 同步嵌套
在Synchronized代码块中再包含Synchronized代码块,这就意味着,占用着一部分锁,再要求另一部分锁
目录,再往上一拉

解决死锁
  1. 避免死锁
            避免同步嵌套来避免死锁的产生
  2. 检测并打断死锁
            有时无法进行避免死锁的操作,此时只能不停的检测是否有死锁产生,如果有死锁产生,则打断死锁,所谓的打断死锁,就是将造成死锁的某一线程错误退出,打断对锁互相要求的环,从而使程序可以正常运行下去。
线程之间的通信
  1. 在程序运行过程中,线程是相对独立的单位,多个线程之间并行的执行,并不会有太多的沟通,每个线程都有属于自己的内存空间,且无法互相访问,所以可以认为多个线程之间是隔离的状态,并没有过多的信息传递。

  2. 而线程在并发运行的过程中,还会无序抢夺cpu,造成执行的顺序不确定,使执行的结果变得不可预期

  3. 有时我们希望能够实现 多个线程之间进行信息的传递执行过程的协调 ,这样的技术称之为线程间的通信技术

线程间的通信技术:
  • 共享内存机制 - 多个线程之间进行信息的传递
  • 等待唤醒机制 - 多个线程执行过程的协调
例子
1. 线程间的通信机制 - 共享内存机制 - 多个线程之间的信息传递

        每个线程都各自有各自的内存空间,且无法互相访问,当多个线程需要进行信息的共享时,可以在多个线程都可以看到的公共的内存中保存数据,从而实现两者的通信

案例:某一个线程通过控制一个布尔类型的信号量 控制另一个线程执行的流程

package cn.tedu.thread;

/**
 * 线程间的通信 - 共享内存方式传递信息
 */
public class Demo03 {
	public static boolean canRun = true; 
	public static void main(String[] args) {
		new Thread(new Thread03_Master()).start();
		new Thread(new Thread03_Slave()).start();
	}
}

class Thread03_Master implements Runnable{
	@Override
	public void run() {
		try {
			Thread.sleep(2000);
			Demo03.canRun = false;
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
class Thread03_Slave implements Runnable{
	@Override
	public void run() {
		while(Demo03.canRun){
			System.out.println("小弟在运行。。。");
		}
		System.out.println("小弟告辞了。。。");
	}
}

目录,再往上一拉

2. 线程之间的通信技术 - 等待唤醒机制 - 协调线程执行的先后顺序
  • 多个线程在并发的过程中,互相抢夺cpu,造成执行的先后顺序是不确定的,如果需要控制线程的执行先后顺序,可以采用等待唤醒的机制
  • 在锁对象身上,具有等待、唤醒的方法,这些方法其实是定义在Object类上的,所以任意对象作为锁对象时,都可以有等待 唤醒的方法使用

在这里插入图片描述
目录,再往上一拉
案例:利用等待唤醒机制 实现一个阻塞式队列

package cn.tedu.thread;

import java.util.LinkedList;

/**
 * 等待唤醒机制 案例 - 实现阻塞式队列
 * @author Administrator
 *
 */
class MyBlockingQueue{
	private LinkedList queue = new LinkedList<>();
	private int MaxSize = 0;
	public MyBlockingQueue(int size) {
		this.MaxSize = size;
	}
	
	//--如果满了还要加 要阻塞当前操作线程
	public synchronized void add(Object obj){
		try {
			if(queue.size() >= this.MaxSize){
				//--不能往里加,阻塞当前线程,直到有人取走,队列变为非满
				this.wait();
			}
			queue.add(obj);
			this.notify();
		} catch (InterruptedException e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}
	
	//--如果空了还要取 要阻塞当前操作线程
	public synchronized Object take(){
		try {
			if(queue.size() <=0 ){
				this.wait();
			}
			Object obj = queue.poll();
			this.notify();
			return obj;
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}
}

public class Demo04 {
	public static void main(String[] args) {
		MyBlockingQueue bq = new MyBlockingQueue(3);
		new Thread(new Runnable() {
			@Override
			public void run() {
				bq.add("aaa");
				bq.add("bbb");
				bq.add("ccc");
				bq.add("ddd");
				bq.add("eee");
			}
		}).start();
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				System.out.println(bq.take());
				System.out.println(bq.take());
				System.out.println(bq.take());
				System.out.println(bq.take());
				System.out.println(bq.take());
			}
		}).start();
	}
}
线程之间的状态转换

在这里插入图片描述

多线程-其他方法
setDaemon()
  • 线程从是否可以独立执行的角度,可以分为用户线程和守护线程
  • 通常创建出来的线程都是用户线程,但可以通过setDaemon(true)将用户线程转换为守护线程用户线程可以独立运行 守护线程不可以 也就是说 如果进程中 已经没有用户线程 只剩下守护线程 则守护线程会自动退出
    在这里插入图片描述
    目录,再往上一拉
    案例:王者荣耀
package cn.tedu.thread;

/**
 * 守护线程 - 王者荣耀 游戏开发
 */
public class Demo05 {
	public static void main(String[] args) throws Exception {
	
		new Thread(new ShuiJing()).start();
		
		Thread t1 = new Thread(new Hero("安其拉")); 
		Thread t2 = new Thread(new Hero("孙悟空")); 
		Thread t3 = new Thread(new Hero("诸葛亮")); 
		Thread t4 = new Thread(new Hero("吕布")); 
		Thread t5 = new Thread(new Hero("亚瑟"));
		
		t1.setDaemon(true);
		t2.setDaemon(true);
		t3.setDaemon(true);
		t4.setDaemon(true);
		t5.setDaemon(true);
		
		t1.start();
		t2.start();
		t3.start();
		t4.start();
		t5.start();
		
		while(ShuiJing.blood>0){
			Thread.sleep(1000);
			ShuiJing.blood -= 8;
		}
	}
}

class Hero implements Runnable{
	private String name = null;
	public Hero(String name) {
		this.name = name;
	}
	
	@Override
	public void run() {
		while(true){
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(name+"说:为了水晶,冲啊~~~碾碎他们。。");
		}
	}
	
}

class ShuiJing implements Runnable{
	public static int blood = 100;
	@Override
	public void run() {
		while(this.blood>0){
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("全军出击,守卫水晶。。血量剩余"+blood);
		}
		System.out.println("水晶被打爆了。。游戏结束。。。");
	}
}

目录,再往上一拉

join()
  • 在当前线程执行时可以调用另一个线程的join方法 ,则当前线程立即进入阻塞模式,直到被join进来的线程执行完成为止
package cn.tedu.thread;

/**
 * join
 * @author Administrator
 *
 */
public class Demo06 {
	public static void main(String[] args) throws Exception {
		System.out.println("上数学课。。。");
		System.out.println("上数学课。。。");
		System.out.println("上数学课。。。");
		System.out.println("上数学课。。。");
		Thread t = new Thread(new Thread_06());
		t.start();
		t.join();//将进入阻塞模式 直到 t执行完成
		System.out.println("上数学课。。。");
		System.out.println("上数学课。。。");
		System.out.println("上数学课。。。");
		System.out.println("上数学课。。。");
	}
}

class Thread_06 implements Runnable{
	@Override
	public void run() {
		System.out.println("做广播体操开始。。。");
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("做广播体操结束。。。");
	}
}
yield()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值