进程:程序的一次动态加载过程,包括了代码加载,编译,执行,结束的一个完整过程
线程:线程是比进程更小的单位,行为很像进程,一个进程在执行过程中可以产生多个线程
多线程序机制:每个java程序都有一个主线程,当jvm加载代码,发现main方法,此时就会启动主线程,在mian方法执行过程中再创建的线程就是其他线程。
线程的四种状态:
新建状态:Thread类被创建
运行状态:执行run()方法
中断状态:
- 1.资源从当前线程被切换给了其他线程
- 2.调用sleep()方法
- 3.wait()方法
- 4 .线程执行某个操作期间进入了阻塞状态
死亡状态:1.正常运行完全部工作 2.被强制中断
状态图:
java线程的优先级在常数1-10之间,默认为5级
创建线程的三种方法:
1.线程任务类,接口Runnable
public class ThreadB implements Runnable{
@Override
public void run() {
for(int i=0;i<10;i++) {
System.out.println(i+"B线程");
}
}
}
启动线程时,需要通过Thread 对象进行启动
public class ThreadTestB {
public static void main(String[] args) {
//1.创建线程任务
ThreadB tb=new ThreadB();
//2.通过线程任务,创建线程对象
Thread t=new Thread(tb);
t.start();
for(int i=0;i<10;i++) {
System.out.println(i+"Main线程");
}
}
}
2.继承Thread的线程类
public class ThreadTestA {
public static void main(String[] args) {
//1.创建线程对象
ThreadA ta=new ThreadA();
ta.start();
for(int i=0;i<10;i++) {
System.out.println(i+"Main线程");
}
}
}
启动线程时,直接 new 线程对象.start()方法
public class ThreadA extends Thread{
@Override
public void run() {
for(int i=0;i<10;i++) {
System.out.println(i+"A线程");
}
}
}
线程同步(synchronized)
问题:当两个或多个线程同时访问同一变量时,并且对该变量会进行修改,那么就可能会出现混乱
线程同步方法:使用 synchronized 修饰来方法
线程同步机制:当一个线程A使用synchronized方法时,其他线程想用synchronized方法时就必须等待,直到线程A使用完synchronized方法。
例子:会计和出纳共同拥有一个账本,他们都可以使用saveOrTake方法对账本进行访问。如果会计正在使用saveOrTake存入钱时,出纳被禁止使用,反之如此。
package com.money;
//线程同步(synchronized):
public class Test {
public static void main(String[] args) {
Bank bank = new Bank();
bank.setMoney(300);
Thread accountant = new Thread(bank); //声明线程变量会计
Thread cashier = new Thread(bank); //声明线程变量出纳
accountant.setName("accountant");
cashier.setName("cashier");
accountant.start();
cashier.start();
}
}
package com.money;
public class Bank implements Runnable {
int money = 200;
public void setMoney(int n) {
money = n;
}
public void run() {
if(Thread.currentThread().getName().equals("accountant")) {
saveOrTake(300);
}else if(Thread.currentThread().getName().equals("cashier")) {
saveOrTake(150);
}
}
//存取方法 (如果将synchronized取掉,线程就不会有等待,结果将会不一样)
public synchronized void saveOrTake(int amount) {
if(Thread.currentThread().getName().equals("accountant")) {
for(int i=1;i<=3;i++) {
money=money+amount/3; //每次存入100,暂停一下
System.out.println(Thread.currentThread().getName()+
"存入"+amount/3+"元,账上有"+money+"元");
}
try {
Thread.sleep(1000); //此时出纳无法使用saveOrTake方法
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else if(Thread.currentThread().getName().equals("cashier")) {
for(int i=1;i<=3;i++) {
money=money-amount/3; //每次取出50,暂停一下
System.out.println(Thread.currentThread().getName()+
"取出"+amount/3+"元,账上有"+money+"元");
}
try {
Thread.sleep(1000); //此时会计无法使用saveOrTake方法
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
协调同步的线程(wait)
问题:当一个线程中在使用的同步方法时,需要用到其中的某个变量,而此变量又需要其他线程修改后才符合此线程的需要,此时就需要用wait()方法。
wait()方法:中断线程的执行,使当前线程等待,允许其他线程使用这个同步方法。使用完后通过线程结束等待:notify(),notifyall()
例子:A和B买电影票,电影票5元一张,售票员只有两张5元的钱,A有20元排第一位,B有5元排第二位。因此A必须等待B先买票。
package com.ticket;
public class Test {
public static void main(String[] args) {
Ticket ticket = new Ticket();
Thread A = new Thread(ticket);
Thread B = new Thread(ticket);
A.setName("A");
B.setName("B");
A.start();
B.start();
}
}
package com.ticket;
public class Ticket implements Runnable {
int five=2;
int twenty=0;
@Override
public void run() {
if(Thread.currentThread().getName().equals("A")) {
saleTicket(20);
}else if(Thread.currentThread().getName().equals("B")) {
saleTicket(5);
}
}
public synchronized void saleTicket(int money) {
if(money==5) {
five=five+1;
System.out.println(Thread.currentThread().getName()+"拿到入场票");
}else if(money==20) {
while(five<3) { //如果5元少于3张则需要等待
try {
System.out.println(Thread.currentThread().getName()+"等待买票");
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
five=five-3;
twenty=twenty+1;
System.out.println(Thread.currentThread().getName()+"拿到入场票,收到15元找零");
}
notifyAll();
}
}
线程联合(join)
一个线程A在占有CPU期间可以让其他线程调用jion()和本线程联合
如B.join()。
如果在此期间联合了B线程,那么A线程会立即中断,执行B线程,直到B线程执行完毕,A线程再重新排队等待CPU资源,恢复执行。但是B线程是已经结束的线程,那么B.join()不会有效果。
join()和wait()有基本相同的效果。两者都是中断当前线程,让出CPU资源。原因在于join()底层的实现是基于wait(),不同处主要有:
1.wait()方法必须在synchronized方法中使用
2.wait()方法可以使用带毫秒参数的wait(long timeout)方法,在延迟timeout毫秒后,被暂停的线程将被恢复到锁标志等待池
守护线程
调用void setDaemon(boolean)将一个线程设置为守护线程,线程默认是非守护线程,非守护线程也称为用户线程
使用:thread.setDaemon(true);
效果:当所有用户线程都结束了,如果守护线程中run方法还没有执行完,守护线程也会立即结束
当线程A结束时,线程B虽然在while中,run方法中还未执行完毕,但是也会随着线程A一起结束
public class Test {
public static void main(String[] args) {
Daemon dam = new Daemon();
Thread A = new Thread(dam);
Thread B = new Thread(dam);
A.setName("A");
B.setName("B");
A.start();
B.setDaemon(true); //线程B为守护线程
B.start();
}
}
public class Daemon implements Runnable {
@Override
public void run() {
if(Thread.currentThread().getName().equals("A")) {
for(int i=0;i<8;i++) {
System.out.println("i="+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}else if(Thread.currentThread().getName().equals("B")) {
while(true) {
System.out.println("线程B是守护线程");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}