线程_java_大全(学习笔记ing)

一.简介

1.进程

程序:为了完成特定任务,用某种语言编写的一组指令集合,就是一段静态的代码。

进程: 在操作系统中独立运行的程序,每一个应用程序就对应一个进程。
​            动态的过程:产生,存在,消亡。  运行中QQ,运行中酷狗
​            一个程序可以同时开启多个进程。 任务管理器qq就有多个对应的进程
​            java代码打开一个进程/运行一个软件

2.线程

线程: 是进程内部的执行单元,用来执行应用程序中的 某一个功能。

多线程:每一个进程可以同时开启多个任务,每个任务就是一个线程。迅雷: 同时下载多个任务

特点:

* 一个进程可以包含多个线程
* 一个进程的多个线程可以共享该进程中的所有资源

 3.CPU时间片

 对于单核UPU,在某一个时间点,只能处理一个程序。

CPU分配给各个程序的时间,称为时间片。进程可以运行的时间(时间很短)

* 从表面上各个程序是同时运行的,实际上CPU在同一个时间点只能执行一个程序。
* 只是CPU在很短的时间内,在不同的程序间进行切换,轮流执行每个程序,执行速度很快,感觉是在同时运行。

二.创建线程(Thread)

主线程: JVM启动时会创建的一个线程,用来执行main方法

* 实际,java程序运行时至少有两个线程存在,main线程  垃圾回收线程gc

* 想要更多的线程,需要自己创建线程

创建线程的方法:1.继承Thread类  2.实现Runnable接口

方法一:继承Thread类

1. 定义一个类,继承Thred类,重写run方法
2. 创建自己的线程对象
3. 开启线程--  start()方法 ------ 不能直接调用run()

public class Thread_lots extends Thread{
	@Override
	public void run() {
		for (int i = 0; i < 5; i++) {
			System.out.println(Thread.currentThread().getName()+"   "+i);
		}
		super.run();
	}
	
	
	Thread_lots(){
	}
	
//	通过构造方法给他名字 需要写含一个参数的构造函数 则无参的构造函数也要写
	Thread_lots(String name){
//		this.setName(name); 父类里面有这个语句
		super(name);
	}
}
	public static void main(String[] args) {
		//主线程的语句 获取当前线程的名字	
		for (int i = 0; i < 5; i++) {
			System.out.println(Thread.currentThread().getName()+"   "+i);
		}
		
		Thread_lots th1 = new Thread_lots();
		th1.setName("线程1");
//		th1.run();没有开启任何线程 任然输出为main0
		th1.start();

//		通过构造方法直接给名字
		Thread_lots th2=new Thread_lots("线程2");
		th2.start();
	}

注意:run方法只是调用了一个普通方法用来处理线程对应的业务逻辑,并没有启动另一个线程,程序还是会按照顺序执行相应的代码,必须使用start方法启动一个线程。

方法二:实现Runnable接口

1. 定义一个类,实现接口,重写run方法
2. 创建线程类的实例
3. 创建一个Thread类的对象,将上一步中的对象传入
4. 调用start()方法

class Mythread implements Runnable{
	@Override
	public void run() {
		for (int i = 0; i < 5; i++) {
			System.out.println(Thread.currentThread().getName()+i);
		}
	}
}
public static void main(String[] args) {
		for (int i = 0; i < 5; i++) {
			System.out.println(Thread.currentThread().getName()+i);
		}
		
		Mythread m = new Mythread();
		Thread t = new Thread(m);
		t.setName("方法二 接口实现建立线程");
		t.start();
	}

 两种方法比较

继承Thread类:
* 单一继承的特点,不能再继承其他类
* 如果没static修饰变量,多个线程时没有共享一个变量

实现Runnable接口:
* 可以继续继承其他类,避免了单继承的局限性
* 适合多个线程去处理同一个资源的情况

//接口50苹果 没按照顺序输出 执行吃苹果操作 还没输出打印结果时 另一个线程抢占cpu 只new一次
class Mythread2 implements Runnable{
	int num=50;
	
	@Override
	public void run() {
		while(num>0){
			System.out.println(Thread.currentThread().getName()+"吃了1个苹果 还剩"+(--num));
		}
	}
}
//继承 150个苹果 每次new变量分配50个 
class Thread2 extends Thread{
	static int num=50;//注意这边设置为静态的才是公用的 否则new三次 则分配150个苹果了
	
