多线程的学习及个人理解

一.相关概念的理解

程序:一个固定的运行逻辑和数据等的集合,一般储存在硬盘中.
进程:一个正在运行的程序就是一个进程,进程是一个动态的概念
线程:多个线程组成一个程序,每个线程的都可以独立完成其中一个任务,而且各个任务之间没有依赖关系,都可以单独执行.
并行和并发:并行是指多个进程或线程同时运行,计算机cpu不能实现,并发是指cpu在多个线程之间高速切换,可以实现,并发示意图如下:

在这里插入图片描述

二.创建线程的两种常用方式

方式一:
创建一个类T,并继承Thread类,在T类中重写run()方法,
创建T对象t,t.Start();即可启动线程

public class Work1_2 {

	public static void main(String[] args) {
		//创建Window2类对象
		Window2 t1 = new Window2();
		//启动线程
		t1.setName("赵新");
		//创建Window2类对象
		Window2 t2 = new Window2();
		//启动线程
		t2.setName("詹爱芳");
		//创建Window2类对象
		Window2 t3 = new Window2();
		
		t3.setName("程文涛");
		
		t1.start(); //启动线程
		t2.start(); //启动线程
		t3.start(); //启动线程
	}

}
//创建一个类Window2,并继承Thread类,
class Window2 extends Thread {
	public static int tkts = 100;
	//重写run()方法
	@Override
	public void run() {
		while (true) {
			synchronized (Window2.class) {
				if (tkts <= 0) {
					System.out.println("已卖光,明天再来吧 ");
					break;
				}
				System.out.println(getName() + "卖出了第" + tkts-- + "已经卖出去了,还剩" + tkts);
			}
		}
	}
}
方式二:
创建一个类T,实现接口Runnable,在T类中重写run方法.
T t = new T();
Thread th = new Thread(t);
package com.youjiuye.work;

public class Work1 {

	public static void main(String[] args) {
		//
		Window w1 = new Window();
		Thread t1 = new Thread(w1);
		//
		Window w2 = new Window();
		Thread t2 = new Thread(w2);
		//
		Window w3 = new Window();
		Thread t3 = new Thread(w3);
		
		t1.start(); //启动线程
		t2.start(); //启动线程
		t3.start(); //启动线程
		
	}

}
//创建一个类Window 实现接口runnble
class Window implements Runnable {
	public static int tkt = 1000;
	//重写run方法
	@Override
	public void run() {

		synchronized (Window.class) {

			while (true) {
				if (tkt <= 0) {
					System.out.println("诶嘿,卖光了.");
					break;
				}
				System.out.println("第" + tkt-- + "张票已卖出,还剩" + tkt);
				
			}
		}
	}

}

三.使用匿名内部类创建线程对象


public class 匿名内部类 {

	public static void main(String[] args) {
		
		//用匿名内部类创建继承方式的线程对象
		Thread t1 = new Thread() {
			@Override
			public void run() {
				//这里写线程要执行的代码
			}
		};
		
		//用匿名内部类创建实现接口方式的任务对象
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				//这里写线程要执行的代码
			}
		});
		
		t1.start();
		t2.start();
		
	}

}

四.线程的常用方法

获取线程名字:
	getName();
		1.可以使用线程对象.这个方法.  也可以在线程类中直接使用getName();.
		在这个线程类被调用时,获取的就是调用这个线程类的对象的线程的线程名.
		2.实现了Runnable接口的类, run方法中不能使用getName().
		3.没有给线程起名字,那么线程有默认的名字,名字就是Thread-x, x序号,从0开始.

设置线程名程:
	1.setName(String name);  使用线程对象调用setName(String name);修改线程名,
	可以在线程开启钱修改也可以在线程开启后修改.
	2.Tread的构造方法:
		new Tread(String name);
		new Tread(Runnable r, String name);

获取当前线程对象
	Thread.currentThread();获取这句话所在线程的线程对象.

线程中的休眠方法
	当某段代码在运行到某处需要休眠时,就在某处加上Thread.sleep(long 毫秒);
	Thread.sleep();方法有一个编译时异常,使用时要处理:
	1.普通方法中既可以生命也可以捕获.
	2.在继承了Tread的方法中只能捕获
	3.在实现了runnable 的方法中也只能捕获

五.守护线程

守护线程就是用于守护其他线程可以正常执行的线程,为其他核心线程准备良好的运行环境.
如果非守护线程全部死亡(挂起), 那么守护线程就没有存在意义的,一段时间之后,
守护线程也会死亡(挂起);
setDaemon(boolean flag): 线程对象调用这个方法,参数给true,此时这个线程就是守护线程;
参数给false,它就不是守护线程.
boolean isDaemon(): 测试一个线程对象是否为守护线程,是就返回true, 否则返回false;

六.锁

某段代码在某个线程中执行, 但是这段代码还没有完整的执行结束, CPU就把资源切换其他线程中,执行别的任务了,此时就会导致一些错误的数据产生.所以我们使用锁对象(synchronized)来保证某段代码执行完后CPU在切换走到其他线程.

