java-线程

1.进程

进行中的应用程序,有独立的内存空间和系统资源

2.线程

线程是CPU运算的最小单位,包含在进程之中

3. 进程和线程的关系

线程是包含在进程之中的,一个进程至少包含一个线程,否则无法执行

进程和线程的关系,就像车身和车轮,不是越多越好,要结合实际的硬件环境

4. 线程的执行

多线程在单核心CPU下是轮流交替执行,因为每个线程执行的时间较短,切换频率较高,所以我们感知不到这个过程,宏观上感知就是"同时"执行的,实际上是轮流随机交替执行。

5. 并发和并行

并发:同时发生,轮流交替执行

并行:真正意义上的同时执行

6. 线程的名称

java.lang.Thread类 线程类

main线程为主线程

getName()获取线程名

setName()设置线程名

currentThread()表示获取当前线程对象

package com.qfedu.test3;
/**
 * 	java.lang.Thread类 线程类
 * 	main线程为主线程   
 * @author WHD
 *
 */
public class TestThread1 {
	public static void main(String[] args) {
		Thread th1 = Thread.currentThread();
		
		System.out.println("当前线程名称:" + th1.getName());
		
		th1.setName("主线程");
		
		System.out.println("当前线程名称:" + th1.getName());
		
	}
}

7.线程的创建

7.1 继承Thread类

创建线程方式1:继承Thread类 重写run方法

run方法中为线程执行的逻辑代码

面试题:调用start方法和调用run方法的区别?

调用start方法会创建新的线程,调用run方法不会创建新的线程,依然使用main线程

package com.qfedu.test3;
/**
 * 	创建线程方式1:继承Thread类  重写run方法
 * 	run方法中为线程执行的逻辑代码
 * 
 * 	面试题:调用start方法和调用run方法的区别?
 * 	调用start方法会创建新的线程,调用run方法不会创建新的线程,依然使用main线程
 * @author WHD
 *
 */
public class MyThread1 extends Thread{
	@Override
	public void run() {
		// 线程的默认名称为  Thread-0  -1 -2
		System.out.println("当前线程的名称是:" + Thread.currentThread().getName());
	}
	
	
	public static void main(String[] args) {
		MyThread1 mt1 = new MyThread1();
		
		mt1.run();
	}
	
}

7.2 实现Runnable接口

创建线程方式2:实现Runnable接口 重写run方法

Runnable不能直接创建线程对象 需要作为参数传入Thread类构造方法中 来创建对象

package com.qfedu.test4;
/**
 * 	创建线程方式2:实现Runnable接口 重写run方法
 * 	Runnable不能直接创建线程对象  需要作为参数传入Thread类构造方法中 来创建对象
 * @author WHD
 *
 */
public class MyThread1 implements Runnable{

	@Override
	public void run() {
		for (int j = 0; j < 10; j++) {
			System.out.println("当先线程的名称为:" + Thread.currentThread().getName() + "====" + j);
			
		}
	}
	
	
	public static void main(String[] args) {
		MyThread1 mt = new MyThread1();
		
		Thread th1 = new Thread(mt, "----线程A----");
		Thread th2 = new Thread(mt, "线程B");
		
		th1.start();
		th2.start();
		
		
	}
}

8. 线程的状态

线程的状态:创建–就绪–运行–阻塞–死亡

package com.qfedu.test5;
/**
 * 	线程的状态:创建--就绪--运行--阻塞--死亡
 * @author WHD
 *
 */
public class MyThread1 implements Runnable{