	@Override
	public void run() {
		while(num>0){  //继承了Thread   则不需要Thread.currentThread()
			System.out.println(this.getName()+"吃了1个苹果 还剩"+(--num));
		}
	}

	public Thread2(String name) {
		super(name);
	}
	public Thread2() {
	}
}
public static void main(String[] args) {
		Mythread2 m2 = new Mythread2();
		Thread t1 = new Thread(m2, "COCO");//有这种构造方法
		Thread t2 = new Thread(m2, "JACK");
		Thread t3 = new Thread(m2, "NANCY");
		t1.start();
		t2.start();
		t3.start();
		
		
		Thread2 t4 = new Thread2("SOON");
		Thread2 t5 = new Thread2("LATER");
		Thread t6 = new Thread2("MOMENT");
		t4.start();
		t5.start();
		t6.start();
	}

三.生命周期

静态方法直接 类名.方法名 Thread.sleep() Thread.yeild()

 方法名作用说明 
start启动线程,进入就绪状态
sleep休眠线程,线程由执行状态就入阻塞状态 静态方法
join 暂停执行的线程,等待另一个线程执行完成后再执行
yield暂停执行线程,线程从执行状态进入就绪状态,只是一瞬间的事情,其他的线程不一定能够执行 静态方法
interrupt中断线程的休眠或是等待状态 

1.sleep

public static void main(String[] args) {
		Mythread m = new Mythread();
		m.start();
	}

class Mythread extends Thread{
	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println(i);
			try {
				Thread.sleep(1000);                        //静态方法
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

2.join

也有可能别的线程先抢占到cpu 不是绝对的;
哪个线程调用join代表哪个线程插队先执行;
如果main()抢到了 则到5就让一下  没有就算;

public static void main(String[] args) {
		
		Mythread1 m2 = new Mythread1();
		m2.start();
		
		for (int i = 0; i < 10; i++) {
			if(i==5){
				try {
					m2.join();
				} catch (InterruptedException e) {
					System.out.println("暂停执行的线程,等待另一个线程执行完成后再执行");
				}
			}
			System.out.println(Thread.currentThread().getName()+i);
		}
	}


class Mythread1 extends Thread{
	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println(this.getName()+i);
		}
	}
}

3.yield

也有可能别的线程先抢占到cpu 不是绝对的,暂停执行线程,线程从执行状态进入就绪状态,只是一瞬间的事情,其他的线程不一定能够执行。

public static void main(String[] args) {
		Mythread1 m3 = new Mythread1();
		m3.start();
		
		for (int i = 0; i < 10; i++) {
			if(i==5){
				Thread.yield();                //静态方法 暂停当前线程 让出cpu
			}
			System.out.println(Thread.currentThread().getName()+i);
		}
	}


class Mythread3 extends Thread{
	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println(this.getName()+i);
		}
	}
}

4.interrupt

表示中断这个线程,中断线程的休眠或是等待状态,若m4先抢占到cpu 就睡觉 main开始执行完还没到三秒,将m4喊醒,报异常try_catch。

public static void main(String[] args) {

			Mythread4 m4 = new Mythread4();
			m4.start();
			m4.setPriority(Thread.MAX_PRIORITY);//设置优先级 抢占到cpu的几率更大 即常量
			
			
			for (int i = 0; i < 10; i++) {
				System.out.println("main"+i);
			}
			m4.interrupt();
		}

class Mythread4 extends Thread{//休眠过程被中断会抛异常
		@Override
		public void run() {
			for (int i = 0; i < 10; i++) {
				System.out.println(this.getName()+i);
				try {
					Thread.sleep(3000);                        //静态方法
				} catch (InterruptedException e) {
					System.out.println("线程被中断啦");
				}
			}
		}
	}

四.线程安全问题

1.同步和异步

(1)同步线程:是指有多个线程在同时执行时,一个线程要等待上一个线程执行完成后,才开始执行当前线程   ----  排队   ---  效率低  -- 比较安全。
(2)异步线程:一个线程执行时,另一个线程不用等待,可以直接抢占cpu资源   ---  效率高 -- 不安全。

2.线程安全问题

多个线程在同时访问共享的数据时可能出现问题---  称为线程的安全问题。

(1)问题

当线程同时访问数据,CPU切换,导致一个线程还没有完成,执行了一部分代码;此时另一个线程加入进来,导致共享数据发生异常。

