Java线程基础回顾
概念
- 程序(Program)
- 完成特定任务,用某种语言编写的一组指令的集合.简单来说:就是我们的代码.
- 进程
- 进程指运行中的程序,比如我们使用的QQ,就启动了一个进程,操作系统就会为该进程分配内存空间.
- 进程是程序的一次执行过程,或是正在运行的一个程序.是动态过程:有它滋生的生产,存在和消亡的过程.
- 线程
- 线程是由进程创建,是进程的一个实体
- 一个进程可以拥有多个线程
- **单线程:**同一时刻,只允许执行一个线程
- **多线程:**同一时刻,可以执行多个线程
- 并发:同一时刻,多个任务交替执行,造成一种"貌似同时"的错觉,简单来说,单核CPU实现的多任务就是并发
- **并行:**同一时刻,多个任务同时执行,多核CPU可以实现并行
- 宏观并行,微观并发.
线程常用方法
| 方法 | 描述 |
|---|---|
| setName | 设置线程名称,使之与参数name相同 |
| getName | 返回该线程名称 |
| start | 使该线程开始执行;Java虚拟机底层调用start0()方法 |
| run | 调用线程对象run方法 |
| setPriority | 设置线程优先级 ( MIN 1 NORM 5 MAX 10) |
| getPriority | 返回线程优先级 |
| sleep | 休眠 |
| interrupt | 中断线程 (不是终止) |
package tech.aowu.ThreadMethod;
public class ThreadMethod01 {
public static void main(String[] args) throws InterruptedException {
T t = new T();
t.setName("Aowu");
t.setPriority(Thread.MIN_PRIORITY);
t.start();
for (int i = 0; i < 5; i++) {
Thread.sleep(1000);
System.out.println(i);
}
t.interrupt();//当执行到这里 就会中断t线程的休眠
}
}
class T extends Thread{
@Override
public void run() {
while (true){
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+"吃包子... "+ i);
}
try {
System.out.println(Thread.currentThread().getName()+"休眠中...");
Thread.sleep(20000);
} catch (InterruptedException e) {
//当该线程执行到一个interrupt方法时, 就会catch一个异常,可以加入自己的业务代码
//捕获到InterruptedException 执行以下
System.out.println(Thread.currentThread().getName()+"被中止...");
}
}
}
}
| 方法 | 描述 |
|---|---|
| yield | 线程的礼让.让出cpu让其他线程执行,但礼让的时间不确定,也不一定成功 具体情况看CPU |
| join | 线程的插队.插队的线程一旦插入成功.则一定先执行完插入的线程所有的任务 |
package tech.aowu.ThreadMethod;
public class ThreadMethod02 {
public static void main(String[] args) throws InterruptedException {
T2 t2 = new T2();
t2.start();
for (int i = 1; i <= 20; i++) {
Thread.sleep(1000);
System.out.println("主线程吃了 "+i);
if (i==5){
System.out.println("主线程让子线程先吃");
t2.join();
System.out.println("子线程吃完");
}
}
}
}
class T2 extends Thread{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Join Thread ...... 子线程吃了: "+i);
}
}
}
用户线程和守护线程
- 用户线程:也叫工作线程
- 守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
- 常见的守护线程:垃圾回收机制
package tech.aowu.ThreadMethod;
public class ThreadMethod03 {
public static void main(String[] args) throws InterruptedException {
T3 t3 = new T3();
//希望当main线程结束后,子线程也可以结束
//只需将子线程设置为守护线程
t3.setDaemon(true);
t3.start();
for (int i = 0; i < 5; i++) {
System.out.println("main......");
Thread.sleep(1000);
}
}
}
class T3 extends Thread{
@Override
public void run() {
while (true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("子线程..........");
}
}
}
创建线程