	@Override
	public void run() { // 运行状态
		System.out.println("run方法开始执行……");
		try {
			Thread.sleep(3000); // 阻塞状态
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		for (int j = 0; j < 10; j++) {
			System.out.println("当先线程的名称为:" + Thread.currentThread().getName() + "====" + j);
			
		}
	} // 死亡状态
	
	
	public static void main(String[] args) {
		MyThread1 mt = new MyThread1();
		
		Thread th1 = new Thread(mt, "----线程A----"); // 创建状态
		
		th1.start(); // 就绪状态
		
		
	}

}

9. 线程的优先级

线程的优先级:默认为5,最小为1,最大为10

优先级高的线程表示获取CPU资源的概率增大,但是不能保证一定优先执行

设置线程优先级可以使用父类Thread提供的静态常量,也可以直接书写1~10之间的整数数值

package com.qfedu.test5;

/**
 * 	线程的优先级:默认为5,最小为1,最大为10  
 * 	优先级高的线程表示获取CPU资源的概率增大,但是不能保证一定优先执行
 * 
 * 	设置线程优先级可以使用父类Thread提供的静态常量,也可以直接书写1~10之间的整数数值
 * @author WHD
 *
 */
public class MyThread2 extends Thread{
	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println(Thread.currentThread().getName() + "---" + i );
		}
	}
	
	public static void main(String[] args) {
		MyThread2 th1 = new MyThread2();
		MyThread2 th2 = new MyThread2();
		
		th1.setName("---线程A---");
		th2.setName("线程B");
		
		System.out.println(th1.getPriority());
		System.out.println(th2.getPriority());
		
		th1.setPriority(6);
		th2.setPriority(7);
		
		th1.start();
		th2.start();
		
	}
}

10. 线程的休眠

线程休眠,属于阻塞状态,表示让当前线程暂停指定时间,然后再继续执行

sleep(long millis)

sleep(long millis,int nanos)

package com.qfedu.test6;
/**
 * 	线程休眠
 * 	sleep(long millis)
 * 	sleep(long millis,int nanos)
 * @author WHD
 *
 */
public class MyThread1 extends Thread{
	@Override
	public void run() {
		System.out.println("run方法开始执行");
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("run方法执行完毕");
	}
	
	
	
	public static void main(String[] args) {
		MyThread1 th1 = new MyThread1();
		th1.setName("线程A");
		
		th1.start();
		
	}
}

11.线程的插队

线程的插队 表示当前线程插队执行另外一个线程

join() 无参表示插队线程执行完毕 再执行被插队线程

join(long millis) 有参 毫秒数表示插队线程只能插队指定的时间

join(long millis,it nanos) 有参 毫秒数表示插队线程只能插队指定的时间

package com.qfedu.test6;
/**
 * 	线程的插队  表示当前线程插队执行另外一个线程  
 * 	join()  无参表示插队线程执行完毕 再执行被插队线程
 * 	join(long millis) 有参 毫秒数表示插队线程只能插队指定的时间
 * 	join(long millis,it nanos) 有参 毫秒数表示插队线程只能插队指定的时间
 * @author WHD
 *
 */
public class MyThread2 extends Thread{
	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + "---" + i);
		}
	}
	
	
	public static void main(String[] args) throws InterruptedException {
		MyThread2 th1 = new MyThread2();
		th1.setName("----线程A----");
		
		th1.start();
		
		for (int i = 0; i < 50; i++) {
			if(i == 20) {
				th1.join(3000);
			}
			System.out.println(Thread.currentThread().getName() + "----" + i);
		}
		
	}

}

12. 线程的礼让

yield() 线程的礼让 只是提供一种可能 并不一定能够保证礼让

package com.qfedu.test6;
/**
 * 	yield() 线程的礼让 只是提供一种可能 并不一定能够保证礼让
 * @author WHD
 *
 */
public class MyThread3 extends Thread{
	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			if(i == 10) {
				System.out.println("=====线程礼让了=====");
				Thread.yield();
			}
			System.out.println(Thread.currentThread().getName() + "--" + i);
		}
	}
	
	public static void main(String[] args) {
		MyThread3 th1 = new MyThread3();
		MyThread3 th2 = new MyThread3();
		
		th1.setName("---线程A---");
		th2.setName("线程B");
		
		th1.start();
		th2.start();
		
		
		
	}

}

13.课堂练习

