多线程

多线程

1.概念
1.1 进程

进程:进行(执行)中的应用程序,我们称之为进程。进程属于CPU分配资源的最小单位。

1.2 线程

线程:线程属于CPU执行调度的最小单位。

1.3 进程和线程的关系

1.线程包含在进程之中,一个进程至少包含一个线程。

举例:进程和线程就像车身和车轮,车轮不是越多越好(线程不是越多越好,要根据实际的硬件环境)

1.4 线程的执行

多个线程是由CPU根据每个线程分配的时间片来决定随机轮流执行的。每个线程最多占用20ms,过了这个时间将切换的其他的线程。

1.5 并发和并行

举例:比如你去饭店点了两盘菜,最后你将这两盘菜吃完了,从饭店老板的角度来看属于同时吃完的,但是严格意义上来说,属于轮流交替吃完的。这中属于并发。并行是指比如你和你的同桌两个人,一人点一盘菜,这样才属于并行。

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

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

2.线程的创建
2.1 继承Thread类

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

2.适用于单继承,书写简单

package com.qfedu.test1;
/**
 * 	创建线程方式1:
 * 	继承Thread类  重写run方法 创建线程对象
 * @author WHD
 *
 */
public class Test2 extends Thread{
	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName() + "在 执行");
	}
	
	
	public static void main(String[] args) {
		Test2 th1  = new Test2();
		th1.start();
//		th1.run();
		Test2 th2 = new Test2();
		th2.start();
	}
}

2.2 实现Runnable接口

实现Runnable接口,重写run()方法

1.避免了单继承的局限性

2.便于数据的共享

package com.qfedu.test1;
/**
 * 	创建线程方式2:
 * 	实现Runnable接口 重写run方法
 * 
 * @author WHD
 *
 */
public class Test3 implements Runnable{

	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName() + "在执行");
	}
	
	public static void main(String[] args) {
		Test3 t = new Test3();
		Thread th1 = new Thread(t, "线程A");
		th1.start();
//		th1.run();
	}

}

2.3 调用start方法和调用run方法的区别(面试题)

调用start方法将会开启一个新的线程,但是调用run方法不会开启新的线程,只是使用main线程调用方法。

3.线程的状态(面试题)

1.创建 new Thread对象

2.就绪 start方法

3.运行 run方法

4.阻塞 sleep 、join方法

5.死亡 线程执行完毕

package com.qfedu.test2;
/**
 * 	线程的状态  
 * @author WHD
 *
 */
public class Test2 implements  Runnable{
	@Override
	public void run() { // 执行
		try {
			// 阻塞
			Thread.sleep(2000); // 休眠属于阻塞状态的表现之一
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		for (int i = 0; i < 10; i++) {
			System.out.println(Thread.currentThread().getName() + "线程" + i);
		}
	// 死亡
	}
	
	
	public static void main(String[] args) {
		Test2 t1 = new Test2(); 
		Thread th1 = new Thread(t1,"线程A"); // 创建
		Thread th2 = new Thread(t1,"线程B");
		th1.start(); // 就绪
		th2.start();
	}
}

4.线程常用的方法

currentThread() 获取当前线程对象

getName() 获取线程名称

setName() 设置线程名称

getPriority() 获取线程优先级

stop() 停止当前线程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dp5tmCjH-1603197207912)(.\img\线程方法1.png)]

package com.qfedu.test2;
/**
 * 	线程的优先级
 * 	默认为5 最大10 最小1
 * 	MAX_PRIORITY	10
 * 	MIN_PRIORITY	1	
 * 	NORM_PRIORITY	5
 * 	setPriority()设置优先级
 * 	getPriority()获取优先级
 * @author WHD
 *
 */
public class Test3 extends Thread{
	@Override
	public void run() {
		for (int i = 0; i < 5; i++) {
			System.out.println(Thread.currentThread().getName() + "线程执行" + i);
		}
	}
	
