一、引入概念
程序:是为完成特定任务、用某种语言编写的一组指令的集合。简单的说:就是我们写的代码
进程:
1、进程是指运行中的程序
2、进程是程序的一次执行过程,或是正在运行的一个程序,是动态过程:有它自身产生,存在和消亡的过程。
线程:
1.线程由进程创建的,是进程的一个实体
2.一个进程可以拥有多个线程,
单线程和多线程:
单线程:同一个时刻,只允许执行一个线程
多线程:同一时刻,可以执行多个线程
并发和并行:
二、线程的继承实现图;(自定义线程可以继承Thread也可以直接实现Rubable)
(1)继承Thread实现一个简单的线程
public class Thread01 { public static void main(String[] args) { cat cat = new cat();
cat.run();//run只是在cat中的一个方法,当调用时,并不会重新开启一个线程。会在main中按照顺序执行
cat.start();//启动线程,启动之后run方法开始运行,并不会对main方法造成任何影响。
} }
class cat extends Thread{ @Override public void run() { int runtime = 0; while (runtime<8) { try { System.out.println("喵喵,我是小猫咪!"); runtime++; Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
线程的结构
可以在程序运行时(进程开始的时候:在终端利用Jconsole查看当前进程中的线程)
注意:
在进程中,如果主线程停止了,子线程还没结束,并不会结束进程。
(2)实现一个Runnabled的接口,实现一个多线程的类
public class Thread02 { public static void main(String[] args) { dog dog = new dog(); //dog.start(); Thread thread = new Thread(dog); thread.start(); } } class dog implements Runnable{ @Override public void run() { int timesize = 0; while (timesize<10) { try { System.out.println("hi! here is a dog!!!"); timesize++; Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
注意:由于没有继承Thread类,所以在main中无法调用start方法启动线程,Runnable中只有一个run方法,所以需要重新定义一个新的线程,再将实现类dog放进去;
(3)线程的的基本使用
<1>线程如何理解:
(4)线程终止
基本说明:
1.当线程完成任务后,会自动退出。
2.还可以通过使用变量来控制run方法退出的方式停止线程,即通知方式
(利用一个loop来通知线程结束)
public class Thread02 { public static void main(String[] args) throws InterruptedException { dog dog = new dog(); //dog.start(); Thread thread = new Thread(dog); thread.start(); Thread.sleep(10*1000); dog.setLoop(false); } } class dog implements Runnable{ private boolean loop = true; public void setLoop(boolean loop) { this.loop = loop; } @Override public void run() { int timesize = 0; while (loop) { try { System.out.println("hi! here is a dog!!!"); timesize++; Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
(5)线程常用方法
1).setName //设置线程名称,使之与参数name相同
2).getName//返回该线程的名称
3). start//使该线程开始执行;Java虚拟机底层调用该线程的startO方法
4). run 1/调用线程对象run 方法;
5).setPriority //更改线程的优先级
6). getPriority //获取线程的优先级
7).sleep1/在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
8). interrupt 1/中断线程
9)yield:线程的礼让。让出cpu,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功
10).join: 线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务
代码实例:
public class ThreadMothed { public static void main(String[] args) throws InterruptedException { T t = new T(); t.start();//启动线程 for (int i = 0; i <10; i++) {//进行主线程的代码运行 Thread.sleep(1000); System.out.println("here is main!!"+i); if (i == 3){ System.out.println("开始插队!!"); t.join();//让T插队,插队之后t线程将运行完毕退出再进入主程序 System.out.println("插队完成!!"); } } } } class T extends Thread{ @Override public void run() { int i = 0; while (i<10){ try { for (int j = 0; j < 10; j++) { Thread.sleep(1000); System.out.println("hello test let`s go!!"+ j); i++; } } catch (InterruptedException e) { e.printStackTrace(); } } } }
11)用户线程和守护线程
1.用户线程:也叫工作线程,当线程的任务执行完或通知方式结束
2.守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
3.常见的守护线程:垃圾回收机制
public class ThreadMothed01 { public static void main(String[] args) throws InterruptedException { T1 t1 = new T1(); Thread thread = new Thread(t1); thread.setDaemon(true);//设置线程为守护线程 thread.start(); for (int i = 0; i < 5; i++) { System.out.println("test deamon!!!"); Thread.sleep(1000); } } } class T1 implements Runnable{ @Override public void run() { int i = 0; // while (i < 10) { while (true) {//测试守护线程 for (int j = 0; j < 10; j++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test!!!" + j); i++; } } } }
二、线程的生命周期
1、在JDK中Thread.State枚举表示了线程的几种状态
线程状态图
三、线程同步机制(Synchronized)
线程同步机制:
1.在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技
术,保证数据在任何时刻,最多有一个线程访问,以保证数据的完整性。
2.也可以这里理解:线程同步,即当有一个线程在对内存进行操作时,其他线程都不
可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作.
同步具体方法:
四、互斥锁
基本介绍:
1. Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。
2.每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。
3.关键字synchronized来与对象的互斥锁联系。当某个对象用synchronized修饰时,表明该对象在任一时刻只能由一个线程访问
4.同步的局限性:导致程序的执行效率要降低
5.同步方法(非静态的)的锁可以是this,也可以是其他对象(要求是同一个对象)
6.同步方法(静态的)的锁为当前类本身。
public class selltickets { public static void main(String[] args) { // Tickets tickets = new Tickets(); // Tickets tickets1 = new Tickets(); // Tickets tickets2 = new Tickets(); // tickets.start(); //定义三个新线程,开始进行线程同步测试,线程处理的对象需要是同一个对象 //不然不会体现出线程同步,线程锁的作用 Tickets tickets = new Tickets(); new Thread(tickets).start(); new Thread(tickets).start(); new Thread(tickets).start(); } } class Tickets extends Thread{ //class Tickets implements Runnable{ private int ticket = 100; private boolean loop = true; @Override public void run() { while (loop){ loop=sell(); } } private synchronized boolean sell(){ if (ticket<=0){ System.out.println("票已全部售出。。。"); return false; } if (ticket>0){ System.out.println(Thread.currentThread().getName() + "-售出一张票-剩余的票数"+(--ticket)); try { Thread.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } } return true; } }
注意事项和细节:
1.同步方法如果没有使用static修饰:默认锁对象为this
2.如果方法使用static修饰,默认锁对象:当前类.class
3.实现的落地步骤:
需要先分析上锁的代码
选择同步代码块或同步方法
要求多个线程的锁对象为同一个即可!
五、线程死锁
简介:多个线程都占用了对方的锁资源,但不肯相让,导致了死锁,在编程是一定要避免死锁的发生。
(1)使用继承方式Thread 体现死锁
public class DeadLock { public static void main(String[] args) { Lock lock = new Lock(true);//传参true获取o1对象 lock.setName("true");//设置线程名称 Lock lock1 = new Lock(false);//传参true获取o2对象 lock1.setName("false"); //开始新的进程 lock.start(); lock1.start(); } } class Lock extends Thread { static Object o1 = new Object(); static Object o2 = new Object(); private boolean loop; public Lock(boolean loop) { this.loop = loop; } @Override public void run() { if (loop){ synchronized (o1){ System.out.println(Thread.currentThread().getName()+"进入正确的o1,等待o2"); synchronized (o2){ System.out.println("拿到o2,o1执行完成"); } } }else{ synchronized (o2){ System.out.println(Thread.currentThread().getName()+"进入错误的o1,等待o2"); synchronized (o1){ System.out.println("拿到o2,o1执行完成"); } } } } }
使用实现Runnable 的方式体现死锁
public class DeadLock { public static void main(String[] args) { Lock lock = new Lock(true); Thread thread = new Thread(lock); thread.setName("A"); Lock lock1 = new Lock(false); Thread thread1 = new Thread(lock1); thread1.setName("B"); thread.start(); thread1.start(); } } class Lock implements Runnable { static Object o1 = new Object(); static Object o2 = new Object(); private boolean loop; public Lock(boolean loop) { this.loop = loop; } @Override public void run() { if (loop){ synchronized (o1){ System.out.println(Thread.currentThread().getName()+"进入正确的o1,等待o2"); synchronized (o2){ System.out.println("拿到o2,o1执行完成"); } } }else{ synchronized (o2){ System.out.println(Thread.currentThread().getName()+"进入错误的o1,等待o2"); synchronized (o1){ System.out.println("拿到o2,o1执行完成"); } } } } }
六、释放锁
释放锁的情况:
1.当前线程的同步方法、同步代码块执行结束
案例:上厕所,完事出来
2.当前线程在同步代码块、同步方法中遇到break、return.
案例:没有正常的完事,经理叫他修改bug,不得已出来
3.当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束
案例:没有正常的完事,发现忘带纸,不得已出来
4.当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,并释
放锁。
案例:没有正常完事,觉得需要酝酿下,所以出来等会再进去
不会释放锁的情况:
1.线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方
法暂停当前线程的执行,不会释放锁
案例:上厕所,太困了,在坑位上眯了一会
2.线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,
该线程不会释放锁。
提示:应尽量避免使用suspend()和resume()来控制线程,方法不再推荐使用