Java Thread
进程:CPU为每个应用程序分配的独立空间,一个进程可能有多个Thread。进程为Thread中的摸个Execute任务程序,多个进程之间可以进行共享Data。而JAVA的Thread则是由JVM进程分配的,mainMethod所在Thread,则成为主Thread。
〇、线程状态
正常情况ThreadExecute步骤:新建-》Run-》死亡
当CPU资源不够时,CUP分配给各个Thread的资源可能不同(貌似有点像是Thread在抢资源,实际是CPU分配资源给每个Thread)。 因此就会出现Thread的阻塞、休眠、等待3个状态。其中阻塞状态,当cpu资源够时,阻塞状态的Thread可能恢复到Run状态,而休眠、等待的Thread也可能进入Run状态。休眠、等待状态可能转换成阻塞状态,但是阻塞状态不会变成休眠、等待状态。

一、多线程的实现
0、线程常用方法
package learn.javase.threads;
/**
* 继承Thread创建线程,设置线程名称、获取线程名称
* @author Jole
* */
public class ThreadDemo01 {
public static void main(String[] args) {
MyThreadsDemo mt = new MyThreadsDemo();
mt.setName("Hi");
mt.start();
//获取当前线程
Thread t = Thread.currentThread();
System.out.println(t.getName());
}
}
ThreadImplementation Method主Test类:
package learn.javase.threads;
public class MyThreadsDemo extends Thread{
public MyThreadsDemo() {
super("Google");
}
public void run() {
for(int i=0;i<5;i++) {
try {
Thread.sleep(1000);
System.out.println(i);
}catch(Exception e) {
System.out.println(e.getMessage());
}
}
}
}
ThreadTest主类:
package learn.javase.threads;
/**
* 实现多线程的4中方式:
* 1、继承Thread类
* 2、实现Runnable接口
* 3、匿名内部类
* 4、匿名接口
* 5、实现Callable接口,与Runnable的区别:
* 可以有返回值
* 可以抛异常
* @author Jole
* */
public class ThreadNumberDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
//方式1,继承Thread
new ThreadThread1().start();
//方式2,实现Runnable接口
new Thread(new ThreadRunnables2()).start();
//方式3,匿名内部类
new Thread() {
public void run() {
System.out.println("匿名内部类,实现多线程");
}
}.start();
//方式4,匿名接口
Runnable r = new Runnable() {
public void run() {
System.out.println("匿名接口,实现多线程");
}
};
new Thread(r).start();
//方式4的简洁版
new Thread(new Runnable() {
public void run() {
System.out.println("匿名接口简洁版,实现多线程");
}
}).start();
}
}
1、继承Thread
package learn.javase.threads;
/**
* 方式一继承Thread实现多线程
* @author Jole
* */
public class ThreadThread1 extends Thread{
public void run() {
System.out.println("extends ....thread");
}
}
2、实现Runnable接口
package learn.javase.threads;
/**
* 实现Runnable接口
* @author Jole
* */
public class ThreadRunnables2 implements Runnable{
public void run() {
System.out.println("runnable .. threads");
}
}
3、实现Callable
通过UsageThread Pool,ImplementationCallable
4、区别
继承ThreadInterface与ImplementationRunnableInterface,Implementation多Thread区别:
- 1、单继承、多Implementation
- 2、ImplementationRunnable可以将Thread与任务(run())解耦
package learn.javase.threads;
/**
* 实现Runnable接口,创建线程。
* 继承Thread接口与实现Runnable接口,实现多线程区别:
* 1、单继承、多实现
* 2、将线程与任务(run())解耦
* @author Jole
* */
public class RunnableDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
new Thread(new RunnableThreadDemo()).start();
for(int i=0;i<5;i++) {
System.out.println("main..."+i);
}
}
}
ThreadImplementation类:
package learn.javase.threads;
public class RunnableThreadDemo implements Runnable{
public void run() {
for(int i=0;i<5;i++) {
System.out.println("run..."+i);
}
}
}
二、线程池
1、线程池使用
package learn.javase.threads;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 线程池
* @author Jole
* */
public class ThreadPoolDemo {
public static void main(String[] args) {
//使用线程池工场Executors,创建线程池
ExecutorService es = Executors.newFixedThreadPool(2);
//使用线程池,构造器为实现Runnable的接口,使用完后自动放回线程池
es.submit(new ThreadPoolThreas());
es.submit(new ThreadPoolThreas());
//如果线程池里面线程不够了,只有等待其它线程执行完后,在使用
es.submit(new ThreadPoolThreas());
//一般不用关闭线程池,特殊情况关闭线程池,可以使用
// es.shutdown();
}
}
Thread PoolThreadImplementationRunnableInterface:
package learn.javase.threads;
public class ThreadPoolThreas implements Runnable{
public void run() {
System.out.println(Thread.currentThread().getName()+"线程池的使用");
}
}
2、线程池-实现多线程
package learn.javase.threads;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* 通过线程池,实现Callable接口,实现多线程,并获取任务(run)返回值,和异常
* @author Jole
* */
public class ThreadPoolDemo2 {
public static void main(String[] args) throws Exception{
//使用线程池工场Executors创建线程池工场
ExecutorService es = Executors.newFixedThreadPool(2);
//通过实现Callable实现线程,提交并返回Future对象
Future t = es.submit(new ThreadCallableDemo());
//通过get获取返回值
System.out.println(t.get());
}
}
Implementation类Callable
package learn.javase.threads;
import java.util.concurrent.Callable;
/**
* * @author Jole
* 使用继承Callable<T>泛型接口,实现多线程,并使用线程池
*/
public class ThreadCallableDemo implements Callable<String>{
public String call() {
return "实现Callable接口";
}
}
3、实例
Example1
package learn.javase.threads;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* 使用线程池实现异步提交求和:1+...100= 1+... 200=
* 注意:
* 有返回值
* 计算不同的值
* @author Jole
* */
public class ThreadMainDemo1 {
public static void main(String[] args) throws Exception{
//使用线程池工场Executors创建线程池
ExecutorService es = Executors.newFixedThreadPool(3);
//使用线程池中的线程,并使用构造方法传参,求和并返回
Future<Integer> f =es.submit(new CallableDemo(100));
Future<Integer> f2 =es.submit(new CallableDemo(2100));
System.out.println("1+...100="+f.get());
System.out.println("1+...200="+f2.get());
//关闭线程池
es.shutdown();
}
}
Implementation类:
package learn.javase.threads;
import java.util.concurrent.Callable;
/**
* 通过实现Callable接口,及构造方法传参,并通过call返回值
* @author Jole
* */
public class CallableDemo implements Callable<Integer>{
private int number;
//创建构造方法,为了传参
public CallableDemo(int number) {
this.number = number;
}
public Integer call() {
int sum = 0;
//使用构造方法传的参数求和并返回
for(int i=0;i<=number;i++) {
sum=sum+i;
}
return sum;
}
}
Example2:
package learn.javase.threads;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* 通过线程池,实现Callable接口,实现多线程,并获取任务(run)返回值,和异常
* @author Jole
* */
public class ThreadPoolDemo2 {
public static void main(String[] args) throws Exception{
//使用线程池工场Executors创建线程池工场
ExecutorService es = Executors.newFixedThreadPool(2);
//通过实现Callable实现线程,提交并返回Future对象
Future t = es.submit(new ThreadCallableDemo());
//通过get获取返回值
System.out.println(t.get());
}
}
Implementation类:
package learn.javase.threads;
import java.util.concurrent.Callable;
/**
* * @author Jole
* 使用继承Callable<T>泛型接口,实现多线程,并使用线程池
*/
public class ThreadCallableDemo implements Callable<String>{
public String call() {
return "实现Callable接口";
}
}
三、线程安全
1、线程安全
package learn.javase.threads;
/**
* 可能出现安全问题的情况:多线程,共享一个数据
* 线程安全问题,及解决办法加synchronized,实例:卖票
* @author Jole
* synchronized解决线程安全问题,使用公式:
* synchronized(锁(任意对象)){
* 代码块
* }
* * 注意:因为加了锁,所以效率会降低,但是安全性得到了保证,效率低的原因如下:
* 1、每次线程都会先判断是否有锁
* 2、获取锁
* 3、执行完代码块
* 4、执行完后,还会锁
* 5、在等下一个锁执行相同操作,相当于只能排队上一个厕所。
*/
public class ThreadSeaerfDemo {
public static void main(String[] args) {
//多线程出现的安全问题,票有负数还在卖
// Tickets00 t = new Tickets00();
// Thread t0 = new Thread(t);
// Thread t1 = new Thread(t);
// Thread t2 = new Thread(t);
// t0.start();t1.start();t2.start();
//原始代码
// Tickets01 t = new Tickets01();
// Thread t0 = new Thread(t);
// Thread t1 = new Thread(t);
// Thread t2 = new Thread(t);
// t0.start();t1.start();t2.start();
//第一次优化代码:同步方法,同步锁对象为:this
// Tickets02 t = new Tickets02();
// Thread t0 = new Thread(t);
// Thread t1 = new Thread(t);
// Thread t2 = new Thread(t);
// t0.start();t1.start();t2.start();
//第一次优化代码:同步静态方法,同步锁对象为:类名.class
Tickets03 t = new Tickets03();
Thread t0 = new Thread(t);
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t0.start();t1.start();t2.start();
}
}
多线Thread出现的Problem
package learn.javase.threads;
/** 〇、原始多线程,会出现安全问题,会出现票卖完了还在卖,也就是会出现票为负数的情况
* @author Jole
* */
public class Tickets00 implements Runnable{
//票的数量
private int ticket = 100;
public void run() {
while(true) {
if(ticket>0) {
//为了展示出现线程安全问题,此处让线程修改20毫秒
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
//每次票减1
System.out.println(Thread.currentThread().getName()+ " " + ticket--);
}
}
}
}
2、线程安全解决方案
解决多ThreadProblem方案UsageSynchronizationLock,1、Usagesynchronized 2、UsageLockInterface
SynchronizationLockImplementation Principle图:

