Java线程基础
Posted Al_tair
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java线程基础相关的知识,希望对你有一定的参考价值。
线程基础
大家好呀!我是小笙!我学习了韩顺平老师的类和对象的知识,收获颇丰!现在来和大家分享笔记!
线程
进程和线程的概念
概念:进程是指运行中的程序,是程序的一次执行过程或是正在运行的一个程序。动态过程:产生,存在,消亡的过程
那么线程是什么?
线程由进程创建,是进程的一个实体,一个进程可以拥有多个线程
- 单线程:同一个时刻只允许一个线程
- 多线程:同一个时刻,可以执行多个线程
并发:同一个时刻,多个任务交替执行,单核cpu实现多任务
并行:同一个时刻,多个任务同时执行,多核cpu可以实现并行执行多任务
那我们为什么要用多线程而不是多进程呢?
线程间的切换和调度成本远小于进程
线程的生命周期
public enum State
// 创建进程,但是资源条件未满足
NEW,
// 运行进程
RUNNABLE,
// 阻塞进程
BLOCKED,
// 无时间限制等待notify()方法唤醒
WAITING,
// 有时间限制等待notify()方法唤醒
TIMED_WAITING,
// 结束进程
TERMINATED;
线程基本使用
创建线程的两种方式
-
继承Thread类,重写run方法(本质:Thread类也实现了Runable接口)
-
实现Runable接口,重写run方法
// 使用Thread构造接受实现了Runnable的类,可以调用start()方法 public Thread(Runnable target) this(null, target, "Thread-" + nextThreadNum(), 0);
源码解析多线程机制
多线程机制说明
用例代码
// 疑问:为什么调用start()方法而不是直接调用run()方法,不都是实现run()方法吗?
// 本质区别有没有创建新的线程,直接调用run方法就是和使用普通方法一样没什么区别,并没有创建线程
public class Thread01 extends Thread
int times = 0;
public static void main(String[] args) throws InterruptedException
Thread01 thread01 = new Thread01();
thread01.start();
for (int i = 0; i < 60; i++)
System.out.println(Thread.currentThread().getName()+i);
Thread.sleep(1000);
@Override
public void run()
while(true)
try
Thread.sleep(1000);
catch (Exception e)
System.out.println(e.getMessage());
System.out.println("喵喵,我是小猫咪"+ ++times );
if(times == 80)
break;
使用Terminal – jconsole工具观察进程
注意要main方法和其他进程要持续较长时间。这样子才好观测
源码分析
// 调用线程start方法:thread01.start();
// 源码分析
public synchronized void start()
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try
start0();
started = true;
finally
try
if (!started)
group.threadStartFailed(this);
catch (Throwable ignore)
// 本地方法,开辟线程
private native void start0();
进程终止
stop()方法(不推荐)
为什么stop()方法被废弃而不推荐使用呢?
因为stop()方法太过于暴力,强行把执行到一半的程序强行退出,会导致数据不一致的问腿
自制设置标志位退出
public class StopThread
public static void main(String[] args) throws InterruptedException
Thread1 thread1 = new Thread1();
thread1.start();
Thread.sleep(10000);
thread1.setFlag(false);
class Thread1 extends Thread
private int count = 0;
// 设置标志位来判断线程终止时间
private boolean flag = true;
@Override
public void run()
while (flag)
try
Thread.sleep(50);
catch (InterruptedException e)
e.printStackTrace();
System.out.println(++count);
public void setFlag(boolean flag)
this.flag = flag;
中断方式退出程序
中断方式类似于之前通过标志位方式退出线程的方法,但是中断更加强劲一些,它可以让等待在sleep或者wait的线程引发异常退出
public class InterruptThread
public static void main(String[] args) throws InterruptedException
Interrupt thread1 = new Interrupt();
thread1.start();
Thread.sleep(10000);
thread1.interrupt();
class Interrupt extends Thread
private int count = 0;
private boolean flag = true;
@Override
public void run()
while (true)
if(this.isInterrupted())
System.out.println("Interrupted");
break;
try
Thread.sleep(50);
catch (InterruptedException e)
e.printStackTrace();
// 为什么这里还需要中断一次
// 因为sleep()方法中断抛出的异常会清除中断标志位,因此还需要再中断一次
this.interrupt();
System.out.println(++count);
线程的常用方法
等待wait()和通知notify()
有些人会好奇,wait和notify方法不是Object类的方法吗,为什么放在线程这里特别拿出来讲?
因为这两个方法平时并不能随便调用,它必须包含在对应的同步块中
public final void wait() throws InterruptedException
wait(0L);
// 当多个线程在等待,则随机通知其中一个等待线程
public final native void notify();
// 通知所有等待线程
public final native void notifyAll();
Object.wait()方法和Thread.sleep()方法的区别
-
wait()方法可以被唤醒,使用wait方法之后会释放目标对象的锁
-
sleep()方法不会释放任何的资源
等待线程结束join()
join()方法:线程的插队,如果插队的线程一旦插入成功,则肯定先执行完插入的线程的所有任务、
// 无线等待,直到目标线程的任务执行完成
public final void join() throws InterruptedException
// 给出一个最大的等待时间
public final synchronized void join(long millis, int nanos) throws InterruptedException
示例代码
public class Join
public static void main(String[] args) throws InterruptedException
Thread3 t = new Thread3();
t.start();
for (int i = 1; i <= 20; i++)
if(i == 5)
System.out.println("让Thread3先完成");
t.join();
System.out.println("main继续执行");
System.out.println("main: " + i);
Thread.sleep(1000);
class Thread3 extends Thread
private int count;
@Override
public void run()
while(true)
if(count++ == 20)
System.out.println("Thread3结束了");
break;
try
Thread.sleep(1000);
catch (InterruptedException e)
e.printStackTrace();
System.out.println("Thread3: "+count);
join()方法的底层源码
// join方法的本质就是调用wait方法在当前对象实例进行等待
// 被等待的线程会在执行完成后调用notifyAll()方法唤醒等待的进程进程
public final synchronized void join(final long millis)
throws InterruptedException
if (millis > 0)
if (isAlive())
final long startTime = System.nanoTime();
long delay = millis;
do
wait(delay);
while (isAlive() && (delay = millis -
TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)) > 0);
else if (millis == 0)
// 测试此线程是否存在。如果线程已启动且尚未死亡,则该线程处于活动状态 RUNNABLE状态
while (isAlive())
wait(0);
else
throw new IllegalArgumentException("timeout value is negative");
谦让yeild()
yeild():线程的礼让,让出cpu让其他进程执行,但是礼让的时间不确定,也不一定礼让成功,还有就是当前处理器是否忙碌,如果处理器完成处理的过来,就不会进行礼让
使用场景:当你觉得这个线程不重要或者优先级很低,那适当让出cpu给那些更重要的线程是否是一个明智之举
用户线程和守护线程
用户线程:又称工作线程,当执行的任务执行完或通知方式结束
守护线程:一般是为工作线程服务,当所有线程结束,守护线程自动结束(比如:垃圾回收机制)
public class ThreadMethod
public static void main(String[] args)
MyDaemonThread md = new MyDaemonThread();
// 设置为守护线程
md.setDaemon(true);
md.start();
for (int i = 0; i < 10; i++)
try
Thread.sleep(1000);
catch (InterruptedException e)
e.getMessage();
System.out.println("用户线程在此");
class MyDaemonThread extends Thread
@Override
public void run()
while(true)
try
Thread.sleep(1000);
catch (InterruptedException e)
e.getMessage();
System.out.println("守护线程在此");
线程同步机制
同步概念:当有多个线程同时在对内存进行操作,在某一个时刻只允许一个线程对该内存进行操作(比如:写操作)
关键字synchronized的作用是实现线程的同步,它的工作是对同步代码枷锁,使得每一次只能有一个线程进入同步块,从而保证了线程的安全
关键字synchronized的用法
// 指定锁对象 默认锁对象就是this
synchronized(对象) // 需要得到对象的锁,才能操作同步代码
// 直接作用于实例方法 默认锁对象就是this
public synchronized void method()
// 直接作用于静态方法 默认锁对象就是当前类.class
public static synchronized void method()
public class increase01 implements Runnable
static int count = 0;
static int count2 = 0;
public static synchronized void increase()
count++;
public synchronized void increase2()
count2++;
public static void main(String[] args) throws InterruptedException
// 如果同一类传入的对象不同,对象锁就无法启到作用了,必须使用类的锁才可以锁住
Thread t1 = new Thread(new increase01());
Thread t2 = new Thread(new increase01());
t1.start();t2.start();
t1.join(); t2.join();
System.out.println(count); // 20000000
System.out.println(count2); // 小于20000000
i
System.out.println("-------");
increase01.count2 = 0;
increase01.count = 0;
// 传入了相同对象,就不需要使用静态锁,对象锁就可以实现
increase01 inc = new increase01();
Thread thread = new Thread(inc);
Thread thread1 = new Thread(inc);
thread.start(); thread1.start();
thread.join(); thread1.join();
System.out.println(count); // 20000000
System.out.println(count2); // 20000000
@Override
public void run()
for (int i = 0; i < 10000000; i++)
increase();
释放锁
四种情况释放锁
- 当前线程的同步方法和同步代码块执行结束
- 当前的线程在同步代码块和同步方法中遇到break,return
- 当前线程在同步代码块中出现了未处理的Error或者Exception导致被迫退出
- 当前的线程在同步代码块或者同步方法中执行了wait房啊,暂停当前的线程同时释放资源
二种情况不释放锁
- 线程执行同步代码块或者同步方法时,程序调用Thread.sleep()和Thread.yield()方法不会释放锁
- 线程执行同步代码块或者同步方法时,其他线程调用suspend方法将它挂起,此时它并不会释放该锁(不推荐使用挂起方法)
编程题
1.在main方法中启动两个线程,在第一个线程循环随机打印100 以内的整数,直到第二个线程从键盘中读取了‘Q’命令就终止了第一个线程
public class Homework01
public static void main(String[] args) throws InterruptedException
RandomNum randomNum = new RandomNum();
Thread thread = new Thread(randomNum);
Thread thread2 = new Thread(new Input(randomNum));
thread.start();
thread2.start();
// 线程1
class RandomNum implements Runnable
private boolean loop = true;
public void setLoop(boolean loop)
this.loop = loop;
@Override
public void run()
while(loop)
System.out.println((int)(Math.random()*100));
try
Thread.sleep(1000);
catch (InterruptedException e)
e.getMessage();
System.out.println("RandomNum退出程序");
// 线程2
class Input implements Runnable
private RandomNum rJava多线程基础2