多线程

线程创建

 * 创建线程的方式有两种 , 一种是实现Runnable接口 . 另一种是继承Thread类
 * 继承类的可以直接创建线程对象 , 实现接口的不行 , 得创建实现类的对象

实现Runnable接口

class Processor_02 implements Runnable {

	@Override
	public void run() {
		for (int i = 0; i < 5; i++) {
			// 获取当前线程的对象  因为这个实现类没有继承Thread , 所以Thread里的方法不能直接
			//调用 , 需要先获取这个线程对象 , 才能使用Thread里的对象
			System.out.println (Thread.currentThread().getName()+ i);
		}
	}

}

继承Thread类


class Processer_01 extends Thread{
	@Override
	public void run() {
		// 继承Thread类 , 还需要覆写run方法才算是一个线程类
		for (int i = 0; i < 5; i++) {
			System.out.println("线程测试1 :" + i); 
		}
	}
}
// main方法也是一个线程 , 由于现在计算机大多是多核 , 所以mian方法的线程和新建的
// 线程类对象是并行 但是如果多来几个线程对象 , 就有几个并发了
// 并行是几个线程在几个处理器上同时处理多个任务 , 
// 并发是在某一时刻 , 线程过多 , 一个核可能得同时处理好几个线程 
// 这时 , 在执行某个程序的时候可能不会直接到他执行完 , 在执行到一半的时候 , 时间片(举例)
// 可能就已经用完了 , 这时会处理下一个线程 , 这时就叫做并发 , 而平时所说的高并发 , 就是
// 在这时会有很多个线程来抢占时间片 , 但是这些线程可能会处理同一个数据 , 在这种时刻 ,
// 很容易就会出现读脏数据等错误 , 这就是我们接下来为什么要加S锁和L锁

线程的生命周期 sleep currentThread

*  线程的生命周期 : 创建 , 就绪 , 运行 , 阻塞 (就绪,运行,阻塞.....), 死亡
 *  
 *  currentThread() : 静态方法,用于获取当前线程的对象,写在哪个线程中,就获取哪个线程的对象
 *  
 *  sleep(long m) : 静态方法,让当前线程睡眠,需要传入对应的毫秒数,写在哪个线程就睡眠哪个线程
 *  
 *  这两个方法都是静态方法 , 是在哪里调用的就是谁 , 与是哪个对象调用的无关 , 至于方法有关
 *  比如 , 在main方法里调用 , 但是有时候很容易出错 , 比如 
 *  在调用的时候在他前面加个对象再调用 , 依然是main调用的 , 而不是哪个对象 , 因为
 *  在编译阶段 , 编译器会直接将这个错误语法纠正成为方法名 , 然后再编译
public class Thread_Lifecycle {
	public static void main(String[] args) {
		Thread thread2 = new Thread(new Processor_02());
		thread2.start(); 
		// 先执行哪个并不清楚
		for (int i = 0; i < 5; i++) {
			try {
				// 但是在执行main方法的时候 , 会睡眠1秒钟 , 所以在执行的时候 , 会显得是先执行了Thread2
				/**
				 * Thread-00
					Thread-01
					Thread-02
					Thread-03
					Thread-04
					main
					main
					main
					main
					main
				 */
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName());
		}
		
		
	}
}

interrupt() 强制唤醒

 * interrupt()
 *  强制唤醒 : 相当于一个是睡到自然醒 , 一个是被闹铃叫醒 , 被闹铃叫醒就是强制唤醒
public class Thread_Interrupt {
	public static void main(String[] args) {
		Thread thread3 = new Processer_03();
		thread3.start();
		
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// main休眠3秒 
		
		// 然后在这里唤醒Thread3
		thread3.interrupt();
		/* 唤醒之后就是异常了 , 如果是自然唤醒 , 就是正常执行了
		 * java.lang.InterruptedException: sleep interrupted
			at java.lang.Thread.sleep(Native Method)
			at day_01.Processer_03.run(Thread_Interrupt.java:30)
			叮叮叮~闹钟响了
			开始上班
		 */
	}
}

class  Processer_03 extends Thread{
	@Override
	public void run() {
		try {
			Thread.sleep(300000);
			System.out.println("哎呀,睡醒了");
		} catch (InterruptedException e) {
			e.printStackTrace();
			System.out.println("叮叮叮~闹钟响了");
		}
		System.out.println("开始上班");
	}
}

