javaSE_day13学习笔记 —— 线程(同步机制:synchronized关键字)

线程

1. 程序、进程、线程

  • 程序:静态的代码
  • 进程:程序动态的执行过程。产生、执行、消亡
  • 线程:动态执行的过程,是进程内最小的执行单位
    进程有直接独立的内存
    线程共享进程的资源,在进程内,每个线程可以完成独立的功能
    在这里插入图片描述
    eg: 线程之间实际上轮换执行
package cn.tedu.thread;

class T extends Thread{
	//线程完成的功能代码要写到线程体方法中,run()
	@Override
	public void run() {
		for (int i = 0; i <100; i++) {
			System.out.println("hello");
		}		
	}
}
class T2 extends Thread{
	//线程完成的功能代码要写到线程体方法中,run()
	@Override
	public void run() {
		for (int i = 0; i <100; i++) {
			System.out.println("Thread");
		}		
	}
}
public class MyThread {

	//主线程
	public static void main(String[] args) {
		T t1 = new T();
		T2 t2 = new T2();
		t1.start();
		t2.start();
	}
}

在这里插入图片描述

2. 线程创建的两种方式

  • 继承Thread类 —— 是线程类
  • 实现Runnable接口 —— 不是线程类

代码:要写到run()方法里
启动线程:start()方法
在这里插入图片描述
练习:用两种方法创建两个线程,一个打印10以内的偶数,一个打印10以内的奇数。
在这里插入图片描述

3. 线程类的构造方法

  • Thread()
  • Thread(String name)
  • Thread(Runnable r)
    • 功能:实现接口类的run方法
  • Thread(Runnable r,String name)

4. 常用方法

  • 返回正在运行的线程对象
    Thread t = Thread.currentThread();
  • 返回线程的名称
    t.getName();
  • 返回线程的唯一id
    t.getId();
  • 判断线程是否可用
    t.isAlive();
  • sleep方法:线程休眠
    Thread.sleep()
  • join方法:等待线程执行完毕
    t.join()

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 练习
  1. 模拟倒计时
package cn.tedu.thread;
/**
 * 模拟倒计时
 * @author Dell
 *
 */