需求说明:每个线程代表一个人,可设置每人爬山速度,每爬完100米显示信息,爬到终点时给出相应提示

分析:

1.先继承Thread类,重写run方法

2.编写3属性 name表示角色名/线程名 length 总长度 time 每爬100米耗时

3.编写构造方法完成三个属性初始化,其中name属性调用父类构造初始化

package com.qfedu.test7;
/**
 * 	需求说明:每个线程代表一个人,可设置每人爬山速度,每爬完100米显示信息,爬到终点时给出相应提示
 * 	分析:	1.先继承Thread类,重写run方法
 * 			2.编写3属性  name表示角色名/线程名   length 总长度    time 每爬100米耗时
 * @author WHD
 *
 */
public class ClimbThread extends Thread{
	String name;
	int length;
	int time;
	
	
	public ClimbThread(String name, int length, int time) {
		super(name);
		this.length = length;
		this.time = time;
	}


	@Override
	public void run() {
		while(length > 0) {
			try {
				Thread.sleep(time);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			length -= 100;
			System.out.println(Thread.currentThread().getName() + "爬了100米还剩余" + length + "米");
		}
		
		System.out.println("**************恭喜" + Thread.currentThread().getName() + "爬到了山顶**************");
	}
	
	public static void main(String[] args) {
		ClimbThread oldMan = new ClimbThread("赵四", 1000, 1000);
		ClimbThread youngMan = new ClimbThread("练习两年半的练习生", 1000, 500);
		
		oldMan.start();
		youngMan.start();
		
		
	}
}

某科室一天需看普通号50个,特需号10个,

特需号看病时间是普通号的2倍,(耗时不同是指线程休眠时间不同)

开始时普通号和特需号并行叫号,(同时start)

叫到特需号的概率比普通号高,(线程优先级不同)

当普通号叫完第10号时,要求先看完全部特需号,再看普通号(线程插队)

使用多线程模拟这一过程

分析:我们可以使用当前类作为其中一个角色,比如当前类作为特需号,在main方法中主线程作为普通号

package com.qfedu.test7;
/**
 * 	某科室一天需看普通号50个,特需号10个,
 * 	特需号看病时间是普通号的2倍,(耗时不同是指线程休眠时间不同)
 * 	开始时普通号和特需号并行叫号,(同时start)
 * 	叫到特需号的概率比普通号高,(线程优先级不同)
 *	当普通号叫完第10号时,要求先看完全部特需号,再看普通号(线程插队)
 *	使用多线程模拟这一过程
 *
 *	分析:我们可以使用当前类作为其中一个角色,比如当前类作为特需号,在main方法中主线程作为普通号
 * @author WHD
 *
 */
public class Special extends Thread{
	@Override
	public void run() {
		for (int i = 1; i <= 10; i++) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + i + "号在看病");
		}
		
		System.out.println(Thread.currentThread().getName() + "就诊完毕");
	}
	
	
	public static void main(String[] args) throws InterruptedException {
		Special s1 = new Special();
		s1.setName("****特需号****");
		s1.setPriority(MAX_PRIORITY);
		s1.start();
		
		Thread.currentThread().setName("普通号");
		
		for (int i = 1; i <= 50; i++) {
			Thread.sleep(500);
			System.out.println(Thread.currentThread().getName() + i + "号在看病");
			
			if(i == 10) {
				s1.join();
			}
		}
		System.out.println(Thread.currentThread().getName() + "看病完毕");
		
		
	}
}

14.synchronized关键字

14.1 版本1

使用多线程模拟抢票 三个人(三个线程)抢10张票

必须保证:

1.票号不能重复

2.抢到的票不能多于10张

目前存在问题:票一共卖出了30张 总数以及票号都不对

解决方案:使用static修饰

package com.qfedu.test8;
/**
 * 	使用多线程模拟抢票  三个人(三个线程)抢10张票
 * 	必须保证:
 * 		1.票号不能重复
 * 		2.抢到的票不能多于10张
 * 
 * 	目前存在问题:票一共卖出了30张 总数以及票号都不对 
 * 	解决方案:使用static修饰
 * @author WHD
 *
 */