1、synchronized
Usagesynchronized解决ThreadSafetyProblem,Usage公式:
synchronized解决线程安全问题,使用公式:
synchronized(锁(任意对象)){
代码块
}
1.1、synchronized加在外面:SynchronizationLock对象为obj
package learn.javase.threads;
/** 一、原始线程同步解决,同步锁为obj
* * synchronized解决线程安全问题,使用公式:
* synchronized(锁(任意对象)){
* 代码块
* }
* @author Jole
* */
public class Tickets01 implements Runnable{
//票的数量
private int ticket = 100;
Object obj = new Object();
public void run() {
while(true) {
//为了解决安全问题加入了synchronized关键字
synchronized (obj) {
if(ticket>0) {
//为了展示出现线程安全问题,此处让线程修改20毫秒
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
//每次票减1
System.out.println(Thread.currentThread().getName()+ " " + ticket--);
}
}
}
}
}
1.2、synchronized加在非静态Method上:此时SynchronizationLock对象为this
package learn.javase.threads;
/**
* 二、优化原始的线程安全解决办法,同步锁方法,同步锁对象为this
* @author Jole
* */
public class Tickets02 implements Runnable{
//总票数
private int tickets =100;
//原始同步锁
// Object obj = new Object();
public void run() {
bay();
}
//第一次优化,将锁和代码块抽为一个方法,测试同步锁为obj
// public void bay() {
// while(true) {
// synchronized (obj) {
// if(tickets > 0) {
// try {
// Thread.sleep(20);
// }catch(Exception e) {
// e.printStackTrace();
// }
// System.out.println(Thread.currentThread().getName()+" " +tickets-- );
// }
// }
// }
// }
//第二次进一步优化为,同步方法,此时同步锁为:this
//测试就可以省去以前的obj对象的创建了,代码以前更占用少一点内存了
public synchronized void bay() {
while(true) {
if(tickets > 0) {
try {
Thread.sleep(20);
}catch(Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" " + tickets--);
}
}
}
}
1.3、synchronized加在静态Method上:此时SynchronizationLock对象为:类名.class
package learn.javase.threads;
/**
* 静态同步方法,此时同步锁为:类名.class
* 如此类中的同步锁对象是:Tickets03.class
* @author Jole
* */
public class Tickets03 implements Runnable{
//总票数
private static int tickets =100;
public void run() {
buy();
}
//静态方法同步锁,此时代码的锁为:Tickets03.class
public static synchronized void buy() {
while(true) {
if(tickets > 0) {
try {
Thread.sleep(20);
}catch(Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" " + tickets--);
}
}
}
}
2、Lock
Usagesynchronized时,GetLock,释放Lock都是JVM自动完成的,而通过Lock可以手动GetLock,释放Lock。
package learn.javase.threads;
/**
* 手动获取锁,释放锁
* @author Jole
* */
public class ThreadShouDong {
public static void main(String[] args) {
ThreadRunnableDemo01 t = new ThreadRunnableDemo01();
Thread t0 = new Thread(t);
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t0.start();
t1.start();
t2.start();
}
}
LockInterfaceImplementation类:
package learn.javase.threads;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 手动获取锁,释放锁
* @author Jole
* */
public class ThreadRunnableDemo01 implements Runnable{
//票总数
private int tickets = 100;
//锁 Lock接口的实现类ReentrantLock
private Lock lock = new ReentrantLock();
public void run() {
while(true) {
//获取锁
lock.lock();
try {
if(tickets > 0) {
Thread.sleep(20);
System.out.println(Thread.currentThread().getName()+" "+tickets--);
}
}catch(Exception e) {
e.printStackTrace();
}finally {
//释放锁
lock.unlock();
}
}
}
}
3、注意点
Usage多Thread为了保证Data的Safety性,加了synchronized或Lock保持Synchronization,因此效率上就降低了。
可能出现SafetyProblem的情况:多Thread,共享一个Data。
Usagesynchronized时,SynchronizationLock必须保证为同一个SynchronizationLock对象才行。
四、死锁
Deadlock:多个Thread,对方互相等待Get对方的Lock。双方一直处于等待Get对方Lock对象状态,也就是Deadlock。
package learn.javase.threads;
/**
* 死锁:多个线程,对方互相等待获取对方的锁。双方一直处于等待获取对方锁对象状态,也就是死锁。
* @author Jole
* */
public class DealThreadMainDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
DetalThread death = new DetalThread();
Thread t0 = new Thread(death);
Thread t1 = new Thread(death);
t0.start();
t1.start();
}
}
SynchronizationLock对象A:
package learn.javase.threads;
/**
* 同步锁A,对象
* @author Jole
* */
public class LockA {
private LockA() {
};
public static final LockA lockA = new LockA();
}
SynchronizationLock对象B:
package learn.javase.threads;
/**
* 同步锁B,对象
* @author Jole
* */
public class LockB {
public LockB() {
}
public static final LockB lockB = new LockB();
}
模拟出现Deadlock情况,Implementation类:
package learn.javase.threads;
public class DetalThread implements Runnable{
private int number = 0;
public void run() {
while(true) {
if(number % 2 ==0) {
synchronized (LockA.lockA) {
System.out.println(Thread.currentThread().getName()+"线程,"+"第"+number+"次,"+"if抢到资源--获取到--同步锁A对象");
synchronized (LockB.lockB) {
System.out.println(Thread.currentThread().getName()+"线程,"+"第"+number+"次,"+"if抢到资源--获取到--同步锁B对象");
}
}
}else {
synchronized (LockB.lockB) {
System.out.println(Thread.currentThread().getName()+"线程,"+"第"+number+"次,"+"else抢到资源--获取到--同步锁B对象");
synchronized (LockA.lockA) {
System.out.println(Thread.currentThread().getName()+"线程,"+"第"+number+"次,"+"else抢到资源--获取到--同步锁A对象");
}
}
}
number++;
}
}
}
注意Get的Lock是那个。
五、线程等待、唤醒

