什么是线程和进程?
进程:
进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。
在java中,当我们启动main函数时就是启动了一个JVM的进程,而main函数所在的线程就是这个进程中的一个线程,也称主线程。
线程:
线程和进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行过程中可以产生多个线程。
在java中,当我们启动main函数时其实就是启动了一个JVM的进程,而main函数所在的线程就是在这个进程的一个线程,也称为主线程。
使用多线程
1.继承Thread类,重写run方法。
public class Tiket2 extends Thread {
private int i = 10;
@Override
public void run() {
while (i > 0) {
System.out.println("子线程..."+ i);
i--;
}
}
}
测试类:
public class TiketTest {
public static void main(String[] args) {
Tiket2 tiket2 = new Tiket2();
tiket2.start();
System.out.println("主线程...");
}
}
测试结果:
从这里我们可以看出先执行了main函数中的代码然后再执行的tiket2中的代码。
2.实现Runable接口
public class MyThread implements Runnable {
private int i = 10;
@Override
public void run() {
while (true) {
if (i > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + i);
i--;
} else {
break;
}
}
}
测试类:
public class MyThreadTest {
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread);
thread.start();
}
}
虽然MyThread实现了run的方法,但是创建的类并不能直接调用start的方法,需要new一个Thread类然后将创建的myThread放入参数中,最后通过thread来调用start方法。
解决多线程同步问题
我们模拟一个卖票的场景,总共有2个窗口一共有100张票,2个窗口同时卖票,如果线程不同步,那么可能就会造成2个窗口卖同一张票的情况,这样就会造成了线程不同步的问题。
我们创建一个TiketRunable实现了Runnable接口中的run方法:
public class TiketRunable implements Runnable {
private int i = 100;
@Override
public void run() {
//线程同步 锁
while (true) {
// synchronized (this) {
if(i>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//t2
System.out.println(Thread.currentThread().getName() + "--" + i);
i--;
}else {
break;
}
// }
}
}
}
测试类:
public class TiketRunableTest {
public static void main(String[] args) {
TiketRunable tiketRunable = new TiketRunable();
Thread thread = new Thread(tiketRunable);
Thread thread2 = new Thread(tiketRunable);
thread.start();
thread2.start();
System.out.println("main-end");
}
}
测试结果:
从这里我们可以看出两个线程交替进行卖票,但是并没有解决线程冲突的问题,比如Thread0和Thread1同时卖了同一张票。所以我们解决线程同步的方法是加一个锁使用synchronized关键字。
使用synchronized关键字可以加在方法上面也可以加在代码块中,上面的代码被我注释掉了,这种方法就是加在代码块中,然后加在方法中的是:
public class TiketRunable implements Runnable {
private static int i = 1000;
@Override
public void run() {
//线程同步 锁
while (true) {
test();
if (i<1) {
break;
}
}
}
public static synchronized void test() {
if(i>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//t2
System.out.println(Thread.currentThread().getName() + "--" + i);
i--;
}else {
return;
}
}
}
直接在方法中加上synchronized即可。
这两种方法是解决多线程同步的常用方法,还有一些不常用的方法,例如利用Volatile关键字,使用重入锁实现线程同步和ThreadLocal类。
加上锁后的效果:
这样就可以看出线程没有了冲突而且还交替进行。
线程的生命周期(状态)
1.新建状态
2.就绪状态
3.运行状态
4.阻塞状态
5.死亡状态
线程的注意事项
1.启动线程不是用run方法,而是用start方法。
2.执行线程的时候先执行完main函数中的主线程然后再执行子线程。
3.实现Runable接口后不能直接用start方法还是需要new一个Thread类然后调用start方法启动线程。
4.结束线程的时候最好不要用stop()方法,因为stop方法已经过时了,然后会破坏原子的逻辑性,可以用wait()和sleep()方法。