public class Demo6 {
	public static void main(String[] args) {
		new Thread(
				()->{	
		            System.out.println(Thread.currentThread().getName());
					System.out.println("倒计时开始");
					for (int i = 10; i >0; i--) {						
						System.out.println(i);
						try {
							Thread.sleep(1000);
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
					System.out.println("过年啦");
				},"线程sleep") .start();
	}	
}
  1. 模拟图片显示和图片下载
package cn.tedu.thread;
/**
 * 练习:模拟图片显示和图片下载
 * @author Dell
 */
public class Demo8 {	
	public static void main(String[] args) {
		Thread t1 = new Thread() {
			public void run() {
				System.out.println("Thread-1:开始下载图片");
				for (int i = 1; i <=10; i++) {
					System.out.println("Thread-1:下载进度"+i+"%");
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				System.out.println("Thread-1:下载图片完成");
			}
		};
		Thread t2 = new Thread(
			()->{
				System.out.println("Thread-2:显示图片");
				try {
					t1.join();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("Thread-2:显示图片完成");
			});	
		//通常资源够用的情况下,将先执行的线程启动放在前面,则会先执行前面的线程		
		t2.start();
		t1.start();
	}
}

5. 守护线程 setDeamon(true)

  • 创建出来的线程:用户线程(前台线程)和守护线程(后台线程)。
  • 一般创建出来的线程都是用户线程。
  • 设置守护线程:调用setDeamon(true);
  • 用法和创建线程方式没有区别
  • 区别:用户线程执行完毕后,守护线程无条件停止执行。

eg:
在这里插入图片描述

6. 线程的生命周期(从生到死)

  • 新建状态:new Thread()
  • 就绪状态:start()
  • 阻塞状态:sleep()、wait()、io
    • 解除阻塞状态后重新处于就绪状态
  • 运行状态:run() 执行方法(自动执行)
  • 消亡:执行完run()

7. 线程的优先级

  • 不同优先级:高优先级先执行,低优先级后执行 (高优先级不一定最先执行,只是拥有优先权
  • 同一优先级:先到先服务
  • 线程优先级的规范
    • 系统默认为中等优先级(5)
    • 人为设置线程的优先级:
      在这里插入图片描述

8. 线程同步(synchronized)

  • 多个线程操作一个共享变量时,可能会导致共享变量的不完整(不安全)。
  • 为了保证共享变量的安全,采用同步机制保证共享变量的安全性。
  • 同步机制:当一个线程改变共享变量值的时候,其他线程不能使用共享变量,当线程计算完成,返回变量值之后,其他线程才可以使用共享变量。

使用synchronized关键字实现线程同步的两种方法:

  1. synchronized同步块
    Synchronized(锁对象){
    操纵共享变量的代码(共享变量相关的业务代码(运算))
    }
    锁对象只能有一个,否则可能出现死锁现象。
  2. synchronized修饰方法,用此关键字修饰的方法叫同步方法

在这里插入图片描述

  • 练习:
  1. 商场买衣服:商场挑衣服(异步),试衣间只有一个,试衣服是同步。
package cn.tedu.thread;
/**
 * 练习
 * synchronized关键字实现线程同步
 * @author Dell
 */
class Shopping {	
	public void test() {
		System.out.println(Thread.currentThread().getName()+"挑选衣服");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		synchronized (this) {
			System.out.println(Thread.currentThread().getName()+"试衣服......");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}
public class Demo12 {
	public static void main(String[] args) {
		Shopping s = new Shopping();
		Thread t = new Thread(()->{
			s.test();
		}) ;
		Thread t2 = new Thread(()->{
			s.test();
		}) ;
		t.start();
		t2.start();		
	}
}
  1. 一个账号,两个人存钱
package cn.tedu.thread;
/**
 * 模拟银行存钱
 * @author Dell
 *
 */

class Bank{
	private int money=100;
	public synchronized void save(int money) {
		for (int i = 1; i <=3; i++) {
			this.money += money;
			System.out.println(Thread.currentThread().getName()+"余额:"+this.money);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}
public class Demo13 {
	public static void main(String[] args) {
		Bank b1 = new Bank();
		
		Thread t1 = new Thread(() ->{
			b1.save(1000);
		});
		Thread t2 = new Thread(() ->{
			b1.save(1000);
		});
		t1.start();
		t2.start();
	}
}

9. 同步线程之间的通信

  • wait():使当前线程处于等待状态,释放锁
  • notify():唤醒wait的线程
  • notifyAll():唤醒所有wait的线程

使用时要用锁对象调用这三个方法,否则会报异常
在这里插入图片描述

sleep和wait方法的区别

sleepwait
1.是线程类的方法是Object类的方法
2.没有释放锁释放锁
3.超时或者调用interrupt()方法体必须使用notify,notifyAll唤醒
4.可以在任何地方使用wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用
5.静态方法实例方法

eg: 使用两个线程交替打印10。
在这里插入图片描述
练习:生产者生产产品和消费者取走产品
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

10. 线程的死锁

eg:

package cn.tedu.thread;
/**
 * 线程死锁
 * @author Dell
 */
public class Demo {
	public static void main(String[] args) {
		StringBuffer str1 = new StringBuffer();
		StringBuffer str2 = new StringBuffer();
		synchronized (str1) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			synchronized (str2) {
				System.out.println(str1);
				System.out.println(str2);
			}
		}
		synchronized (str2) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			synchronized (str1) {
				System.out.println(str1);
				System.out.println(str2);
			}
		}
	}
}

11. 线程池

  • 为什么使用线程池?
    频繁的创建线程,需要耗费时间和内存

  • 使用线程池的好处:
    管理线程、使线程重用

  • 创建线程池的四种方法:
    (1) newCachedThreadPool
    (2) newFixedThreadPool可以指定线程池中的线程数。

    • shutdown()不关闭正在执行的线程,要等到全部线程执行完
    • shutdownNow()不管线程是否执行完成,立即停止执行

    (3) newScheduleThreadPool 定时周期性执行功能
    (new Runnable(),1,5,TimeUnit,SECONDS) 延迟1秒启动,间隔5秒启动下一个
    (4) newSingleThreadExecutor 单个线程

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值