继承Thread类,重写run方法
start()方法启动线程run()方法则只是调用了run方法start()方法调用start0()方法后,该线程并不一定会立马执行,只是将线程改编成可运行状态.具体什么时候执行取决于CPU,由CPU统一调度
public class Thread01 {
public static void main(String[] args) {
Cat cat1 = new Cat();
/*
* public synchronized void start() {
* start0();
* }
*
* private native void start0();
* //start0()是本地方法,由JVM调用 底层是C/C++实现
* //真正实现start0()方法,而不是run
* */
cat1.start();//启动线程
// cat1.run();//单纯调用run方法
//当main线程启动一个子线程Thread-1 主线程不会阻塞,回继续执行
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"主线程 i="+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//当一个类继承了Thread类,该类就可以当作一个线程
//重写run方法
//run Thread类实现了Runnable接口的run方法
class Cat extends Thread{
int times=0;
@Override
public void run() {
while (true){
System.out.println(Thread.currentThread().getName()+" 回顾Thread"+times++);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (times==8) break;
}
}


实现Runnable接口,重写run方法
-
Java是单继承,在某些情况下一个类可能已经继承了父类,这时在用继承Thread类来创建线程显然是不可能了,由此,我们还可以通过实现Runnable接口来创建进程
public class Thread02 {
public static void main(String[] args) {
Dog dog = new Dog();
Thread dogThread = new Thread(dog);//由于Runnable接口没有start方法所以不能直接调用 这里使用了设计模式(代理模式)
dogThread.start();
for (int i = 0; i < 10; i++) {
System.out.println("100");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class ThreadProxy implements Runnable{ //模拟实现代理模式 可以把ThreadProxy当作Thread
private Runnable target=null;//属性,类型Runnable
public ThreadProxy(Runnable target) {
this.target = target;
}
public void start(){
start0();//实现多线程的方法
}
private void start0() {//只是模拟了Thread类里的start0方法
run();
}
@Override
public void run() {
if (target!=null){
target.run();
}
}
}
class Dog implements Runnable{//实现Runnable接口
int times=0;
@Override
public void run() {
while (true){
System.out.println(Thread.currentThread().getName()+" Wang"+times++);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
实现Callable接口,重写call方法(没用过)
线程终止
- 线程完成任务,自动退出
- 通过使用变量来控制run方法退出,即 通知
package tech.aowu.ThreadExit;
public class ThreadExit {
public static void main(String[] args) throws InterruptedException {
T t1 = new T();
t1.start();
//希望main线程控制t1线程终止,必须可以修改loop变量
//让t1 退出run方法 从而终止 t1线程 -> 通知方式
System.out.println("主线程休眠3s");
Thread.sleep(3000);
t1.setLoop(false);
}
}
class T extends Thread{
private int count=0;
//设置一个控制变量
private boolean loop=true;
public void setLoop(boolean loop) {this.loop = loop;}
@Override
public void run() {
while (loop){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" is still running... nums: "+count++);
}
}
}
线程生命周期

| 状态 | 描述 |
|---|---|
| NEW | 尚未启动的线程 |
| RUNNABLE | 在JVM中执行的线程,Runnable可细分Ready和Running状态 |
| BLOCKED | 被阻塞等待监视器锁定的线程 |
| WAITING | 正在等待另一个线程执行特定动作的线程 |
| TIMED_WAITING | 正在等待另一个线程执行动作打到指定等待时间的线程 |
| TERMINATED | 已退出的线程 |
package tech.aowu.ThreadState;
public class ThreadState {
public static void main(String[] args) throws InterruptedException {
T t = new T();
System.out.println(t.getName()+" State: "+t.getState());
t.start();
while (Thread.State.TERMINATED!=t.getState()){
System.out.println(t.getName()+" State: "+t.getState());
Thread.sleep(500);
}
System.out.println(t.getName()+" State: "+t.getState());
}
}
class T extends Thread{
@Override
public void run() {
while (true){
for (int i = 0; i < 3; i++) {
System.out.println("i="+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
}
}
}

线程同步 Synchronized
- 在多线程编程,一些敏感数据不允许被多个线程同时访问,此时使用同步访问技术,保证数据在任何同一时刻,最多有一个线程访问,以保证数据的完整性.
- 线程同步:当一个线程在对内存操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才可以对该内存地址进行操作
实现同步:
-
同步代码块
synchronized(对象){//得到对象的锁,才能够同步代码 //需要被同步的代码 } -
Synchronized还可放在方法声明中,表示整个方法为同步方法
public synchronized void method(String param){ //需要被同步的代码 }
互斥锁
- Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性
- 每个对象都对应于一个可成为"互斥锁"的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象
- 关键字
synchronized来与对象的互斥锁联系.当某个对象用synchronized修饰时,表明该对象在任一时刻只能由一个线程访问 - 同步的局限性:导致程序的执行效率降低
- 同步方法(非静态)的锁可以是this,也可以是其他对象(要求是同一对象)
- 同步方法(静态的)的锁为当前类本身
注意
- 同步方法如果没有使用static修饰:默认锁对象为this
- 如果方法使用static修饰,默认锁对象:当前类.class
- 实现的步骤
- 需要先分析上锁的代码
- 选择同步代码块或同步方法
- 要求多个线程的锁对象为同一个即可
卖票问题
- 同步方法
public synchronized void func(){}这时锁加在this对象 与synchronized (this){}类似 - 同步代码块
synchronized (object){}锁加在object对象 - 静态同步方法的锁为当前类本身
public synchronized static void func(){}锁加在currclass.class - 静态方法中的同步代码块锁加载当前类本身
public static void func(){synchronized (currclass.class){}}- 原因分析:静态方法是类方法,没有实例对象就已经加载,所以无法使用this
package tech.aowu.ticket;
//使用多线程模拟10个窗口同时售票100张
public class SellTicket {
public static void main(String[] args) {
SellTicket03 sellTicket03 = new SellTicket03();
for (int i = 0; i < 10; i++) {new Thread(sellTicket03).start();}
}
}
class SellTicket04 implements Runnable{
private int ticketNum=30;//让多个线程共享ticketNum
private boolean loop=true;
Object object= new Object();
public void sell(){
synchronized (object){ //同步代码块 加锁在this对象
if (ticketNum <= 0) {
System.out.println("售罄...");
loop = false;
return;
}
System.out.println("窗口:" + Thread.currentThread().getName() + "售出一张票" + "剩余票数:" + (--ticketNum));
//休眠50ms
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public void run() {
while (loop){
sell();
}
}
}
//实现接口,使用同步方法synchronized实现线程同步
class SellTicket03 implements Runnable{
private int ticketNum=100;//让多个线程共享ticketNum
private boolean loop=true;
public synchronized void sell(){//方法加锁 同步方法 这时锁加在this对象 也可在代码块写synchronized,同步代码块
if (ticketNum<=0){
System.out.println("售罄...");
loop=false;
return;
}
System.out.println("窗口:"+Thread.currentThread().getName()+"售出一张票"+"剩余票数:"+(--ticketNum));
//休眠50ms
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while (loop){
sell();
}
}
}
//Thread方式
class SellTicket01 extends Thread{
private static int ticketNum=100;//让多个线程共享ticketNum
@Override
public void run() {
while (true){
if (ticketNum<=0){
System.out.println("售罄...");
break;
}
//休眠50ms
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("窗口:"+Thread.currentThread().getName()+"售出一张票"+"剩余票数:"+(--ticketNum));
}
}
}
}
线程死锁
多个线程都占有了对方的锁资源,都不愿意释放,导致死锁.
package tech.aowu.ticket;
public class DeadLock {
public static void main(String[] args) {
DeadLockDemo deadLockDemo = new DeadLockDemo(true);
deadLockDemo.start();
DeadLockDemo deadLockDemo2 = new DeadLockDemo(false);
deadLockDemo2.start();
}
}
class DeadLockDemo extends Thread{
static Object object1=new Object();
static Object object2=new Object();
boolean flag;
public DeadLockDemo(boolean flag){
this.flag=flag;
}
@Override
public void run() {
//如果flag为true 线程A就会先得到object1对象锁 然后尝试获取object2对象锁
//如果线程A得不到object2锁 就会进入blocked状态
//如果flag为false 线程B就会现持有object2对象锁 然后尝试获取object1对象锁
//如果线程B得不到object1对象锁 就会Blocked
if (flag){
synchronized (object1){
System.out.println(Thread.currentThread().getName()+" 进入1");
synchronized (object2){
System.out.println(Thread.currentThread().getName()+" 进入2");
}
}
}else {
synchronized (object2){
System.out.println(Thread.currentThread().getName()+" 进入3");
synchronized (object1){
System.out.println(Thread.currentThread().getName()+" 进入4");
}
}
}
}
}
释放锁
什么时候会释放锁?
- 当前线程的同步方法,同步代码块执行结束.
- 当前线程在同步方法,同步代码块中遇到break,return
- 当前线程在同步方法,同步代码块中出现了未处理的Error或Exception,导致异常结束.
- 当前线程在同步代码块,同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁.
以下操作不会释放锁!!
- 线程执行同步代码块或同步方法时,程序调用了Thread.sleep(),Thread.yield()方法暂停当前线程的执行,不会释放锁.
- 线程执行同步代码块时,其他线程调用了该线程的suspend()方法将线程挂起,该线程不会释放锁,
- 注意!! 尽量避免使用suspend()和resume()方法来控制线程