Java线程基础

java线程

生命周期图示:
在这里插入图片描述

1).线程介绍及创建方式

进程:就是一个正在运行的程序,比如:typeroa、eclipse、浏览器等等

线程:线程是进程中的一个执行单元,例如:QQ音乐播放音乐和下载音乐

  • 一个程序至少有一个进程,一个进程至少有一个线程。
  • 线程的划分尺度小于进程,使得多进程程序的并发性高。
  • 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
  • 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
  • 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。

关系:在一个进程中可以包含多个线程

当需要在同一个时刻去做多件事情时,就需要开启多个线程

多线程的好处

  • 充分利用CPU的资源
  • 简化编程模型
  • ​ 带来良好的用户体验

在 Java 中实现多线程有两种手段,一种是继承 Thread 类,另一种就是实现 Runnable 接口

两者的区别:

一、继承Thread类
  编写简单,可直接操作线程
  适用于单继承
二、实现Runnable接口
  避免单继承局限性
  便于共享资源

//方式一:new完subThread之后,直接调用thread类的方法
//1、线程中的任务不需要手动调用,而是通过start方法在创建并开启线程后自动调用的
//2、多个线程之间的运行是互不影响的
//3、线程的运行是随机的(和资源争夺有关)
public class ThreadDemo {
	public static void main(String[] args) {
		SubThread st = new SubThread();
		//创建并开启了一个线程
		st.start();
		int a = 1/0;
		System.out.println("--Over--");
	}
    class SubThread extends Thread{
	/**
	 * 指定要执行的任务
	 */
        @Override
        public void run() {
		System.out.println("执行任务");
        }
    }
}
/**方式二:new完sub1Thread之后还要将这一新建对象放进Thread方法中。
    1.实现run()方法
    2.编写线程执行体;
    3.创建线程对象;
    4.调用start()方法启动线程。
*/
public class ThreadDemo {
	public static void main(String[] args) {
		Sub1Thread st = new Sub1Thread();
		//创建一个Thread对象创建并开启线程
		Thread t = new Thread(st);//Runnable target = new SubThread();
		t.start();
		//当主线程结束后,如果循环还在执行就说明循环时在另一个线程中
		int a = 1/0;
		System.out.println("--over--");
	}
}
/**
 * Runnable接口中提供了任务
 * 实现类只需要给到具体的任务即可
 */
class Sub1Thread implements Runnable{
	@Override
	public void run() {
		for(int i=0;i<200;i++) {
			System.out.println("Runnable 创建线程");
		}
	}
	
}

在Java中,每次程序运行至少启动2个线程:一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个JVM实际上就是在操作系统中启动了一个进程。

2).垃圾回收机制

虚拟机的内存是有限的,因此,对于在程序运行过程中创建的对象(占用的资源),虚拟机需要对其进行监控,当发现有对象不再有引用的时候就将其进行回收,该回收的动作是贯穿整个程序的,它是一个单独的线程,称该线程为垃圾回收机制。

@Override
protected void finalize() throws Throwable {
    System.out.println("垃圾回收器运行");
    super.finalize();
}

public static void main(String[] args) {
    new User();
    new User();
    new User();
    //通知 gc()
    System.gc();
    new User();
    new User();
    new User();
}

注意:

1、虽然手动通知了回收器需要回收垃圾对象,但是回收器并不是立马就执行的

2、垃圾回收器需要判断通知要回收的对象是否真的能回收

3).线程同步分析

关键词:synchronize 同步锁

语法:synchronize(锁){锁的内容}

要求:多个线程在操作时所用的锁是同一把锁

private int count = 200;
//使用Object对象作为唯一锁,前提是该对象只被创建一次供多个线程使用
Object object = new Object();