public class Buyticket1 extends Thread{
	int ticketCount = 10;

	@Override
	public void run() {
		while(ticketCount > 0) {
			ticketCount--;
			System.out.println(Thread.currentThread().getName() + "抢到了第"+ (10 - ticketCount )+"张票,还剩余"+ ticketCount +"张");
		}
		System.out.println("票售罄了");
	}
	
	public static void main(String[] args) {
		Buyticket1 zhaosi = new Buyticket1();
		Buyticket1 guangkun = new Buyticket1();
		Buyticket1 dana = new Buyticket1();
		
		zhaosi.setName("赵四");
		guangkun.setName("广坤");
		dana.setName("大拿拿");
		
		zhaosi.start();
		guangkun.start();
		dana.start();
	}
	
	
}

14.2 版本2

使用多线程模拟抢票 三个人(三个线程)抢10张票

必须保证:

1.票号不能重复

2.抢到的票不能多于10张

目前存在问题:使用static修饰,依然会存在票的总数以及票号是不对的

解决方案:必须保证同一时间只能有一个线程访问票的总数 以及 操作票的数量 才可以实现要求 使用synchronized关键字将需要同步的

代码包括起来

package com.qfedu.test8;
/**
 * 	使用多线程模拟抢票  三个人(三个线程)抢10张票
 * 	必须保证:
 * 		1.票号不能重复
 * 		2.抢到的票不能多于10张
 * 
 * 	目前存在问题:使用static修饰,依然会存在票的总数以及票号是不对的
 * 	
 * 	解决方案:必须保证同一时间只能有一个线程访问票的总数 以及 操作票的数量  才可以实现要求   使用synchronized关键字将需要同步的
 * 	代码包括起来
 * @author WHD
 *
 */
public class Buyticket2 extends Thread{
	static int ticketCount = 10;

	@Override
	public void run() {
		while(ticketCount > 0) {
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			ticketCount--;
			System.out.println(Thread.currentThread().getName() + "抢到了第"+ (10 - ticketCount )+"张票,还剩余"+ ticketCount +"张");
		}
		System.out.println("票售罄了");
	}
	
	public static void main(String[] args) {
		Buyticket2 zhaosi = new Buyticket2();
		Buyticket2 guangkun = new Buyticket2();
		Buyticket2 dana = new Buyticket2();
		
		zhaosi.setName("赵四");
		guangkun.setName("广坤");
		dana.setName("大拿拿");
		
		zhaosi.start();
		guangkun.start();
		dana.start();
	}
	
	
}

14.3 版本3

使用多线程模拟抢票 三个人(三个线程)抢10张票

必须保证:

1.票号不能重复

2.抢到的票不能多于10张

synchronized关键字:

单词:同步

适用场景:

  • 修饰代码块:表示此代码块同时只能有一个线程访问
  • 修饰方法:表示此方法同时只能有一个线程访问
package com.qfedu.test8;
/**
 * 	使用多线程模拟抢票  三个人(三个线程)抢10张票
 * 	必须保证:
 * 		1.票号不能重复
 * 		2.抢到的票不能多于10张
 * 
 * 	synchronized关键字:
 * 	单词:同步
 * 	适用场景:
 * 		修饰代码块:表示此代码块同时只能有一个线程访问
 * 		修饰方法:表示此方法同时只能有一个线程访问
 * 
 * @author WHD
 *
 */
public class Buyticket3 implements Runnable{
	int ticketCount = 10;

	@Override
	public void run() {
		while(true) {
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			synchronized(this) {
				if(ticketCount == 0) {
					break;
				}
				ticketCount--;
				System.out.println(Thread.currentThread().getName() + "抢到了第"+ (10 - ticketCount )+"张票,还剩余"+ ticketCount +"张");
			}
			
		}
		System.out.println("票售罄了");
	}
	
