多线程
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方法是Thread类的成员方法,wait()方法是Object类的成员方法
sleep()方法不会释放,wait()方法会释放锁
sleep和wait方法都可以使线程进入阻塞状态
sleep和wait方法都会抛出InterruptedException
定义两个线程操作同一个变量,一个线程对其加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();
}
}