多线程
1.进程和线程
进程:受操作系统管理的基本单元(可以将一个exe理解为一个进程)
线程:进程中独立运行的子任务。
并发:同一时间执行不同的任务,任务来回切换
2.创建线程
2.1继承Thread
//继承Thread
public class MyThread extends Thread {
@Override
public void run() {
super.run();
System.out.println("搬砖");
}
}
2.2实现Runnable接口
public class MyThread2 implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("搬砖");
}
}
3.线程API
方法 | 描述 |
---|---|
setName(String name) | 为线程设置名称 |
getName() | 获取线程名称 |
getId() | 获取线程的唯一标识 |
setPriority(int i) | 设置线程的优先级 |
4.线程生命周期
- 新生:线程类new出对象,此时内存中仅仅是为对象分配内存空间,为成员变量赋初始值。
- 就绪:调用线程start()方法后线程进入就绪状态,此时线程并不是立即执行,而是等待线程调度器分配时间片,当该线程得到资源后,调用线程run()方法
- 运行:线程run()方法运行时。
- 阻塞:当线程因为某些原因不能继续运行,并放弃所占用的处理器资源时,就进入阻塞状态,阻塞状态不能直接进入运行状态,而是当阻塞结束是,重新进入就绪状态,重新等待线程调度器分配资源。当出现如下情况时,线程进入阻塞。
- 线程调用sleep()方法时,当sleep()休眠时间结束时,线程进入就绪状态
- 线程调用阻塞式IO方法,在该方法返回之前,线程被阻塞
- 线程试图获得一个同步监视器,但是该监视器正在被其他线程持有
- 线程在等待某个notify()
- 死亡:run()方法执行完成,或者线程抛出异常
5.线程同步
5.1synchronized
非线程安全:多个线程访问同一对象实例变量时,出现读取结果不一致(或者称为脏度),就是非线程安全
- 同步方法
package cn.it.sy;
public class MyNumber {
private int count = 0;
// 当前对象
public synchronized void change(String s) {
try {// 原子性
if (s.equals("a")) {
count = 100;
System.out.println("a set count over");
Thread.sleep(2000);
} else {
count = 200;
System.out.println("other set count over");
}
System.out.println(Thread.currentThread().getName() + "====" + count);
} catch (InterruptedException e) {
// TODO: handle exception
}
}
}
package cn.it.sy;
public class ThreadA extends Thread{
private MyNumber myNumber;
private String s;
public ThreadA(MyNumber myNumber, String s) {
super();
this.myNumber = myNumber;
this.s = s;
}
@Override
public void run() {
myNumber.change(s);//s="a"
}
}
package cn.it.sy;
public class ThreadB extends Thread{
private MyNumber myNumber;
private String s;
public ThreadB(MyNumber myNumber, String s) {
super();
this.myNumber = myNumber;
this.s = s;
}
@Override
public void run() {
myNumber.change(s);//s="b"
}
}
package cn.it.sy;
/**
* 该示例是为了演示非线程安全的情况
* @author MR.W
*
*/
public class Test {
public static void main(String[] args) {
//非线程安全:多个线程访问相同对象的同一实例变量
MyNumber myNumber1 = new MyNumber();
// MyNumber myNumber2 = new MyNumber();
ThreadA a = new ThreadA(myNumber1, "a");
a.setName("a");
ThreadB b = new ThreadB(myNumber1, "b");
b.setName("b");
a.start();
b.start();
}
}
- 同步代码块
- 必须保证锁唯一
5.2 同步锁
5.3 volatile
6.线程优先级
线程默认优先级和父线程优先级保持一致
方法 | 描述 |
---|---|
setPriority(int i) | 设置线程优先级 |
getPriority() | 获取线程优先级 |
7.线程调度
方法 | 描述 |
---|---|
join() | 停止当前运行的线程,等待join的线程运行结束后再运行当前线程(插队) |
yield() | 暂停当前线程,并重新调度线程,具有相同优先级或者更高优先级的线程优先执行 |
sleep(long l) | 线程休眠l毫秒,休眠结束后继续执行 |
8.线程间通信
方法 | 描述 |
---|---|
wait() | 线程挂起,等待其他线程唤醒,并释放锁 |
notify() | 随机唤醒某个调用wait()方法,处于阻塞状态的线程 |
notifyAll() | 唤醒所有因为调用wait()方法,处于阻塞状态的线程 |
8.1生产者消费者模式
9.线程面试题
-
请简述调用start()方法和run()方法的区别
- run()方法中定义线程执行的任务,start()方法则是用来启动线程
- 直接调用run方法jvm会将run()方法当做普通实例方法执行,而不是启动该线程
- 调用start()方法后,该线程进入就绪状态,当线程调度器为该线程分配时间片以后,该线程开始执行run方法
-
请简述创建线程的两种方式,及区别
- 创建线程可以继承Thread类,并重写run()方法
- 可以继承Runnable接口,并实现/重写run()方法
- 因为java中是单继承,通过继承Thread类创建线程后,该类则不能继承其他类,继承Runnable接口则可以继承其他类或者其他接口,有利于类的扩展。
- 继承Thread类可以直接调用start()方法启动线程,继承Runnable接口后则必须通过Thread的构造方法,传入改对象创建Thread对象再调用star()方法启动线程。
-
请简述sleep()方法和wait()的区别
相同点:
- sleep和wait方法都可以使线程进入阻塞状态
- sleep和wait方法都会抛出InterruptedException
不同点:
- sleep()方法在休眠结束后重新进入就绪状态,wait()方法会一直阻塞线程直到被唤醒
- sleep方法是Thread类的成员方法,wait()方法是Object类的成员方法
- sleep()方法不会释放,wait()方法会释放锁
-
定义两个线程操作同一个变量,一个线程对其加1,另一个线程对其减1,循环输出010101010101
package cn.it.java;
public class Print{
int count = 0;
public synchronized void printAdd(){
if(count==1) {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
count++;
System.out.println(count);
notify();
}
public synchronized void printReduce(){
if(count==0) {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
count--;
System.out.println(count);
notify();
}
}
package cn.it.java;
public class Add extends Thread {
private Print print;
public Add(Print print) {
super();
this.print = print;
}
@Override
public void run() {
while (true) {
print.printAdd();
}
}
}
package cn.it.java;
public class Reduce extends Thread {
private Print print;
public Reduce(Print print) {
super();
this.print = print;
}
@Override
public void run() {
while (true) {
print.printReduce();
}
}
}
package cn.it.java;
public class Test {
public static void main(String[] args) {
Print p = new Print();
Add add = new Add(p);
Reduce reduce = new Reduce(p);
add.start();
reduce.start();
}
}
- 定义三个线程,三个线程循环输入ABCABCABCABC
package cn.it.notify;
public class Print {
boolean a = true;
boolean b = false;
boolean c = false;
public synchronized void printA() {
try {
if(!a) {
wait();
}else if(a&&!b&&!c) {
System.out.println("A");
a = false;
b = true;
c = false;
notifyAll();
}
} catch (InterruptedException e) {
// TODO: handle exception
}
}
public synchronized void printB() {
try {
if(!b) {
wait();
}else if(!a&&b&&!c) {
System.out.println("B");
a=false;
b=false;
c=true;
notifyAll();
}
} catch (InterruptedException e) {
// TODO: handle exception
}
}
public synchronized void printC() {
try {
if(!c) {
wait();
}else if(!a&&!b&&c) {
System.out.println("C");
a=true;
b=false;
c=false;
notifyAll();
}
} catch (InterruptedException e) {
// TODO: handle exception
}
}
}
package cn.it.notify;
public class ThreadA extends Thread{
private Print print;
public ThreadA(Print print) {
super();
this.print = print;
}
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
while(true) {
print.printA();
}
}
}
package cn.it.notify;
public class ThreadB extends Thread{
private Print print;
public ThreadB(Print print) {
super();
this.print = print;
}
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
while(true) {
print.printB();
}
}
}
package cn.it.notify;
public class ThreadC extends Thread{
private Print print;
public ThreadC(Print print) {
super();
this.print = print;
}
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
while (true) {
print.printC();
}
}
}
package cn.it.notify;
public class Test {
public static void main(String[] args) {
Print print = new Print();
ThreadA a = new ThreadA(print);
ThreadB b = new ThreadB(print);
ThreadC c = new ThreadC(print);
a.start();
b.start();
c.start();
}
}