	public static void main(String[] args) {
		Test3 th1 = new Test3();
		Test3 th2 = new Test3();
		th1.setName("线程A");
		th2.setName("线程B");
		
		th1.setPriority(Thread.MAX_PRIORITY); 
		th2.setPriority(Thread.MIN_PRIORITY);
		System.out.println(th1.getPriority());
		System.out.println(th2.getPriority());
		th1.start();
		th2.start();
	}
	
}
package com.qfedu.test2;
/**
 * 	线程的休眠
 * 	sleep(long 毫秒)
 * 	sleep(long 毫秒,int 纳秒)
 * @author WHD
 *
 */
public class Test4 extends Thread{
	@Override
	public void run() {
		// 检查异常
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName());
	}
	
	public static void main(String[] args) {
		Test4 th1 = new Test4();
		th1.start();
	}
}
package com.qfedu.test2;
/**
 * 	线程的插队
 * 	join() 直到插队线程执行完毕 被插队线程再继续执行
 * 	join(long 毫秒) 被插队线程等待指定的时间 时间过后继续轮流交替来执行
 * 	join(long 毫秒,int 纳秒) 被插队线程等待指定的时间 时间过后继续轮流交替来执行
 * @author WHD
 *
 */
public class Test5 extends Thread{
	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			System.out.println(Thread.currentThread().getName() + "在执行" + i + "============================");
		}
	}
	
	public static void main(String[] args) throws InterruptedException {
		Test5 th1 = new Test5();
		th1.start();
		for (int i = 0; i < 100; i++) {
			System.out.println(Thread.currentThread().getName() + "主线程" + i);
			if(i == 10) {
				th1.join();
			}
		}
	}	
}

package com.qfedu.test2;
/**
 * 	线程的礼让
 * 	并不能保证一定会礼让其他线程 只是提供一种可能 用于缓解并发的问题
 * 	yield() 礼让
 * @author WHD
 *
 */
public class Test6 extends Thread{
	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			if(i == 3) {
				System.out.println("线程礼让");
				Thread.yield(); // 当前线程会礼让 如果此时本来执行的是线程A  执行完礼让 有可能会执行线程B
			}
			System.out.println(Thread.currentThread().getName() + "在执行");
		}
    }	
	
	public static void main(String[] args) {
		Test6 th1 = new Test6();
		Test6 th2 = new Test6();
		th1.setName("线程A");
		th2.setName("线程B");
		th1.start();
		th2.start();
	}
}
package com.qfedu.test2;
/**
 * 	线程的中断(停止)
 * 	interrupt()  设置线程中断标识为true  标识 此线程可以被中断
 * 	interrupted() 打印当前线程的是否可被中断 
 * 	stop()  停止线程
 * @author WHD
 *
 */
public class Test7 extends Thread{
		@Override
		public void run() {
			for (int i = 0; i < 10; i++) {
				if(i == 5) {
					Thread.currentThread().interrupt();
					Thread.currentThread().stop();
				}
				System.out.println(Thread.currentThread().getName() + "再执行" + i + "是否可被中断状态" + Thread.interrupted());
			}
		}


		public Test7(String name) {
			super(name);
		}



		public static void main(String[] args) {
			Test7 th1 = new Test7("线程A");
			th1.start();
		}
}

package com.qfedu.test2;
/**
 * isAlive()  判断线程是否还存活
 * @author WHD
 *
 */
public class Test8 extends Thread{
	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + "执行" + i);
		}
	}
	public static void main(String[] args) throws InterruptedException {
		Test8 th1 = new Test8();
		th1.setName("线程A");
		th1.start();
		Thread.sleep(1000);
		System.out.println("是否还存活" + th1.isAlive());
		
		Thread.sleep(5000);
		System.out.println("是否还存活" + th1.isAlive());
		
		
		
	}
}

5.线程安全synchronized关键字

在多线程访问同一个资源的情况,因为线程运行的特点:线程是随机轮流交替来执行的,所以多个线程访问同一个数据,会出现问题,我们可以使用同步关键字来解决

synchronized:

1.用于修饰代码块

​ a.同一时间只能有一个线程访问同步代码块中的内容

​ b.但是 其他线程可以访问没有被同步代码块包括的代码

​ c.其他的同步代码块 同样被锁定 (这些多个同步代码块所锁定的是同一个对象 比如 this)

2.用于修饰方法

​ 表示被同步关键字修饰的方法同一时间只能有一个线程访问

