什么是进程
-
正在运行的程序,是系统进行资源分配的基本单位。
-
目前操作系统支持多线程,可以同时执行多个进程,通过进程ID区分。
-
单核CPU在同一个时刻,只能运行一个进程,宏观并行、微观串行
什么是线程
-
线程:又称轻量级进程
-
进程中的一条执行路径,也是CPU的基本调度单位
-
一个进程由一个或者多个线程组成,彼此之间完成不同的工作,
-
同时执行,称为多线程。
-
JAVA虚拟机是一个进程,当中默认包含主线程(main),可以通过代码创建多个独立线程,与main并发执行。
进程和线程之间的区别
-
进程是操作系统资源分配的基本单位,线程是CPU的基本调度单位
-
一个程序运行后至少有一个进程
-
一个进程可以包含多个线程,但是至少需要有一个线程(主线程),否则这个进程没有意义
-
进程之间不能共享数据段地址,打但是同进程的线程之间可以共享数据段地址
线程的组成
-
任何一个线程都具有基本的组成部分:
-
CPU时间片:操作系统(OS)会为每个线程分配执行时间
-
运行数据:
-
堆空间:存储线程需要使用的对象,多个线程可以共享堆中的对象
-
栈空间:存储线程需要使用的局部变量,每个线程都拥有独立的栈
-
-
线程的逻辑代码
-
线程的特点
-
线程抢占式执行,效果随机性
-
效率高
-
可以防止单一线程长时间独占CPU
-
-
在单核CPU中,宏观上同时执行,微观上顺序执行
创建线程的三种方式
-
1、继承Thread类,重写run方法(适合没有资源共享)
-
内存图
-
-
public class MyThread extends Thread{ //1继承Thread接口 @Override public void run() { //2重写run方法(编写需要执行的功能) for(int i = 0;i<100;i++){ System.out.println("子线程-------"+i); } } }
-
public class TestMyThread { public static void main(String[] args) { //3创建子类对象 MyThread myThread = new MyThread(); //调用start()方法,启动线程 myThread.start(); for(int i = 0;i<100;i++){ System.out.println("主线程++++++++++++++++++++"+i); } } }
-
-
2、实现Runnable接口(适合资源共享)
-
操作相同,共享资源类实现Runnable接口
-
操作不同,使操作类分开操作
-
-
public static void main(String[] args) { //创建实现类对象,表示线程要执行的功能 MyRunnable myRunnable = new MyRunnable(); //创建线程对象 Thread thread = new Thread(myRunnable,"我的Runnable线程"); //调用start()方法,启动线程 thread.start(); for(int i = 0;i<100;i++){ System.out.println("main线程-----------------"+i); } }
public class MyRunnable implements Runnable{ //1、子类继承Runnable接口 @Override public void run() { //实现run方法 for(int i = 0;i<100;i++){ System.out.println(Thread.currentThread().getName()+"+++++++"+i); } }
-
匿名内部类实现Runnable
Runnable runnable= new Runnable() { @Override public void run() { for(int i = 0;i<100;i++){ System.out.println(Thread.currentThread().getName()+"+++++++"+i); } } } ; Thread thread1 =new Thread(runnable ,"我的匿名内部类线程"); thread1.start();
-
-
3.实现Callable接口(JDK1.5新添加的)
获取和修改线程名称
-
获取线程ID和线程名称
-
1、在Thread的子类中调用this.getId()和this.getName()
-
public void run() { //2重写run方法(编写需要执行的功能) for(int i = 0;i<100;i++){ //获取线程名称和线程id System.out.println(this.getId()+""+this.getName()+"子线程-------"+i); }
-
2、使用Thread.currentThread().getId()和Thread.currentThread().getName(),获取当前线程
-
for(int i = 0;i<100;i++){ //获取线程名称和线程id System.out.println(Thread.currentThread().getId()+Thread.currentThread().getName()+"子线程-------"+i); }
-
-
修改线程名称(线程id不可更改)
-
1、调用线程对象的setName()方法
-
//在线程执行之前,调用setName()来修改线程名称 myThread.setName("小翠");
-
2、使用线程子类的构造方法赋值
//修改线程名称,在构造方法中赋值,子类线程重写带参数的构造方法,调用supper(String)进行改名 MyThread myThread = new MyThread("我的子线程1");
public class MyThread extends Thread{ //1继承Thread接口 public MyThread(String naem){ super(naem); }
-
案例:实现4个窗口共同卖100张票
-
public class TicketWin extends Thread{ private int ticket = 100; @Override public void run() { while (true){ if(ticket<=0){ System.out.println(Thread.currentThread().getName()+"票已卖完"); break; } System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"张票"); ticket--; } } } public static void main(String[] args) { Ticket ticket = new Ticket(); Thread t1 = new Thread(ticket,"窗口1"); Thread t2 = new Thread(ticket,"窗口2"); Thread t3 = new Thread(ticket,"窗口3"); t2.start(); t1.start(); t3.start();
-
内存分析
线程的状态
-
New:初始状态:线程对象被创建,即为初始状态。只在堆中赖皮内存,与常规对象一样
-
Ready就绪状态:线程调用start()方法,进入就绪状态,等待OS选中,并且分配时间片
-
Running 状态:获取时间片之后,进入运行状态,如果时间片到期,回到就绪状态
-
Terminated终止状态:主线程main()或独立线程run()结束,进入终止状态,并且释放持有的时间片
线程常见方法
-
休眠:
-
public static void sleep (long millis)
-
当前线程主动休眠millis毫秒,在此期间,释放CPU,不在争抢CPU
-
public static void main(String[] args) { SleepThread s1 = new SleepThread(); SleepThread s2 = new SleepThread(); s1.start(); s2.start(); } //静态内部类 static class SleepThread extends Thread{ @Override public void run() { for (int i = 0;i<10;i++){ System.out.println(Thread.currentThread().getName()+"----"+i); //休眠 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }
-
-
放弃
-
public static void yield()
-
当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片
-
谦让,放弃:放弃CPU,让给优先级和他相同或者比它优先级高的线程
-
public static void main(String[] args) { YieldThread y1 = new YieldThread(); YieldThread y2 = new YieldThread(); y1.start(); y2.start(); } static class YieldThread extends Thread{ @Override public void run() { for (int i = 0;i<10;i++) { System.out.println(Thread.currentThread().getName() + "----" + i); //谦让,放弃:放弃CPU,让给优先级和他相同或者比它优先级高的线程 Thread.yield(); } } }
-
-
加入
-
public static void join()
-
当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片
-
join();加入线程,阻塞当前线程,直到加入线程执行完毕
-
public static void main(String[] args) throws InterruptedException { joinThread join1 = new joinThread(); join1.start(); //加入线程 join1.join(); for(int i =0;i<10;i++) { System.out.println("主线程" + "....." + i); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } static class joinThread extends Thread{ @Override public void run() { for(int i =0;i<10;i++){ System.out.println(Thread.currentThread().getName()+"....."+i); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } }
-
-
线程优先级
-
线程对象.setPriority(int)
-
线程优先级为1-10,默认为5,优先级越高,表示获取CPU机会越多
-
线程优先级,从1-10,10的优先级最高,1的优先级最低.p1.setPriority
-
public static void main(String[] args) { PrioeityThread p1 =new PrioeityThread("线程1"); PrioeityThread p2 =new PrioeityThread("线程2"); PrioeityThread p3 =new PrioeityThread("线程3"); //设置p1的优先级为1 p1.setPriority(1); p3.setPriority(10); p1.start(); p2.start(); p3.start(); } static class PrioeityThread extends Thread{ public PrioeityThread(String name ){ super(name); } @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "....." + i); } } }
-
-
线程打断
-
线程对象.interrupt();
-
打断线程,被打断线程抛出InterruptedException异常
-
public static void main(String[] args) throws IOException { InterruptThread interruptThread = new InterruptThread(); interruptThread.start(); System.out.println("20秒内输入任意字符结束子线程"); System.in.read(); interruptThread.interrupt();//打断子线程 System.out.println("主线程结束"); } static class InterruptThread extends Thread{ @Override public void run() { for(int i =0;i<10;i++) { System.out.println(Thread.currentThread().getName() + "....." + i); try { Thread.sleep(10000); System.out.println("子线程自然苏醒"); } catch (InterruptedException e) { // e.printStackTrace(); System.out.println("子线程被打醒..."); } System.out.println("子线程结束了...."); } } }
-
-
守护线程
-
线程有两类:用户线程(前台线程)、守护线程(后台线程)。
-
如果程序中所有前台程序都执行完毕,后台线程会自动结束
-
垃圾回收器线程属于守护线程
-
setDaemon(true)设置为守护线程
-
DaemonThread daemonThread = new DaemonThread(); daemonThread.setDaemon(true); daemonThread.start(); for(int i =0;i<10;i++){ System.out.println(Thread.currentThread().getName()+"....."+i); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } static class DaemonThread extends Thread{ @Override public void run() { for(int i =0;i<100;i++){ System.out.println(Thread.currentThread().getName()+"....."+i); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }
-
-
线程状态(等待)
线程安全问题
-
线程不安全
-
当多线程并发访问临界资源时,如果破坏原子操作,可能会造成数据不一致
-
临界资源:共享资源(同一对象),一次仅允许一个线程使用,才能保证其正确性
-
原子操作:不可分割的多步操作,被视为一个整体,其顺序和步骤不可打乱或缺省
-
线程同步(同步代码块)
-
同步代码块
synchronized(锁){//对临界资源对象加锁(monitor)
** //代码 (原子操作)**
}
private Object lock = new Object(); synchronized (lock){ if(ticket>0){ System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"张票"); ticket--; }else { break; } }
-
每个对象都有一个互斥锁标记,用来分配给线程
-
只有拥有对象互斥锁标记的线程,才能进入到该对象加锁的同步代码块
-
线程退出同步代码块时,会释放相应的互斥锁标记
线程的状态(阻塞)
线程安全(同步方法)
-
同步方法
synchronized 返回值类型 方法名称 (形参列表){ //对当前对象(this)加锁
//代码(原子操作)
}
public class Ticket implements Runnable{ private static int ticket = 100; //private Object lock = new Object(); @Override public void run() { while (true){ if(!sale()) break; } } public synchronized boolean sale(){ if(ticket<=0){ return false; } System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"张票"); ticket--; return true; } }
-
只有拥有对象互斥锁标记的线程,才能进入该对象加锁的同步方法中
-
线程退出同步方法时,会释放相应的互斥锁标记
-
如果是静态方法,锁是【类名.class】
死锁
-
当第一个线程拥有对A对象锁标记,并且等待B对象锁标记,同时第二个线程有B对象标记,并且等待A对象锁标记时,产生死锁
-
一个线程可以同时拥有多个对象的锁标记,当线程阻塞时,不会释放已经拥有的锁标记,由此可能造成死锁
-
public class MyLock { //A锁 public static Object objectA= new Object(); //b锁 public static Object objectB= new Object(); } public class Boy extends Thread{ @Override public void run() { synchronized (MyLock.objectA){ System.out.println("男孩拿到A锁"); synchronized (MyLock.objectB){ System.out.println("男孩拿到b锁"); System.out.println("男孩可以吃饭"); } } } public class Girl extends Thread{ @Override public void run() { synchronized (MyLock.objectB){ System.out.println("女孩拿到B锁"); synchronized (MyLock.objectA){ System.out.println("女孩拿到A锁"); System.out.println("女孩可以吃饭"); } } } public static void main(String[] args) throws InterruptedException { Boy boy = new Boy(); Girl girl = new Girl(); girl.start(); Thread.sleep(100); boy.start();
线程通信
-
等待
-
public final void wait()
-
public final void wait (long timeout)
-
必须在对obj加锁的同步代码块中。在一个线程中,调用obj.wait()时,此线会释放其拥有的所有标记,同时此线程阻塞在锁的等待队列中,释放锁,进入等待队列。
-
-
通知
-
public final void notify():从等待队列中随机唤醒一个线程
-
public final void notifyAll():唤醒所有的等待线程
-
必须在对obj加锁的同步代码块中,从obj的waiting中释放一个或者全部线程。对自身没有任何影响。
-
public class BankCard { private boolean flag;//默认false private double money;//默认0 //存钱 public synchronized void save(double m) throws InterruptedException {//this:锁 if(flag){ this.wait();//释放锁和CUP,进入等待 } this.money = m + money; System.out.println(Thread.currentThread().getName() + "存了" + m + "元,余额是:" + this.money); this.flag =true; this.notify(); } //取钱 public synchronized void take(double m) throws InterruptedException {// this:锁 if(!flag){//没钱 this.wait(); //取等待队列等待 } this.money = money - m; System.out.println(Thread.currentThread().getName() + "取了" + m + "元,余额是:" + this.money); this.flag = false; //唤醒存钱线程 this.notify(); }
生产者和消费者
public class BreadCon { //创建数组,保存产品 private Bread[] breads = new Bread[6]; private int size; //存放面包 public synchronized void input(Bread bread) throws InterruptedException { while (size>=breads.length){ this.wait(); } breads[size] = bread; System.out.println(Thread.currentThread().getName()+"生产了"+bread.getId()+"号面包"); size++; this.notifyAll();// 通知消费者消费 } public synchronized void output() throws InterruptedException { while (size<=0){ this.wait(); } size--; //size-1代表下标 Bread b = breads[size]; System.out.println(Thread.currentThread().getName()+"消费了"+b.getId()+"号面包"); breads[size] = null; this.notifyAll();// 通知生产者生产 } }