学习线程的主要知识点概要如下:
进程和线程的区别:
* 1.进程:操作系统而言,进程就是程序,多进程就是多个程序同时执行
* 一个进程包含一个~多个线程,每一个进程都有自己的代码和运行空间,进程之间切换开销较大,进程是资源分配的最小单位
* 2.线程:程序中的顺序流,多线程:一个程序中的多个顺序流同时执行
* 一系列线程共享代码和数据空间,每个线程都有自己的程序计数器,线程之间切换开销较小,线程是cpu调度的最小单位
线程和进程的状态: 新生 就绪 运行 阻塞 终止
CPU只会给就绪状态和运行状态的线程时间片,不会给阻塞状态的
新生状态:线程创建完就是新生状态
就绪状态:当调用start()方法就是进入就绪状态,等待CPU给时间片,不给时间片不执行,时间片用完了也进入就绪状态
运行状态:当CPU给之间片真正执行的时间,当时间片用完了,继续回到就绪状态
阻塞状态:在阻塞状态时,CPU必定不会给时间片,因为被阻塞了,必须等待其进入到就绪状态才行
终止状态:线程被终止了
如何进入到就绪状态
* 1.start()
* 2.阻塞解除
* 3.线程切换,被切换的线程进入到就绪状态
* 4.yield() 礼让线程
如何进入到阻塞状态
1.sleep(毫秒数) 方法 当时间结束了就自动回到就绪状态
2.join()方法 当被插队的结束了才能回到就绪状态
3.wait()方法 一直等待,直到被notify()方法唤醒才能进入到就绪状态
thread.join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。比如在线程B中调用了线程t的join()方法,直到线程t执行完毕后,才会继续执行线程B。
- t.join(); //在B线程中使调用线程 t 在此之前执行完毕。
- t.join(1000); //在B线程中,等待 t 线程,等待时间是1000毫秒
注意:如果线程被生成了,但还未被起动,调用它的 join() 方法是没有作用的,将直接继续向下执行
如何让一个线程进入到终止状态
1.正常执行完毕
2.destroy() |stop() 已过时
3.通过标识手动判断
sleep() 线程休眠
* 1.模拟网络延迟
* 2.放大问题的可能性
* 抱着资源睡觉,同步的对象资源,让出cpu的资源
yield() 有这个方法的线程是高风亮节,礼让线程
当A,B线程同时开启时候,当A中有yield()方法,在执行A中的方法时候,遇到yield方法,这个线程就回到就绪状态,再次调用的时候就会接着上次没有执行的代码向下执行!!!!!!!!!!!!!!!!!!
如何创建一个线程
* 1) 继承Thread,重写run()方法
* 1)在run()方法中定义线程体
* 2)开启: 使用start()方法开启线程
* 2) 实现Runnable接口,重写run()方法 推荐
* 3) 实现Callable接口,重写call()方法
第一种:单继承Thread类,重写run方法:
public class ThreadDemo01 extends Thread{
//多线程的线程体
@Override
public void run() {
for(int i=1;i<=20;i++){
System.out.println("一遍吃饭...");
}
}
public static void main(String[] args) {
//开启多线程 创建线程
ThreadDemo01 th=new ThreadDemo01();
th.start(); //开启线程
//th.run(); 注意:这是方法的调用,不是多线程的开启
for(int i=1;i<=20;i++){
System.out.println("一遍说话...");
}
//th.start(); 放在这里的话:主线程中代码执行完毕,只剩下一个th线程,看不到线程切换的效果
}
}
第二种,实现runnable接口,避免单继承的局限性,推荐使用
public class ThreadDemo02 implements Runnable{
//定义线程体的方法,当被调用的时候,会逐行执行里面的代码
@Override
public void run() {
for(int i=1;i<=10;i++){
System.out.println("一遍敲代码...");
}
}
public static void main(String[] args) {
ThreadDemo02 th=new ThreadDemo02();//创建线程
//开启线程
Thread t=new Thread(th); //因为开启线程的方法在Thread类中,Thread做为代理类出现,静态代理
t.start();
for(int i=1;i<=10;i++){
System.out.println("一遍陪女朋友...");
}
}
}
练习:
* 模拟12306
* 需求: 100张票,3个人买完
* 资源共享: 100张票
下面的代码会造成多人抢到一张票的问题,待优化
在创建线程的时候Thread th1=new Thread(web,"王冬冬");th1作为代理出现,web指的是代理哪一个线程,第二个指的是线程名
public class Web12306_03 implements Runnable{
//成员 资源
int tikets=100;
@Override
public void run() {
//循环买票
while(true){
if(tikets<=0){
break;
}
try {
Thread.sleep(1); //线程睡眠100ms
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在购买第"+tikets--);
}
}
public static void main(String[] args) {
Web12306_03 web=new Web12306_03();
//开启三个线程
Thread th1=new Thread(web,"王冬冬");
Thread th2=new Thread(web,"王宇晨");
Thread th3=new Thread(web,"郭友波");
th1.start();
th2.start();
th3.start();
}
}
线程的优先级
* 线程的优先级: 提高优先执行的可能性,但是不一定就会先执行
* void setPriority(int newPriority) 更改线程的优先级。
* 1~10 1最小 10最大
* Thread.NORM_PRIORITY 5
* Thread.MAX_PRIORITY 10
* Thread.MIN_PRIORITY 1
可以直接输入数字 th.getPriority() //5/10/1/... th.getState()//获得状态 new / runnable time_waiting
synchronized 同步锁
锁方法简单,但是效率低,因为锁的是整个,要效率可以锁代码块
* 线程安全:
* 多个线程同时操作同一个资源的时候,才可能会出现线程安全问题,例如上述的抢票
*
* 通过同步synchronized关键字控制线程安全:
* 同步方法
* 静态方法
* 成员方法
* 同步块 synchronized(类的class对象|this|资源){代码} 锁住的必须是不可变的
* 类: 类名.class 一个类的Class对象 一个类只有一个Class对象 this和static不能同时使用
*
* 锁要锁着不变的东西,会变的锁不住,锁住资源
* 同步的范围 {} 中的内容太大,效率低,同步的范围太小,锁不住
* 单例模式:懒汉式
public static synchronized Single newInstance(){
System.out.println("啦啦啦啦啦啦啦啦啦啦啦啦");
System.out.println("啦啦啦啦啦啦啦啦啦啦啦啦");
System.out.println("啦啦啦啦啦啦啦啦啦啦啦啦");
System.out.println("啦啦啦啦啦啦啦啦啦啦啦啦");
System.out.println("啦啦啦啦啦啦啦啦啦啦啦啦");
if(single==null){
single=new Single();
}
return single;
}
public static Single newInstance(){
System.out.println("啦啦啦啦啦啦啦啦啦啦啦啦");
System.out.println("啦啦啦啦啦啦啦啦啦啦啦啦");
System.out.println("啦啦啦啦啦啦啦啦啦啦啦啦");
System.out.println("啦啦啦啦啦啦啦啦啦啦啦啦");
System.out.println("啦啦啦啦啦啦啦啦啦啦啦啦");
synchronized (Single.class) { //控制多线程排队执行
if(single==null){
System.out.println("-----------------------");
single=new Single();
}
} //{}中的代码就是排队执行的代码
return single;
}
synchronized 可以锁类,可以锁this,可以锁资源
当锁类的时候,相当于把这个类的所有对象整个锁住了
同步this,同步当前对象,锁住这个对象,这个对象的所有资源都被锁住了
如果只想要锁住其中某个资源,可以只锁这个资源,一般是成员变量
线程通信
* wait() 和 notify()通过操作同一份资源控制线程的通信,方法必须使用在同步的环境下
* wait()进入到对象(资源)的等待池中排队等待,等待被唤醒,会让出cpu的资源,同时也会释放对象的锁
* sleep() 让出cpu的资源,不会释放对象的锁
* notify() 唤醒正在等待的线程,只是具备了可执行的能力,就绪状态,如果想要执行,除了获取cpu的资源以外,还要拿到对象的锁
public class Demo01 extends Object{
public static void main(String[] args) {
Street street=new Street();
new Thread(new Person(street)).start();
new Thread(new Car(street)).start();
}
}
class Street{
//信号灯
boolean flag=false;
//we 东西 false
public void we(){
if(flag==true){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("车走...");
flag=true;
this.notify();
}
}
//ns 南北 true
public void ns(){
if(flag==false){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("人走...");
flag=false;
this.notify();
}
}
}
//人
class Person implements Runnable{
Street street=null; //街道
public Person(Street street) {
super();
this.street = street;
}
@Override
public void run() {
synchronized (street) {
while(true){
street.ns();
}
}
}
}
class Car implements Runnable{
Street street=null; //街道
public Car(Street street) {
super();
this.street = street;
}
@Override
public synchronized void run() {
synchronized (street) {
while(true){
street.we();
}
}
}
}