进程:正在运行的程序,由操作系统分配独立的内存空间和系统资源。
线程:线程是进程的一部分,必须依赖进程。线程共享进程的内存空间和系统资源。
关系:一个进程拥有多个线程,一个线程只属于一个进程。
一、线程生命周期:
1、新建状态:当线程创建之后就进入了新建状态。保持这个状态直到启动线程。
2、就绪状态: 当线程调用了start()方法后便进入了就绪状态。等待cpu的调度。就绪状态时线程的入口,要想进入运行状态,就必须先处于就绪状态。
3、运行状态:当cpu开始调度已经处于就绪的线程,线程才进入运行状态,才开始正式运行。
4、阻塞状态:当线程运行中遇到了yield(),wait(),sleep()就会使线程进入阻塞状态,直到再次进入就绪状态,才有机会被cpu调度重新进入运行状态。
5、死亡状态:线程运行结束或者遇到异常,就会进入死亡状态,结束该线程的生命周期。
二、线程的创建:
线程创建有三种基本方式:
1、继承Thread类,重写run()方法:
public class TestThread {
public static void main(String[] args) {
MyThread m=new MyThread();
m.start();//启动线程
}
}
/***
* 通过继承创建线程 重写run()方法
* @author otote
*
*/
public class MyThread extends Thread{
@Override
public void run() {
for(int i=0;i<100;i++) {
System.out.println(i);
}
}
}
2、实现Runnable接口,重写run()方法。
public class TestRunnable {
public static void main(String[] args) {
MyRunnable m=new MyRunnable();
new Thread(m).start();//启动线程
}
}
public class MyRunnable implements Runnable{
@Override
public void run() {
for(int i=0;i<100;i++) {
System.out.println(Thread.currentThread().getName()+i);
}
}
}
3、使用Callable和Future接口创建线程。
public class TeestCallable {
public static void main(String[] args) throws Exception{
Callable<Integer> callable=new MyCallable();
FutureTask<Integer> futureTask=new FutureTask<>(callable);//用FutureTask来包装MyCallable对象
new Thread(futureTask).start();
int sum=futureTask.get();//获取值
System.out.println(sum);
}
}
public class MyCallable implements Callable<Integer>{
@Override
public Integer call() throws Exception {
int sum=0;
for(int i=0;i<100;i++) {
sum+=i;
}
return sum;
}
}
继承Thead的线程类不能再继承其他的类,扩展性差;实现Runnable的任务类还可以再继承其他的类和实现另外的接口,扩展性好。
当两个线程的资源共享时,实现Runnable接口的方式实现线程更好。
三、线程的优先级:
调用setPriority()可为线程设置优先级。
优先级越高的线程,抢占资源的概率大。并不是绝对的。
四、线程的阻塞
引起线程阻塞的方法:
1、线程睡眠 sleep()
线程的睡眠即让当前正在运行的线程按照指定的时间暂停,并进入阻塞状态。
public class SleepDemo {
public static void main(String[] args) {
web12306 web=new web12306();
Thread t1=new Thread(web,"路人甲");
Thread t2=new Thread(web,"路人乙");
Thread t3=new Thread(web,"路人丙");
t1.start();
t2.start();
t3.start();
}
}
class web12306 implements Runnable{
private int num=50;
@Override
public void run() {
while(true) {
if(num<=0) {
break;
}
try {
Thread.sleep(1000);//线程睡眠 1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到了"+"\t"+"剩余票数"+num--);
}
}
}
2、线程的礼让 yield()
线程的礼让即暂停当前运行的线程转为就绪状态并调用其它线程。
public class yieldDemo01 extends Thread{
public static void main(String[] args) {
yieldDemo01 demo=new yieldDemo01();//创建
Thread t=new Thread(demo);//就绪
t.start();
//cpu调度运行
for(int i=0;i<300;i++) {
if(i%20==0) {
//暂停本线程 main
Thread.yield();
}
System.out.println("main........"+i);
}
}
@Override
public void run() {
for(int i=0;i<200;i++) {
System.out.println("join........"+i);
}
}
}
3、线程的合并 join()
线程的合并即暂停当前的线程并执行调用join()方法的线程,直到执行结束当前线程才得以执行。
public class JoinDemo01 extends Thread{
public static void main(String[] args) throws InterruptedException {
JoinDemo01 demo=new JoinDemo01();
Thread t=new Thread(demo);//新生
t.start();//就绪
//cpu调度运行
for(int i=0;i<300;i++) {
if(i==50) {
t.join();//main 阻塞
}
System.out.println("main........"+i);
}
}
@Override
public void run() {
for(int i=0;i<100;i++) {
System.out.println("join........"+i);
}
}
}
4、守护线程 setDaemon(true)
守护线程是为其他线程提供服务的,当前台线程结束生命周期后守护线程也会自动终止。
调用setDaemon(true)可以将指定的线程设置为守护线程。
public class Test {
public static void main(String[] args) {
MyThread myThread=new MyThread();
for (int i = 0; i <100; i++) {
System.out.println("main******"+i);
if (i==30) {
myThread.setDaemon(true);//设为守护线程
myThread.start();
}
}
}
}
class MyThread extends Thread{
@Override
public void run() {
for(int i=0;i<1000;i++) {//主线程结束后守护线程也会自动终止 由cpu调度决定不是实时的
System.out.println("mythread-----"+i);
}
}
}
五、线程安全:
当多个线程共享一份资源时,由于cpu的调度的不确定性导致获取到的资源不一致,最后导致结果截然不同。所以为了避免出现这种情况就引入了线程安全。
线程同步:锁定共享的资源,每次只能让一个线程去执行,当这个线程执行完了,其他线程才能去执行。线程同步后效率会降低,但是线程会变得安全。
1、同步方法:在方法定义时加上synchronized 关键字,此方法即为同步方法。在调用该方法时会一步一步的执行方法体内的代码,直到执行完毕才会释放该方法,其他线程才能够访问该方法。
public synchronized void name() {
//代码块
}
2、同步代码块:将需要锁定的代码放在同步块内。obj 为锁对象,一般选择共享资源对象作为锁对象,也可用this。
synchronized (obj) {
//代码块
}
3、Lock互斥锁:创建一把锁,为需要锁定的代码加上一个锁,并在代码执行结束后释放锁。否则会造成死锁。
Lock lock=new ReentrantLock();//创建锁
public void test() {
//上锁
lock.lock();
//代码块
//解锁
lock.unlock();
}
水平有限,如有不足欢迎指出。