@Override
public void run() {
    while(true) {
        try {
            synchronized (object) {
                //假设A先执行,B进来,C进来
                if(count>0) {
                    //A睡眠 B睡眠 C睡眠
                    Thread.sleep(10);
                    //谁先醒是随机的(需要争夺CPU资源)
                    System.out.println(Thread.currentThread().getName()+"卖了一张票,剩余"+(--count)+"张");
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
@Override
public void run() {
    //每当线程进来时都会重新创建一个新的锁
    Object object = new Object();
    while(true) {
        try {
            synchronized (object) {
                //假设A先执行,B进来,C进来
                if(count>0) {
                    //A睡眠 B睡眠 C睡眠
                    Thread.sleep(10);
                    //谁先醒是随机的(需要争夺CPU资源)
                    System.out.println(Thread.currentThread().getName()+"卖了一张票,剩余"+(--count)+"张");
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
private int count = 200;
	
//Object object = new Object();

@Override
public void run() {
    while(true) {
        try {
            //使用class对象作为唯一锁
            synchronized (Tickets.class) {
                //假设A先执行,B进来,C进来
                if(count>0) {
                    //A睡眠 B睡眠 C睡眠
                    Thread.sleep(10);
                    //谁先醒是随机的(需要争夺CPU资源)
                    System.out.println(Thread.currentThread().getName()+"卖了一张票,剩余"+(--count)+"张");
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
/**
	 * 锁方法
	 * 每个方法都默认持有一个this
	 * 所以方法的默认锁是this
	 */
public synchronized void sale() {
    try {
        //假设A先执行,B进来,C进来
        if(count>0) {
            //A睡眠 B睡眠 C睡眠
            Thread.sleep(10);
            //谁先醒是随机的(需要争夺CPU资源)
            System.out.println(Thread.currentThread().getName()+"卖了一张票,剩余"+(--count)+"张");
        }
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

4).单例模式及同步锁

概念:单例模式就是保证对象在使用的过程是唯一的

思考:如何保证对象唯一?

考虑:对象是怎么来的----通过构造器来的

单例的实现步骤:

1、私有化构造器

2、提供一个功能的方法用来获取唯一的对象

3、将全局属性静态并私有化

//懒汉式
public class Single {

	private static Single single;
	
	private Single() {}
	//节约资源,对象只在使用的时候才会创建
	public static Single newInstance() {
		if(single == null) {
			single = new Single();
		}
		return single;
	}
}

//开发选择该方式--省事  饿汉式
public class Single2 {

    //类加载后就创建了,一致存在
	private static Single2 single = new Single2();
	
	private Single2() {}
	
	public static Single2 newInstance() {
		return single;
	}
}

单例模式的目的是为了保证对象唯一,那么在多线程的情况下是否还能保证唯一?

public class Single {

	private static Single single;
	
	private Single() {}
	
	public static Single newInstance() {
		if(single == null) {//先判断,在考虑是否要等待锁,以提高性能
			//假设线程A进来,B进来
			synchronized (Single.class) {//加锁后提高了安全性,但是影响性能
				if(single == null) {
					try {
						Thread.sleep(100);
						single = new Single();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}
		return single;
	}
}

5).synchronized和Lock锁的区别

1、synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁

2、用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了

3、synchronized放弃锁只有两种情况:①线程执行完了同步代码块的内容②发生异常;而lock不同,它可以设定超时时间,也就是说他可以在获取锁时便设定超时时间,如果在你设定的时间内它还没有获取到锁,那么它会放弃获取锁然后响应放弃操作

6).sleep和wait区别

public class ThreadDemo {

	public static void main(String[] args) {
		Object obj = new Object();
		
		SubThread st = new SubThread(obj);
		
		new Thread(st,"窗口A").start();
		new Thread(st,"窗口B").start();
	}
}

/**
 * 线程在阻塞的过程中是否会释放锁
 * sleep在阻塞的过程中是不释放锁的
 * wait在阻塞的过程中是会释放锁的
 */
class SubThread implements Runnable{
	
	private int count = 200;
	
	private Object obj;
	
	public SubThread(Object obj) {
		this.obj = obj;
	}
	
	@Override
	public void run() {
		while(true) {
			synchronized (obj) {
				if(count>0) {
					try {
						System.out.println(Thread.currentThread().getName()+"持有锁");
						--count;
						//Thread.sleep(100);
						//先唤醒其他的线程
						obj.notify();
						//然后自己进行等待
						obj.wait();
						System.out.println(Thread.currentThread().getName()+"重新持有锁 ");
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
}

sleep和wait的区别:

sleep在阻塞的过程中是不释放锁的

wait在阻塞的过程中是会释放锁的

7).线程池Excutors

所谓的线程池其实就是将所有的线程都在同一个空间内进行管理,管理的是线程的数量、线程的创建、任务的分配

在实际需求中,可能会出现短时间内的任务量远远超出的线程数量,这种情况下,不同的处理方式对性能的影响是不同的,常见的处理方式有:

方式一:当有新任务的时候,其他线程是非空闲状态,那就新创建线程 不推荐

方式二:当任务数大于线程数时,不再创建新的线程,对任务进行排队

方式三:指定默认的线程数,超出后不再创建,对任务进行排队 推荐

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值