什么是多线程

本文介绍了Java中的多线程概念,包括多线程的优势、主线程、创建线程的两种方式(继承Thread和实现Runnable)、线程调度、守护线程、同步代码实现以及单例模式的应用。通过实例演示了如何避免死锁问题。

-----------多线程-------------

什么是多线程

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;
			}
		}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值