同步代码块

	synchronized ( 锁对象 ){
	需要保证执行不对打断的代码
	}

同步方法

如果一个方法中所有的代码都需要用同步代码块包裹,那么我们就可以把这个方法简写成同步方法.

权限修饰符 [静态修饰符] synchronized 返回值类型 方法名(参数列表){

}

练习:卖火车票案例

一共有100张票, 三个窗口(安锋, 江硕, 淑芬)卖票,要求不能卖出重复的票,也不能少卖,也不能多卖; 提示: 三个窗口就相当于三个线程,操作同一个公共的资源.

在这里插入代码片

七.线程的生命周期

线程状态:

新建态:刚创建对象的时候
就绪态:线程准备好了所有运行的资源,只差cpu来临
运行态:cpu正在执行.
阻塞态:线程主动休息或者缺少一些运行的资源,即使cpu来临也无法运行
死亡态:运行完成/出现异常/调用方法结束

在这里插入图片描述
Java中通过代码的方式获取线程的状态描述

getState()方法,获取线程各种状态,返回值是Thread.State ,是Thread的内部类.

NEW 新建态,没有开启线程
RUNNABLE  运行态
BLOCKED 阻塞态(锁,I/O)
WAITING 阻塞态(wait方法)
TIME_WAITING 阻塞态(sleep方法)
TERMINATED 死亡态
//代码演示
package com.youjiuye.prictice;
public class 线程状态获取_01 {
	private  static final String A = "A";
	private  static final String B = "B";
	public static void main(String[] args) {		
		test_获取线程TIMED_WAINTING状态();
	}
	private static void test_获取线程的TERMINATED状态() {
		Thread t = new Thread() {
			public void run() {
				System.out.println("祥哥好帅");
			}; 
		};
		t.start();
		while(true) {
			System.out.println(t.getState());// TERMINATED
		}
	}
	private static void test_获取线程BLOCKED状态() {
		Thread t1 = new Thread() {			
			@Override
			public void run() {
				synchronized (A) {
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					synchronized (B) {
						System.out.println("任务1....");
					}
				}
			}
		};
		Thread t2 = new Thread() {			
			@Override
			public void run() {
				synchronized (B) {
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					synchronized (A) {
						System.out.println("任务2....");
					}
				}
			}
		};
		t1.start();
		t2.start();		
		while(true) {
			System.out.println(t1.getState());// BLOCKED
		}
	}
	private static void test_获取线程WAITING状态() {
		Object lock = new Object();
		Thread t = new Thread() {
			public void run() {		
				synchronized (lock) {
					try {
						lock.wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				
			};
			
		};
		t.start();
		while(true) {
			System.out.println(t.getState());// WAITING
		}
	}
	private static void test_获取线程TIMED_WAINTING状态() {
		Thread t = new Thread() {
			@Override
			public void run() {
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}				
			}
		};
		t.start();
		// 在主线程获取t线程的状态
		while(true) {
			System.out.println(t.getState()); // TIMED_WAINTING
		}
	}
	private static void test_获取线程NEW和RUNNABLE状态() {
		Thread t = new Thread() {
			@Override
			public void run() {
				for (int i = 0; i < 100; i++) {
					// 获取线程状态
					System.out.println(this.getState());
				}
			}
		};
		System.out.println(t.getState()); // NEW
		t.start(); // RUNNABLE
	}
}

八.线程池

在没有任务的时候, 会先把一些线程对象提前准备好, 存储到一个 线程池 中,当一旦有了任务,
不需要创建线程了,直接从线程池中获取一条线程,来执行这个任务. 当任务正常执行结束, 不会把线程对象销毁,
而是重新放回线程池;等待新的任务. 当任务破坏力比较大, 导致这条线程对象被挂起, 此时任务不会终止,
而是继续去线程池中获取一条线程,继续执行之后, 直到把任务执行完为止.再把线程对象放回线程池; 使用线程池的缺点:
不管有没有任务,都需要维护一部分线程对象,这部分就会一直占着系统资源. 但是好处远远大于缺点. 能使用线城池就使用;

使用步骤:

①创建线程池对象 --> ②创建任务对象:Runnable的实现类 --> ③把任务提交到线程池中 --> ④关闭线程池
package com.ujiuye.threadPool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class 线程池的使用_01 {
	public static void main(String[] args) {
		// 1 获取一个拥有三条线程的线程池对象
		ExecutorService pool = Executors.newFixedThreadPool(3);
		
		// 2创建四个任务对象
		Runnable r1 = new Runnable() {
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName() + "---任务1");
			}
		};
		Runnable r2 = new Runnable() {
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName() + "---任务2");
			}
		};
		Runnable r3 = new Runnable() {
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName() + "---任务3");
			}
		};
		
		Runnable r4 = new Runnable() {
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName() + "---任务4");
			}
		};
		// 3.提交任务
		pool.submit(r1);
		pool.submit(r2);
		pool.submit(r3);
		pool.submit(r4);		
		// 4关闭线程池
		// pool.shutdown();
		pool.shutdownNow();
		
		//pool.submit(r1);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值