优先级

 * 线程有十个等级的优先级
 * 		分别是1~10
 * 在Thread类中 , 定义了三个常量 , 代表了三个优先级别 
 * 最高 10 Thread.MAX_PRIORITY
 * 正常  5 Thread.NORM_PRIORITY
 * 最高  1 Thread.MIN_PRIORITY  
 *   获取该线程的优先级 : getPriority();
 *   设置该线程的优先级 : setPriority();
 *   
 *   线程创建时会继承父线程的优先级 , Thread类的优先级是5
 *   优先级只是代表在同时抢占一个资源的时候会优先响应优先级别高的 , 所以并不是优先级越高 
 *   就越先执行 
public class Thread_Priority {
	public static void main(String[] args) {
		Thread thread1 = new Processer_01(); 
		Thread thread2 = new Thread(new Processor_02());
		
		// 输出这两个线程的优先级 5 5
		System.out.println(thread1.getPriority());
		System.out.println(thread2.getPriority());
		thread1.setPriority(Thread.MIN_PRIORITY ); // 最小
		thread2.setPriority(Thread.MAX_PRIORITY ); // 最大
		// 1 10
		System.out.println(thread1.getPriority());
		System.out.println(thread2.getPriority());
		// 这时如果只有一个处理器 , 就应该等Thread2执行完之后 , 才会执行thread1
		thread1.start();
		thread2.start();
		/*
		 * 线程测试1 :0
			线程测试1 :1
			测试线程2 : 0
			线程测试1 :2
			测试线程2 : 1
			测试线程2 : 2
			测试线程2 : 3
			测试线程2 : 4
			线程测试1 :3
			线程测试1 :4    
			很显然 , 现在并不是2先执行 , 因为处理器很多 , 不需要抢占 , 所以有没有优先级区别不大
		 */
	}
}

Thread.yield() 让时间片

 * Thread.yield(); 静态方法 , 暂停执行当前方法 , 且开始执行其他方法
 * 相同优先级就会让行 , 如果优先级较低 , 就不会让
public class Thread_Yield {
	public static void main(String[] args) {
		Thread t1 = new Processor_05();
		t1.start();
		for (int i = 0; i < 5; i++) {
			System.out.println("main = "+i);
		}
		/*
		 * 如果是一个处理器, 会先执行完main方法 
		 * main = 0
			Thread-0 = 0
			Thread-0 = 1
			main = 1
			main = 2
			main = 3
			main = 4
			Thread-0 = 2
			Thread-0 = 3
			Thread-0 = 4
		 */
	}
}

class Processor_05 extends Thread {
	@Override
	public void run() {
		for (int i = 0; i < 5 ; i++) {
			Thread.yield(); // 在优先级比他高或者相等的时候 , 让行
			System.out.println(getName()+" = "+i);
			
		}
	}
}

stop 终止线程

* stop : 终止一个线程,容易出现问题,不太安全,不推荐使用
 * 建议优雅的终止一个线程 : 使用标识符解决(true就结束线程 或者false就结束都可以)
public class Thread_Stop {
	public static void main(String[] args) {
		Thread t = new Processor_06();
		t.start();
		// 三秒之后  boolean isStop =true;
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		 // 不能直接调用isStop 因为这是子类特有的属性 , 所以不能使用多态 , 用了多态还需要向下转型
		Processor_06 p = (Processor_06)t;
		p.isStop = true ; // Thread-0 = 5 执行到这里就结束了
	}
}


