Java笔记(线程、锁)

/**
 * 线程
 * 锁
 */



1.线程
	例如:迅雷同时下载多个任务,迅雷是一个进程,每个任务就是一个线程

	线程池:存放的是等待CPU调用的线程
		当调用线程的start()方法,该线程会加入CPU的线程,等待CPU调用
		CPU会随机的从线程池选择一个线程运行,并且分配给它一个时间
		当CPU分配的时间段到了,如果任务还没有结束,CPU也会让当前线程暂停运行,等待下一次调用
		.......
		当某一个线程的任务执行完成,它的任务就结束了,CPU就会把该线程从线程池中销毁

	如果是一个CPU(中央处理器),并且是单核CPU,不可能在同一时刻执行多个任务 
	线程是否执行,是由CPU来决定
	多个线程,CPU如何调用:CPU会把系统执行任务的时间分割成多个时间片段,会在不同的时间段调用不同的线程去运行,由于这个时间间隔非常短,我们感觉不到,所以会以为它是同时运行的

	1.1 线程的第一种创建方法

		1.编写类,继承于 Thread类
		2.重写父类的 run() 方法 这个方法称为"线程体",里面包含线程要具体执行的代码
		3.创建编写类的实例,调用实例的start()方法启动线程

		// 线程
		public class Thread1 extends Thread {

		private String name;

		public Thread1(String name) {
			super();
			this.name = name;
		}

		@Override
			public void run() {
				for(int i = 0; i < 100; i++) {
				System.out.println("线程 " + name);
				}
			}
		}
		
		// 启动线程
		Thread1 t1 = new Thread1("1---------");
		Thread1 t2 = new Thread1("2---------------------");

		// 运行不分前后,谁先抢到 start() 里面的 run() 谁先运行
		t1.start();
		t2.start();

	1.2 线程的第二种创建方法

		1.编写类,实现与 Runnable接口
		2.重写接口中的 run() 方法
		3.Thread th = new Thread(接口实现类的实例)
		4.th.start()

		// 线程
		public class Thread2 implements Runnable {

			@Override
			public void run() {
				for(int i = 0; i < 1000; i++) {
					try {
						Thread.sleep(2000);	// 休眠 2000 毫秒,之后接着运行后面的代码
					} catch (Exception e) {
		
					}
					Thread.yield();		// 礼让线程,把控制权交给其他线程来运行(重新抢夺 run())
					System.out.println(Thread.currentThread().getName());			
				}
			}
		}

		// 启动线程
		Thread t1 = new Thread(new Thread2(), "线程1--------");		// 后面的字符串是线程的名字
		Thread t2 = new Thread(new Thread2(), "线程2------------------");
		
		t1.start();
		t2.start();
		
		t2.setPriority(10);	// 设置线程的优先级,范围是1-10(只是概率大一点,不是绝对)
		t1.setName("1231");	// 设置线程名字

2.锁
	线程安全:一个线程正在操作一个对象或者一个方法,其他线程必须等待该线程访问结束以后才允许操作该对象或者方法
	线程不安全:一个对象或者一个方法可以被多个线程同时访问
	我们可以通过锁而达到线程安全	

	2.1 synchronized 同步锁

		1.这个关键字如果修饰方法表示该方法只允许被一个线程访问,如果方法中已经存在线程,其他线程必须等待该线程退出方法后才可以访问该方法
		2.使用同步块,表示方法中的其他代码依然可以被多个线程访问,但是同步块中的代码只允许一个线程访问
		
		如果多个线程操作了共享资源,操作共享资源的代码都应该放在同步锁中
		使用锁的前提是这个对象是多个线程都会操作的同一个对象

		// 三个窗口同时买卖票,票只有 100 张,一张票只能卖出一次
		
		//售票类
		public class Window implements Runnable {

			private static int ticket = 0;

			@Override
			public void run() {

				while (true) {
					synchronized (this) {
						if (ticket < 1000) {
							ticket++;
							System.out.println(Thread.currentThread().getName() + "卖了第 " + ticket + " 张票");
						} else {
							break;
						}
					}	
				}
			}	
		}

		//三个窗口卖票
		Window w = new Window();
		
		Thread t1 = new Thread(w, "武汉售票口");
		t1.start();
		
		Thread t2 = new Thread(w, "武昌售票口");
		t2.start();
		
		Thread t3 = new Thread(w, "汉口售票口");
		t3.start();
		
	2.2 Lock锁(接口,通过其子类来实例化Lock接口)
		
		//需要上锁的类
		public class LockUp implements Runnable {

			private Lock lock = new ReentrantLock();	//创建锁
	
			@Override
			public void run() {
				for(int i = 0; i < 100; i++) {
					lock.lock();		//获得锁(上锁)
					System.out.println(Thread.currentThread().getName() + i);
					lock.unlock();		//释放锁
				}
			}
		}

		// 启动线程
		LockUp lu = new LockUp();
		
		Thread t1 = new Thread(lu, "线程1----------");
		t1.start();
		
		Thread t2 = new Thread(lu, "线程2----------------------");
		t2.start();

	
	代码加锁的好处与弊端:
		好处:线程安全,数据不会出错
		弊端:1.代码如果加了锁,它会消耗更多的系统资源,会降低程序的性能
			2.如果频繁加锁,容易产生死锁

	2.3 死锁
		在争夺资源时,都在等待对方释放锁,程序进入一种无限等待的状态
	
		// 演示死锁的类
		class Obj{
			static Object objA = new Object();
			static Object objB = new Object();
		}	
		// Obj 和 DieLock 在一个类里面
		public class DieLock implements Runnable {

			private boolean flag;
	
			public DieLock(boolean flag) {
				this.flag = flag;
			}

			@Override
			public void run() {
				if (flag) {
					synchronized (Obj.objA) {
						System.out.println("线程1锁住了objA");
						synchronized (Obj.objB) {
							System.out.println("线程1锁住了objB----------------线程1的任务结束了");
					
						}
					}	
				}else {
					synchronized (Obj.objB) {
						System.out.println("线程2锁住了objB");
						synchronized (Obj.objA) {
							System.out.println("线程2锁住了objA----------------线程2的任务结束了");
					
						}
					}	
				}
		
			}
		}

		// 启动线程
		Thread td1 = new Thread(new DieLock(true));
		td1.start();

		Thread td2 = new Thread(new DieLock(false));
		td2.start();

	产生死锁的四个必要条件

		1.互斥条件:一个资源只能被一个进程占用
		2.不可剥夺条件:某个进程占用了资源,只能它自己去释放
		3.请求和保持条件:某个进程之前申请了资源,还想再申请资源,之前的资源还在占用着
		4.循环等待条件:一定会有两个线程互相等待
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值