Example:input拷贝Data对象,output输入对象:如果不是用Thread等待与唤醒,出现Problem:拷贝的Data太快,还来不及输出,可能出现输出的Data错乱。UsageThread等待和唤醒,可以控制拷贝一个后,拷贝Thread等待,然后输出Thread输出,输出后,输出Thread等待,然后拷贝Thread Wakeup,进行拷贝Data,依次循环。从而保证拷贝一个Data,输出一个Data。
package learn.javase.threads;
/**
* 线程等待、唤醒:
* 涉及同步锁是否是同一个锁,只有锁调用wait和notify才有效,不然抛出异常。
* 正常情况是交替出现,不会出现数据错乱情况。
* @author Jole
* 本类是主测试类
* 该实例:input拷贝数据对象,output输入对象:
* input可能出现拷贝对象拷贝很快,output输出对象很慢,导致output输出数据错乱,因此只能单个拷贝输出,且每次拷贝完后需wait等待,然后等输出完成后在拷贝。
* 因此需要拷贝完成后,等待,并唤醒输出,等输出完成前唤醒拷贝,然后自己在等待,然后拷贝又拷贝,然后又唤醒输出,如此循环。
* 实现方法加一个flag标记,是该拷贝还是输出
*/
public class WatiNotifyThreadMainDemo {
public static void main(String[] args) {
//操作的是同一个对象锁,不然拷贝和输出都是用自己的锁this,也会发生数据错乱
ResourceData resource = new ResourceData();
//保证是同一个锁对象,所以都写了构造方法传入同一个锁对象
InputDemo input = new InputDemo(resource);
OutputDemo output = new OutputDemo(resource);
Thread t0 = new Thread(input);
Thread t1 = new Thread(output);
t0.start();
t1.start();
}
}
要拷贝的Data类:
package learn.javase.threads;
/**
* 操作数据
* @author Jole
* */
public class ResourceData {
public String name;
public String sex;
public boolean flag;
}
拷贝Thread:
package learn.javase.threads;
/**
* 拷贝线程:
* 当标记flag=true时,拷贝完成,需等待
* @author Jole
* */
public class InputDemo implements Runnable {
private ResourceData resourceData;
public InputDemo(ResourceData resourceData) {
this.resourceData = resourceData;
}
@Override
public void run() {
int number=0;
while(true) {
synchronized (resourceData) {
//如果为ture拷贝完成,需等待
if(resourceData.flag) {
try {
resourceData.wait();
}catch(Exception e) {
e.printStackTrace();
}
}
//拷贝值,比如写死拷贝张三,30岁,lishi,20岁
if(number % 2==0) {
resourceData.name = "张三";
resourceData.sex = "男";
}else {
resourceData.name = "Lishi";
resourceData.sex = "nv";
}
//设置true,以便下次进入等待wait
resourceData.flag = true;
//唤醒输出线程
resourceData.notify();
}
number++;
}
}
}
输出Thread:
package learn.javase.threads;
/**
* 输出线程:
* 当标记flag=false时,输出完成,需等待
* @author Jole
* */
public class OutputDemo implements Runnable {
private ResourceData resourceData;
public OutputDemo(ResourceData resourceData) {
this.resourceData = resourceData;
}
@Override
public void run() {
while(true) {
synchronized (resourceData) {
//如果为false,则输出完成,等待
if(!resourceData.flag) {
try {
resourceData.wait();
}catch(Exception e) {
e.printStackTrace();
}
}
//否则输出,并唤醒拷贝
System.out.println(resourceData.name+" .. "+ resourceData.sex);
resourceData.flag = false;
resourceData.notify();
}
}
}
}