- 多线程
- 概念
- 创建线程与基本使用
- 线程终止
- 线程常用方法
- 用户线程和守护线程
- 线程生命周期,线程七大状态
- 线程同步机制
- 线程的死锁
- 释放锁
多线程
概念
- 进程:进程就是运行中的程序,比如打开qq,就是一个进程,操作系统会为他分配内存,进程是程序的一次执行过程,是一个动态过程
- 线程:线程由进程创建,是进程的一个实体,一个进程可以有多个线程
- 并发:一个cpu同时执行多个任务,来回切换
- 并行:多个cpu各自执行一个任务,并行中也可能含有并发,即其中可能有一个cpu执行多个任务。
创建线程与基本使用
- 创建一个线程类有两种方法,
- 第一种是继承Thread类,第二种是实现Runnable接口,Thread类实现了Runnable接口。
- 线程类中需要重写run方法,run方法中写自己的业务逻辑
- Thread.sleep()用来休眠
- Thread.currentThread()返回当前正在执行的线程的引用,getname()用于返回线程名
- 在主函数中启动线程用start方法,不会阻塞,start方法里面再调用start0方法,start0是本地方法,由虚拟机调用,
- run方法是一个普通方法,不会开线程,如果直接调用run方法就会阻塞,而不是开线程
- jconsole可以用于实时看线程结束
public class Thread01 {
public static void main(String[] args) throws Exception{
Dog dog = new Dog();
dog.start();//启动线程
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
Thread.sleep(500);
}
}
}
class Dog extends Thread{
int times=0;
@Override
public void run() {
while(true) {
System.out.println("汪汪汪" + (++times)+ Thread.currentThread().getName());
if (times==20){
break;
}
try {
Thread.sleep(500);//休眠,需要捕获异常
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
- 实现Runnable接口来开发进程类,适用于该类继承了其他类,并且利于共享资源,即开多个线程都调用某个类的run方法,
- 但是start方法在Thread类中,所以当启动进程时需要把这个进程赋给Thread对象。使用了代理模式
public class test02 {
public static void main(String[] args) {
Cat2 cat2 = new Cat2();
Thread thread = new Thread(cat2); //赋给Thread对象
thread.start();
}
}
class Animal{
}
class Cat2 extends Animal implements Runnable{
@Override
public void run() {
while (true) {
System.out.println("hi");
try {
Thread.sleep(200);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
线程终止
run方法内有while循环来不停执行代码,通过改变while的判断条件来停止进程
具体就是while中传入参数,想要停止进程时,把参数改为false
public class test05 {
public static void main(String[] args) {
T t = new T();
t.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
t.setLoop(false);//把进程停止
System.out.println("T运行停止");
}
}
class T extends Thread{
Boolean loop=true;
@Override
public void run() {
while (loop) {//控制进程停止
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("T在运行");
}
}
public void setLoop(Boolean loop) {
this.loop = loop;
}
}
线程常用方法
- start 启动线程
- Thread.sleep ,static方法,休眠,
- interrupt 中断线程的休眠
- getName 得到线程的名字
- setName 设置线程的名字
- getPriority 得到线程的优先级
- setPriority 设置线程的优先级
- setDaemon方法:把一个线程设置成守护线程,注意:需在start前写
- Thread.yield,static方法,线程的礼让,让出cpu让其他线程执行,但不一定会成功(当cpu充分时不会成功)
- join,线程的插队,让其他线程先执行,并且一定当其他线程执行完后才会回来执行当前线程
案例1:
public class test06 {
public static void main(String[] args) throws Exception{
T1 t1 = new T1();
Thread thread = new Thread(t1);
thread.start();
for (int i = 0; i < 5; i++) {
Thread.sleep(1000);
System.out.println("主线程在执行");
}
thread.interrupt();//中断休眠
}
}
class T1 implements Runnable{
@Override
public void run() {
while (true) {
for (int i = 0; i <= 100; i++) {
System.out.println(Thread.currentThread().getName() + " 正在执行" + i);
}
try {
Thread.sleep(50 * 1000);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + " 的休眠被中断了");
}
}
}
}
案例2:
public class test07 {
public static void main(String[] args) {
T2 t2 = new T2();
Thread thread = new Thread(t2);
thread.start();
for (int i = 0; i < 20; i++) {
System.out.println("hi,主线程"+i);
if(i==4){
// try {
// join方法,直接让子线程插队
// thread.join();
//礼让,不一定会成功
Thread.yield();
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
class T2 implements Runnable{
int count=0;
@Override
public void run() {
while (true){
count++;
System.out.println("hello,子线程"+count);
if (count==20){
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
用户线程和守护线程
- 用户线程:也叫工作线程,当线程的任务执行完后或以通知方式结束
- 守护线程:一般是为工作线程服务的,当所有的工作线程结束后,守护线程自动结束
- (守护线程在start后就会工作,在所有线程结束后,就会自动结束)
- 常见的守护线程:垃圾回收机制
setDaemon方法:把一个线程设置成守护线程,注意:需在start前写
public class test08 {
public static void main(String[] args) {
T3 t3 = new T3();
Thread thread = new Thread(t3);
thread.setDaemon(true);
thread.start();
for (int i = 0; i < 10; i++) {
System.out.println("主线程在工作"+i);
// if ()
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
class T3 implements Runnable{
@Override
public void run() {
while (true){
System.out.println("hello,子线程");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
线程生命周期,线程七大状态
线程同步机制
通过synchronized方法实现线程同步,对象互斥锁
- 放在方法声明中,表示整个方法为同步方法
public synchronized void m(){
}//这就是一个实现了线程同步的方法,同一时刻只能有一个线程在调用这个方法
举例可以用来改进卖票程序会超卖和重票问题
public class test04 {
public static void main(String[] args) {
SellTecket sellTecket = new SellTecket();
Thread thread = new Thread(sellTecket);
Thread thread2 = new Thread(sellTecket);
Thread thread3 = new Thread(sellTecket);
thread.start();
thread2.start();
thread3.start();
}
}
class SellTecket implements Runnable{
private int TecketNum=100;
private boolean loop=true; //用来控制线程结束
public synchronized void sell(){ //一个线程进入sell后,其他线程就不能进入
if (TecketNum <= 0) {
System.out.println("票卖完了");
loop=false;
return; }
try {
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + "正在售票,余票为" + (--TecketNum));
}
@Override
public void run() {
while (loop) {
sell();
}
}
}
- 同步代码块
synchronized(){
//内容
}
public void sell(){ //一个线程进入sell后,其他线程就不能进入
synchronized (this) {
if (TecketNum <= 0) {
System.out.println("票卖完了");
loop = false;
return; }
try {
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + "正在售票,余票为" + (--TecketNum));
}
}
(非静态的)同步方法的锁可以是this,也可以是其他对象,但要求是同一对象
(静态的)同步方法的锁是类本身
静态举例:
public static void M(){
synchronized (SellTecket.class){
System.out.println("hello");
}
}
线程的死锁
多个线程都占用了对方的锁资源,但不肯相让,导致了死锁
public class test09 {
public static void main(String[] args) {
T9 t9 = new T9(true);
T9 t92 = new T9(false);
Thread thread1 = new Thread(t9);
Thread thread2 = new Thread(t92);
thread1.start();
thread2.start();
}
}
class T9 implements Runnable{
public static Object obj1=new Object();
public static Object obj2=new Object();
boolean loop;
public T9(boolean loop) {
this.loop = loop;
}
@Override
public void run() {
if (loop){
synchronized (obj1){
System.out.println(" 占用了2");
synchronized (obj2){
System.out.println("占用了1");
}
}
}else {
synchronized (obj2) {
System.out.println(" 占用了1");
synchronized (obj1) {
System.out.println("占用了2");
}
}
}
}
}
//但上述代码不一定会死锁,与线程的调度有关,如当thread1拿到obj1锁后,立刻又拿到了obj2锁,然后把这两个锁都释放了,这时thread2线程还没有开始,这种情况就不会死锁
释放锁
有四种情况会释放锁
- 当前线程的同步方法,同步代码块执行结束时
- 当前线程在同步方法,同步代码块遇到break,return退出了
- 在同步方法,同步代码块遇到Error或Exception
- 当前线程在同步方法,同步代码块执行了wait()方法,当前线程暂停,并释放锁
- (执行sleep()方法,yield()方法不会释放锁)