(2)解决: 线程的同步机制  synchronized+ 锁   

(a)同步方法 :  被synchronized修饰的方法 

(b)同步代码块:被synchronized包围的代码块

(3)语法:  synchronized(对象锁){}

锁-- 对象锁,每一个对象都自带一个锁(标识),且不同对象锁是不一样的

(4)执行过程

(a)当线程执行同步代码块或者同步方法时,必须要有特定对象的锁才行
(b)一旦对象的锁被获取了,则该对象不再拥有锁,直到当前线程执行完同步方法或者同步代码块时,才会释放对象的锁
(c)如果无法获得特定对象的锁,线程会进入该对象的锁池中等待,直到锁被释放,此时需要该锁的线程会进行竞争

(5)线程同步的有缺点

(a)优点:解决了线程的安全问题,是线程在某一时间只能一个线程方法
(b)缺点:由于需要判断锁,消耗资源,效率低

public class 线程安全 {

	public static void main(String[] args) {
		TicketThread m = new TicketThread();
		Thread t1 = new Thread(m,"线程1");
		Thread t2 = new Thread(m,"线程2");
		Thread t3 = new Thread(m,"线程3");
		t1.start();t2.start();t3.start();
	}
}


class TicketThread implements Runnable{
	int num=100;
	static int num2=100;
	@Override
	public void run() {
//产生错误:num=1 1进入if 开始睡眠  此时num=1 2抢占cpu进入if语句输出 此时num=0  而后1睡醒了再输出num=-1
//synchronized不能放在while外面 否则变成单线程 1进入输出直至while结束为止  3个窗口都要进行售票
			while(true){
			synchronized (this) { //从一开始进来就要卡人  同步代码块 
					//this当前对象的锁 标识里面代码是否被执行完 一旦执行完 别的窗口开始抢占cpu
					if(num>0){
						try {
							Thread.sleep(10);
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						System.out.println(Thread.currentThread().getName()+"  "+num--);
					}
					else {
						break;
					}
				}
//				sellTiket();
//				if(num<=0)break;
			}
	}
	
//		同步方法默认使用this锁
	public synchronized void sellTiket(){
		if(num>0){
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"  "+num--);
		}
	}
	
	
//	静态的同步方法使用的是 当前类的Class对象锁 当前正在运行的字节码文件 即class TicketThread 
	public static synchronized void sellTiket2(){
		if(num2>0){
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"  "+num2--);
		}
	}
}

五.线程的通信

1.锁池和等待池

 锁池:

(1)当线程执行synchronized块时,无法获得特定对象的锁,会进入该对象的锁池中等待。
(2)锁被释放,归还给对象时,锁池中多个线程竞争获取该对象的锁

等待池:

(1)当线程获得锁后,可以调用wait()放弃锁,线程就入该对象的等待池
(2)当其他线程调用该对象的notify和notifyAll方法时。等待池中的线程会被唤醒,进入该对象的锁池
(3)当线程获取对象锁后,将从上次调用wait()方法的位置开始继续运行

2.方法

方法作用说明
wait放弃对象锁,进入等待池可以设置等待时间,超时后自动唤醒
notify随机唤醒等待池中的一个线程,线程进入锁池
notifyAll唤醒等待池中的所有线程

(1)这个三个方法只能在synchronized块中使用,只有获得了锁的线程才能调用
(2)等待和唤醒必须使用的是同一个对象

eg:等待和唤醒必须使用的是同一个对象,否则无法唤醒,如下示例使用同一个obj;

public static void main(String[] args) {
		Object obj = new Object();
		Wait1 wait = new Wait1(obj);
		Notify1 notify1 = new Notify1(obj);
		wait.start();
		notify1.start();
	}


class Wait1 extends Thread {
	private Object o;
	public Wait1(Object obj) {
		this.o=obj;
	}

	@Override
	public void run() {
		synchronized (o) {
			System.out.println("111");
			try {
				o.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("222");
		}
	}
}


class Notify1 extends Thread{
	private Object o;
	public Notify1(Object obj) {
		this.o=obj;
	}