	public static void main(String[] args) {
		
		Buyticket3 bt3 = new Buyticket3();
		
		Thread th1 = new Thread(bt3,"赵四");
		Thread th2 = new Thread(bt3,"广坤");
		Thread th3 = new Thread(bt3,"大拿");
		
		th1.start();
		th2.start();
		th3.start();
		
		
		
		
		
		
		
	}
	
	
}

14.4 synchronized修饰方法
package com.qfedu.test8;
/**
 * 	synchronized关键字修饰方法
 * @author WHD
 *
 */
public class Buyticket5 implements Runnable{
	int ticketCount = 10;
	Object obj = new Object();

	@Override
	public synchronized void run() {
		while(true) {
			if(ticketCount == 0) {
				break;
			}
			ticketCount--;
			System.out.println(Thread.currentThread().getName() + "抢到了第"+ (10 - ticketCount )+"张票,还剩余"+ ticketCount +"张");
		}
		System.out.println("票售罄了");
	}
	public static void main(String[] args) {

		Buyticket5 bt3 = new Buyticket5();

		Thread th1 = new Thread(bt3,"赵四");
		Thread th2 = new Thread(bt3,"广坤");
		Thread th3 = new Thread(bt3,"大拿");

		th1.start();
		th2.start();
		th3.start();
	}
}

15.synchronized关键字补充

15.1 为什么同步代码块中写this,表示什么含义,能不能写其他的对象?

不是必须写this,只需要保证多个线程共享的是同一个资源就可以了,可以写其他对象,但是通常写this

synchronized:可修饰,静态代码方法、实例方法、代码块(代表锁类、实例、具体代码块 锁的范围递减的,(锁的范围越小越好))

package com.qfedu.test8;
/**
 * 	为什么同步代码块中写this,表示什么含义,能不能写其他的对象?
 * 	不是必须写this,只需要保证多个线程共享的是同一个资源就可以了,可以写其他对象,但是通常写this
 * @author WHD
 *
 */
public class Buyticket4 implements Runnable{
	int ticketCount = 10;
	Object obj = new Object();

	@Override
	public void run() {
		while(true) {
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			synchronized(this) {
				if(ticketCount == 0) {
					break;
				}
				ticketCount--;
				System.out.println(Thread.currentThread().getName() + "抢到了第"+ (10 - ticketCount )+"张票,还剩余"+ ticketCount +"张");
			}
			
		}
		System.out.println("票售罄了");
	}
	
	public static void main(String[] args) {
		
		Buyticket4 bt3 = new Buyticket4();
		
		Thread th1 = new Thread(bt3,"赵四");
		Thread th2 = new Thread(bt3,"广坤");
		Thread th3 = new Thread(bt3,"大拿");
		
		th1.start();
		th2.start();
		th3.start();
		
	}
	
}

15.2 规则

同一时刻只能有一个线程进入synchronized(this)同步代码块

当一个线程访问一个synchronized(this)同步代码块时,其他synchronized(this)同步代码块同样被锁定

当一个线程访问一个synchronized(this)同步代码块时,其他线程可以访问该资源的非synchronized(this)同步代码

package com.qfedu.test8;
/**
 * 	同一时刻只能有一个线程进入synchronized(this)同步代码块
 *	当一个线程访问一个synchronized(this)同步代码块时,其他synchronized(this)同步代码块同样被锁定
 *	当一个线程访问一个synchronized(this)同步代码块时,其他线程可以访问该资源的非synchronized(this)同步代码
 * @author WHD
 *
 */
public class Test extends Thread{
	@Override
	public void run() {
		System.out.println("非同步代码");
		
		synchronized (this) {
			System.out.println("同步代码块1");
		}
		
		synchronized (this) {
			System.out.println("同步代码块2");
		}
	}
}

15.3 线程安全的类

Vector Hashtable StringBuffer 这三个类均使用synchronized关键字修饰方法来实现线程安全

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值