学习Java的多线程知识之前,我们先来了解一下进程和线程的概念,以及他们之间的关系。
进程
基本概念
进程是具有独立功能的程序在某个数据集合上的一次执行过程。
特点
- 进程是操作系统进行资源分配的基本单位。
- 每个进程都有自己的地址空间,即进程空间。
线程
基本概念
一个进程内部的一个执行单元,它是程序中的一个单一的顺序控制流程。
特点
- 自己不拥有系统资源,只拥有一点儿在运行过程中必不可少的资源。与同一个进程下的其他所有线程共享进程所拥有的全部资源。
- 线程是进程中的一个实体,是被操作系统独立调度的基本单位。
进程和线程的联系和区别
进程是资源分配的基本单位,线程是调度的基本单位。进程包含线程,线程共用进程的资源。
Java实现多线程
有四种方式:
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口,并与FutureTask结合使用
- 线程池
继承Thread类
package com.cc.thread;public class ThreadDemo extends Thread{public ThreadDemo(String name) {super(name);}@Overridepublic void run() {for (int i = 0; i < 10000; i++) {System.out.println(this.getName()+i);}}public static void main(String[] args) {new ThreadDemo("张三").start();new ThreadDemo("李四").start();}}
实现Runnable接口
package com.cc.thread;public class RunableDemo implements Runnable {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}public RunableDemo(String name) {super();this.name = name;}@Overridepublic void run() {for (int i = 0; i < 10000; i++) {System.out.println(this.getName()+i);}}public static void main(String[] args) {new Thread(new RunableDemo("张三")).start();new Thread(new RunableDemo("李四")).start();}}
实现Callable接口,并与FutureTask结合使用
package com.cc.thread;import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;public class CallableDemo {public static void main(String[] args) {CallableImpl callableImpl = new CallableImpl();FutureTask<Integer> futureTask = new FutureTask<Integer>(callableImpl);Thread thread = new Thread(futureTask);thread.start();try {int sum = futureTask.get();System.out.println(sum);} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}}private static class CallableImpl implements Callable<Integer>{@Overridepublic Integer call() throws Exception {int sum = 0;for (int j = 0; j <= 100; j++) {System.out.println(Thread.currentThread().getName()+":"+j);sum += j;}return sum;}}}
运行上面的代码,我们发现每次返回的结果都是5050(这么巧的吗?难道每次call方法瞬间就执行完了,然后再执行的int sum = futureTask.get();),其实如果线程的call方法还未执行完毕,futureTask.get()方法会一直阻塞,直到call()方法执行完毕才能取到返回值。
线程的生命周期
通过查看Thread内部类State的源码,我们知道线程有6中状态,分别是:
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
Thread内部类State的源码
public enum State {/*** Thread state for a thread which has not yet started.*/NEW,/*** Thread state for a runnable thread. A thread in the runnable* state is executing in the Java virtual machine but it may* be waiting for other resources from the operating system* such as processor.*/RUNNABLE,/*** Thread state for a thread blocked waiting for a monitor lock.* A thread in the blocked state is waiting for a monitor lock* to enter a synchronized block/method or* reenter a synchronized block/method after calling* {@link Object#wait() Object.wait}.*/BLOCKED,/*** Thread state for a waiting thread.* A thread is in the waiting state due to calling one of the* following methods:* <ul>* <li>{@link Object#wait() Object.wait} with no timeout</li>* <li>{@link #join() Thread.join} with no timeout</li>* <li>{@link LockSupport#park() LockSupport.park}</li>* </ul>** <p>A thread in the waiting state is waiting for another thread to* perform a particular action.** For example, a thread that has called <tt>Object.wait()</tt>* on an object is waiting for another thread to call* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on* that object. A thread that has called <tt>Thread.join()</tt>* is waiting for a specified thread to terminate.*/WAITING,/*** Thread state for a waiting thread with a specified waiting time.* A thread is in the timed waiting state due to calling one of* the following methods with a specified positive waiting time:* <ul>* <li>{@link #sleep Thread.sleep}</li>* <li>{@link Object#wait(long) Object.wait} with timeout</li>* <li>{@link #join(long) Thread.join} with timeout</li>* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>* </ul>*/TIMED_WAITING,/*** Thread state for a terminated thread.* The thread has completed execution.*/TERMINATED;}
关于线程状态的详细描述和他们之前的相互转换请参考4 Java线程的状态及主要转化方法 · 深入浅出Java多线程
线程方法总结
start | 使线程由新建状态进入就绪状态,只能调用一次,否则会报IllegalThreadStateException |
run | 线程执行体,由系统调用 |
isAlive | 新建和死亡状态会返回false,其他状态返回true |
interrupt | 设置线程中断状态为true |
interrupted | static方法,先返回当前线程中断状态,然后设置线程中断状态为false |
isInterrupted | 返回当前线程中断状态 |
sleep | static native方法,使线程进入阻塞状态(不会释放同步锁) |
currentThread | static方法,该方法返回当前正在使用CPU资源的线程 |
setPriority | final方法,设置线程的优先级,1~10 1最低 10最高 5是默认值 线程的优先级具有继承性,比如A线程启动B线程,则A和B的线程优先级是一样的 |
getPriority | final方法,获取线程优先级 |
setDaemon | 设置守护线程(也叫服务线程,用于为系统中的对象和线程提供服务,如果都是服务线程,那么JVM结束,垃圾回收线程是守护线程) |
isDaemon | 判断是否是守护线程 |
join | thread1.join();使当前线程进入阻塞状态,thread1线程执行完后,再唤醒当前线程 |
yeild | 使当前线程进入就绪状态 |
wait | Object类的方法,让当前线程进入阻塞状态,并且释放它持有的同步锁 |
notify | Object类的方法,唤醒一个阻塞状态的线程 |
notifyAll | Object类的方法,唤醒所有阻塞状态的线程 |
join方法的使用
package com.cc.thread;public class ThreadJoin {public static void main(String[] args) throws InterruptedException {Thread thread1 = new Thread("线程1"){@Overridepublic void run() {for (int i = 0; i < 1000; i++) {System.out.println(Thread.currentThread().getName()+":"+i);}}};thread1.start();thread1.join();for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+":"+i);}}}
interrupt、interrupted和isInterrupted的作用和区别
- interrupt设置线程中断状态为true
- interrupted先返回当前线程中断状态,然后设置线程中断状态为false
- isInterrupted返回当前线程中断状态
- 下面的demo只用到了sleep方法,wait,join方法类似于sleep
package com.cc.thread;public class InterruptTest {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(){@Overridepublic void run() {for (int i = 0; i < 5; i++) {try {sleep(2000);//线程默认中断状态为false,调用sleep方法,或者在sleep的过程中,当中断状态为true时,执行sleep会抛出异常interrupt();//设置中断状态为true} catch (InterruptedException e) {//抛出异常后中断状态自动设置为falseSystem.out.println("出现中断异常,中断状态:"+isInterrupted());}System.out.println("正常执行,中断状态:"+isInterrupted());}}};thread.start();}}
package com.cc.thread;public class InterruptTest {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(){@Overridepublic void run() {for (int i = 0; i < 5; i++) {try {sleep(2000);//线程默认中断状态为false,调用sleep方法,或者在sleep的过程中,当中断状态为true时,执行sleep会抛出异常interrupt();//设置中断状态为trueSystem.out.println("interrupted方法:"+interrupted());//此函数首先返回当前的中断状态,然后将中断状态置为falseSystem.out.println("isInterrupted:"+isInterrupted());} catch (InterruptedException e) {//抛出异常后中断状态自动设置为falseSystem.out.println("出现中断异常,中断状态:"+isInterrupted());}System.out.println("正常执行,中断状态:"+isInterrupted());}}};thread.start();}}
wait方法的使用
package com.cc.thread;public class ObjectWait {public static void main(String[] args) {try {Thread threadTest = new Thread(){public void run(){System.out.println("执行线程中方法");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}};threadTest.start();synchronized(threadTest){threadTest.wait(); //当线程终止的时候,会调用线程自身的notifyAll()方法}System.out.println("执行到了这里");} catch (InterruptedException e) {e.printStackTrace();}}
}
synchronized
synchronized的用法参考Java中使用同步关键字synchronized需要注意的问题 - @ 小浩 - 博客园
synchronized是非公平锁
synchronized的实现原理参考Java中的锁——Lock和synchronized - 夏末秋涼 - 博客园
1,synchronized代码块基于进入和退出monitor对象实现。代码编译后将monitorenter指令插入同步代码块的前面,monitorexit指令插入同步代码块的后面,发生异常时也会执行monitorexit指令
2,synchronized方法读取运行时常量池中方法的 ACC_SYNCHRONIZED 标志来隐式实现的
3,synchronized用的锁存储在对象头中的markword,markword中的锁标记指向的是monitor对象,
锁标记位(无锁01、轻量级锁00、重量级锁10、偏向锁01)
4,monitor对象有两个队列(EntryList、WaitSet)以及锁持有者Owner标记
线程安全的概念
什么是线程安全:在多线程环境下,线程安全的代码会通过线程同步机制保证各个线程可以正常的正确的执行,不会出现数据污染等意外情况。
什么是线程同步:是指各个线程按照一定的顺序执行