	@Override
	public void run() {
		synchronized (o) {
			o.notifyAll();
			System.out.println("开启唤醒服务啦");
		}
	}
}

输出:
111
开启唤醒服务啦
222

六.生产者和消费者的问题

1.简介

多线程之间的协作---生产线程和 消费线程

生产:

(1)生产的商品  放入 缓冲区
(2)缓冲区到达最大值,放弃生产

消费:

(1)从缓冲区获取商品
(2)当缓冲区为空的时候,不再取出

2.思路

一个仓库类 用来缓存信息;一个消费者线程 ,一个生产者线程,由于需要将他们放在一个等待池中,所以声明 private Warehouse w;
但此类没有new出来,不能调用其方法,会报空指针异常;
需要在test类中创建 传参 再构造函数 实现功能共享一个等待池;
睡眠长度不同则实现的效率不同;

public static void main(String[] args) {
		Warehouse w= new Warehouse();
		Produce p1= new Produce(w);
		Produce p2 = new Produce(w);
		Produce p3 = new Produce(w);
		Consumer c = new Consumer(w);
		p1.start();
		p2.start();
		p3.start();
		c.start();
	}
//仓库的属性
public class Warehouse {

	int num=0;
	public final static int MAX_VALUE=10;
	public void in() {
		num++;
	}
	public void out() {
		num--;
	}
	public int getNum() {
		return num;
	}
	public boolean isFull() {
		return num==MAX_VALUE;
	}
	public boolean isEmpty() {
		return num==0;
	}
}



public class Produce extends Thread{
	private Warehouse w;
	
	public Produce(Warehouse w2) {
		this.w=w2;
	}

	public void run() {
		while(true) {
			synchronized (w) {
				if(w.isFull()) {//商品满了 进入等待池
					try {
						w.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				else {
					w.in();
					System.out.println(this.getName()+"生产一样物品 还剩"+w.getNum());
					w.notifyAll();//若之前消费线程进入等待 则需要唤醒
				}
			}
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}



public class Consumer extends Thread{

	private Warehouse w;
	
	public Consumer(Warehouse w2) {
		this.w=w2;
	}
	
	public void run() {
		while(true) {
			synchronized (w) {
				if(w.isEmpty()) {
					try {
						w.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				else {
					w.out();
					System.out.println(this.getName()+"消费一样物品 还剩"+w.getNum());
					w.notifyAll();//若之前生产线程进入等待 则需要唤醒
				}
			}
			try {
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

七.线程的单例

1.简介

为每一个线程提供一个实例

(1)同一个线程获取的是一个实例
(2)不同线程获取的是不同实例

2.解决线程单列

java中提供了ThreadLocal,直接解决线程单列的解决方案

(1)用于管理对象
(2)同一个线程放入的对象,将获取到同一个对象实例(地址一样)

自己通过代码实现:

//泛型类  可能会传不同的类进来 
public class MyThread<T> {
	HashMap<Thread,T> map=new HashMap();

	public void set(T t) {//将当前对象作为key
		map.put(Thread.currentThread(),t);
	}
	
	public T get() {//当前线程对象
		return map.get(Thread.currentThread());
	}
	public void getAddr() {
		System.out.println(Thread.currentThread());
	}
}


public static void main(String[] args) {
		MyThread<User> myThread = new MyThread<User>();
//		ThreadLocal<User> local=new ThreadLocal<User>();
		User u1 = new User("nancy");
		User u2 = new User("Jack");
		//若key值相同 则会覆盖
		myThread.set(u1);
		myThread.set(u2);
		System.out.println(myThread.get().getName());
		System.out.println(myThread.get().getName());
		myThread.getAddr();
		myThread.getAddr();
		
//		local.set(u1);
//		local.set(u2);
//		System.out.println(local.get());
		
		new Thread(new Runnable() {
			public void run() {
				User u3 = new User("Jack");
				myThread.set(u3);
				myThread.getAddr();
				
//				local.set(u3); 或者 local.set(new User("jack"));
//				System.out.println(local.get());
			}
		}).start();
	}
基础
class MyThread implements Runnable(){public void run(){}}
MyThread myThread=new MyThread();
Thread thread=new Thread(myThread,"me");
thread.start();

合并
class MyThread implements Runnable(){public void run(){}}
Thread thread=new Thread(new MyThread(),"me");
thread.start();

匿名内部类
new Thread(new Runnable(){public void run(){}}).start();

这是一边看视频一边学习的笔记-借用了部分老师的笔记内容-如有侵权,通知删除!

新手小白学习之路~~~

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值