package com.qfedu.test4;
/**
 * 	假设有10张票
 * 	三个人抢票
 * 	票号是唯一的 总数不能超过10张
 * 	目前存在问题:
 * 		数量不能保证10个
 * 		票号不能保证唯一
 * 	synchronized 同步的意思
 * 	适用场景
 * 	1.同步代码块 
 * 		a.同一时间只能有一个线程访问同步代码块中的内容 
 * 		b.但是 其他线程可以访问没有被同步代码块包括的代码
 * 		c.其他的同步代码块 同样被锁定 (这些多个同步代码块所锁定的是同一个对象 比如 this)
 * 	2.同步方法
 * 		a.表示当前方法同一时间只有一个线程可以访问
 * @author WHD
 *
 */
public class BuyTicket2 implements Runnable{
	private  int ticketCount = 10;
	Object obj = new Object();
	
	@Override
	
	public  void run() {
		while(true) {
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			// synchronized 同步
			synchronized (obj) {
				if(ticketCount == 0) {
					break;
				}
				ticketCount --;
				System.out.println(Thread.currentThread().getName() + "抢到第"+(10 - ticketCount) +"张票,还剩余" + ticketCount);
			}
			
//			synchronized (this) {
//				System.out.println("hello word");
//			}
			// 其他逻辑代码
		}
	}
	
	public static void main(String[] args) {
		BuyTicket2 bt = new BuyTicket2();
		Thread zhaosi = new Thread(bt, "赵四");
		Thread guangkun = new Thread(bt, "广坤");
		Thread dana = new Thread(bt, "大拿");
		
		zhaosi.start();
		guangkun.start();
		dana.start();
		
	}
	
}

package com.qfedu.test4;
/**
 * 	假设有10张票
 * 	三个人抢票
 * 	票号是唯一的 总数不能超过10张
 * 	目前存在问题:
 * 		数量不能保证10个
 * 		票号不能保证唯一
 * 	synchronized 同步的意思
 * 	适用场景
 * 	1.同步代码块 
 * 		a.同一时间只能有一个线程访问同步代码块中的内容 
 * 		b.但是 其他线程可以访问没有被同步代码块包括的代码
 * 		c.其他的同步代码块 同样被锁定 (这些多个同步代码块所锁定的是同一个对象 比如 this)
 * 	2.同步方法
 * 		a.表示当前方法同一时间只有一个线程可以访问
 * @author WHD
 *
 */
public class BuyTicket3 implements Runnable{
	private  int ticketCount = 10;
	Object obj = new Object();

	@Override

	public synchronized void run() {
		while(ticketCount > 0) {
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			ticketCount --;
			System.out.println(Thread.currentThread().getName() + "抢到第"+(10 - ticketCount) +"张票,还剩余" + ticketCount);
		}
	}

	public static void main(String[] args) {
		BuyTicket3 bt = new BuyTicket3();
		Thread zhaosi = new Thread(bt, "赵四");
		Thread guangkun = new Thread(bt, "广坤");
		Thread dana = new Thread(bt, "大拿");

		zhaosi.start();
		guangkun.start();
		dana.start();

	}
}

6. 线程安全的类

线程安全:StringBuffer,Vector,Hashtable 以上这些类的方法都使用同步关键字修饰

线程不安全:StringBuilder,ArrayList,LinkedList,HashSet,HashMap,TreeSet…… 没有使用同步关键字修饰

() + “抢到第”+(10 - ticketCount) +“张票,还剩余” + ticketCount);
}
}

public static void main(String[] args) {
	BuyTicket3 bt = new BuyTicket3();
	Thread zhaosi = new Thread(bt, "赵四");
	Thread guangkun = new Thread(bt, "广坤");
	Thread dana = new Thread(bt, "大拿");

	zhaosi.start();
	guangkun.start();
	dana.start();

}

}


#### 6. 线程安全的类

> 线程安全:StringBuffer,Vector,Hashtable 以上这些类的方法都使用同步关键字修饰
>
> 线程不安全:StringBuilder,ArrayList,LinkedList,HashSet,HashMap,TreeSet……  没有使用同步关键字修饰

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值