class Processor_06 extends Thread {
	// 如果是true 就终止线程
	boolean isStop = false;
	@Override
	public void run() {
		for (int i = 0; true; i++) {
			// 判断是否终止
			if (isStop) {
				return;
			}
			System.out.println(Thread.currentThread().getName() + " = " + i);
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

join 线程合并

/**
 * join : 线程合并 , 让指定线程在该线程运行完之后再执行 , 为了线程安全 
 * @杜智慧
 *
 * @Date 2021年1月28日
 */
public class Thread_Join {
	public static void main(String[] args) throws InterruptedException {
		Thread thread = new Thread(new Processer_04());
		
		thread.start();
		// 这句话开始,当前线程(也就是main),需要等待thread线程执行完之后,再继续执行
		thread.join(); // 在哪个线程里用就是与哪个线程合并 , 合并之后先执行调用join方法的线程
		// 再执行这句方法调用所在的线程
		/*
		 * Thread-0 = 0
			Thread-0 = 1
			Thread-0 = 2
			Thread-0 = 3
			Thread-0 = 4
			main = 0
			main = 1
			main = 2
			main = 3
			main = 4
		 */
		for (int i = 0; i < 5; i++) {
			System.out.println(Thread.currentThread().getName() + " = " + i);
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}



class Processer_04 implements Runnable {
	@Override
	public void run() {
		for (int i = 0; i < 5; i++) {
			System.out.println(Thread.currentThread().getName() + " = " + i);
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

synchronized S锁

 * 线程同步 : 当多个线程操作同一个数据的时候,为了保证数据的一致 
 * 线程同步本质就是数据同步,是一种安全机制
 * 就是在某一线程操作一个数据的时候 , 其他线程需要访问这个数据就需要等待 
 * S锁 L锁 , 大体上与PV原语差不多 , 拿资源 , 在使用这个资源的时候 ,
 * 在V原语没有释放这个资源之前,   其他人不许用
 * 
 * 
 * 异步编程 : 线程之间是完全独立的,谁的运行也不会受到别的线程的影响
 * 
 * 只要对方法加上 synchronized的成员方法,就代表该方法不能被多个线程同时访问
public class Thread_synchronized {
	public static void main(String[] args) {
		Account account = new Account(500);
		
		// 创建两个线程对象 , 同时去取钱
		Thread t1 = new Thread(new Processor_07(account));
		Thread t2 = new Thread(new Processor_07(account));
		/*
		 * 这两个线程开始执行的 时候 , t调用Processor_07类的run方法
		 * 然后Processor_07再调用Account的withDraw方法 , 这两个两个线程对象 , 同时去取钱
		 * 这时很容易出现读脏数据 , 不可重复读 , 幻读等错误
		 */
		t1.start();
		t2.start();
		/*
		 * Thread-0 取款成功,取款1000元,余额是 : 400.0
		 *	Thread-1 取款成功,取款1000元,余额是 : 300.0
		 * 这里没有出现错误就是因为withDraw方法被synchronized修饰了
		 */
	}
}


//线程类
class Processor_07 implements Runnable {
	// 保存账户 对象
	private Account account;

	public Processor_07(Account account) {
		this.account = account;
	}

	@Override
	public void run() {
		account.withDraw(100);
		System.out.println(Thread.currentThread().getName()
				+ " 取款成功,取款1000元,余额是 : " + account.getBalance());
	}
}

//实体类
class Account {
	private double balance;

	public Account(double balance) {
		this.balance = balance;
	}

	public double getBalance() {
		return balance;
	}

	public void setBalance(double balance) {
		this.balance = balance;
	}

	/**
	 * 取钱方法
	 * 
	 * @param money
	 */
	// synchronized 是一个修饰词 , 被这个词修饰的方法不允许多个线程同时访问 , 否则极易出错
	public synchronized  void withDraw(double money) {
		double after = balance - money;
//		System.out.println(Thread.currentThread().getName()+" 我来取钱啦~");
		try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		balance = after;
	}
}

Lock L锁

* Lock锁 , 只能修饰语句块 Lock只有代码块锁,synchronized有代码块锁和方法锁
 * 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)
 * 
 * 只是开启加锁和关闭锁 都是需要手动 , 所以他又叫显式锁 , synchronized是隐式锁,超出作用域 自动解锁
 * 
public class Thread_Lock {
	// private Lock lock = new ReentrantLock();  class ATM里的锁 创建锁对象
	//	java.util.concurrent.locks.ReentrantLock.ReentrantLock()
	// lock.lock()是开锁
	// lock.unlock()是解锁
	// 创建一个放钱的 , 两个拿钱的 上一个试验synchronized的原理差不多
	ATM atm = new ATM(5000);
	Thread t1 = new Thread(new Processor_08(atm));
	Thread t2 = new Thread(new Processor_08(atm));
	 
}

//线程类
class Processor_08 implements Runnable {
	// 保存账户 对象
	private ATM atm;

	public Processor_08(ATM atm) {
		this.atm = atm;
	}

	@Override
	public void run() {
		atm.withDraw(1000);
	}
}
class ATM {
	private double balance;

	// 锁
	private Lock lock = new ReentrantLock();
	public ATM(double balance) {
		super();
		this.balance = balance;
	}

	public void withDraw(double money) {
		System.out.println(Thread.currentThread().getName() + " 进来了");
		// ---- 非访问数据区域,不需要同步
		// 开启锁 , 数据同步
		lock.lock();
		try {
			double after = balance - money;
			balance = after;
			System.out.println(Thread.currentThread().getName() + "取钱成功,取钱"
					+ money + ",剩余" + balance + "元");
		}finally{
			// 解锁
			lock.unlock();
		}
		
		// ---- 非访问数据区域,不需要同步
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值