1.1线程简介
1.1.1任务,进程,线程,多线程
- 任务
一段时间内做的事情 - 进程
在操作系统运行的程序就是进程,一个程序执行时,内存会开辟一段空间。
程序:指令和数据的有序集合,静态的概念 - 线程
一个进程可以包含有若干个线程,一个进程至少有一个线程
线程是cup调度和执行的单位 - 多线程
多线程调运图(来自狂神说java)
1.2线程创建
1.2.1 继承thread类
自定义线程类继承Thread类
重写run方法,编写线程方法体
创建线程对象,调用start()方法启动线程
1.2.2 实现runnable接口(静态代理)
定义MyRunnable类实现Runnable接口
实现run()方法,编写方法体
创建线程对象,电泳start()方法启动线程
避免单继承的局限性,方便同一个对象被多个线程使用
1.2.3 实现callable接口
实现Callable接口,需要返回值类型
重写call方法,需要抛出异常
创建目标对象
创建执行服务 ExecutorService ser = Excutors.newFixedThreadPool(1);
提交执行:Future <Boolean> result = ser.submit(t1);
获取结果 boolean r1 = result1.get()
关闭服务 ser.shutdownNow()
简单龟兔赛跑练习
public class Race implements Runnable{
private static String winner;
@Override
public void run() {
for (int i = 0; i <=100 ; i++) {
if ("rabbit".equals(Thread.currentThread().getName())){
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("exception in race thread sleep function");
}
}
if (gammeOver(i)){
break;
}
System.out.println(Thread.currentThread().getName()+" is running the step :"+i);
}
}
public static void main(String[] args) {
Race race =new Race();
Thread rabbit = new Thread(race,"rabbit");
Thread turtle = new Thread(race,"turtle");
rabbit.start();
turtle.start();
}
private boolean gammeOver(int step){
if (winner!=null){
return true;
}{
if (step==100){
winner = Thread.currentThread().getName();
System.out.println("Winner is : "+winner+" Game over!" );
return true;
}
}
return false;
}
}
1.3静态代理
真实对象与代理对象都需要同一个接口
代理对象要代理真实角色,需要将真实对象(目标)传入
好处:代理对象可以扩展功能,做甚多真实对象做不了的事情
public class StaticProxy{
public static void main(String[] args) {
//the class which implement the runnable interface ;
You you = new You();
//Thread thread = new Thread(object);
WeddingCompany weddingCompany= new WeddingCompany(you);
//thread.start();
weddingCompany.marry();
}
}
interface Marry {
public void marry();
}
class You implements Marry{
@Override
public void marry() {
System.out.println("Marry is happy");
}
}
class WeddingCompany implements Marry{
private Marry target;
public WeddingCompany(Marry target) {
this.target = target;
}
@Override
public void marry() {
before();
target.marry();
after();
}
private void after() {
System.out.println("after , collection");
}
private void before() {
System.out.println("before , prepare");
}
}
1.4 线程状态
1.4.1停止线程
不推荐JDK提供的stop(),destroy方法
推荐线程自己停止下来
建议使用标志位来进行终止
1.4.2线程休眠
sleep(时间)指定当前线程阻塞毫秒数
sleep存在异常InteruptedException
spleep时间达到后线程进入就绪状态
sleep可以模拟网路延迟,倒计时等等
每个对象都有一个锁,sleep不会释放锁
演示:计时
public class TestSleep {
public static void main(String[] args) {
Date date = new Date(System.currentTimeMillis());
int num = 10;
try {
while (true){
Thread.sleep(1000);
num--;
System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));
date= new Date(System.currentTimeMillis());
if (num==0)
break;
}
// tenDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void tenDown() throws InterruptedException {
int num = 10;
while (true){
Thread.sleep(1000);
num--;
System.out.println(num);
if (num==0)
break;
}
}
}
1.4.3线程礼让
Thread.yield()
礼让线程,让当前正在执行的线程暂停但不阻塞
将线程运行状态转为就绪状态
让CPU重新调度,礼让不一定成功,看CPU心情
1.4.4 Join()
Join 合并线程,待此线程执行之后,其他线程才可以继续执行,其他线程阻塞(插队)
1.4.5线程观测状态
Thread.state
public class TestState {
public static void main(String[] args) {
Thread thread = new Thread(()->{
for (int i = 0; i <5 ; i++) {
try {
Thread.sleep(500);
System.out.println("agrarehaet");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread.State state = thread.getState();//NEW
System.out.println(state);
thread.start();
state = thread.getState();//RUNNABLE
System.out.println(state);
while (state != Thread.State.TERMINATED)
{
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
state = thread.getState();
System.out.println(state);
}
//TERMINATED
}
}
1.4.6线程优先级
Java 提供一个线程调度器老监控程序中启动后进行就绪状态的所有线程,线程调度器按照优先级来决定调度哪个线程来执行
线程的优先级用数字表示,范围1-10
Thread.MIN_PRIORITY=1;
Thread.MAX_PRIORITY=1;
Thread.NORM_PRIORITY=5;
使用以下方式改变或获取优先级
.setPriority(int xxx);
.getPriority();
优先级低只是意味着获得调度的概率低,依据CPU调度情况而定,优先级高的概率会增高
1.4.7守护线程
线程分为用户线程和守护线程
虚拟机必须确保用户线程执行完毕
虚拟机不用等带守护线程执行完毕
如后台记录日志,GC
public class TestDaemond {
public static void main(String[] args) {
God god = new God();
YouSelf youSelf = new YouSelf();
Thread thread = new Thread(god);
thread.setDaemon(true);
thread.start();
new Thread(youSelf).start();
}
}
//上帝
class God implements Runnable{
@Override
public void run() {
while(true){
System.out.println("God bless you");
}
}
}
//你
class YouSelf implements Runnable{
@Override
public void run() {
for (int i = 0; i <36500 ; i++) {
System.out.println("always alive");
}
System.out.println("+++++++++Good bye+++++++");
}
}
2.1线程同步 -多个线程操作同一个资源
2.1.1 并发
同一个对象被多个线程所执行(抢票,取钱)
处理多线程问题是, 多个线程访问同一个对象,并且一些线程需要修搞这个对象,就需要用到线程同步。线程同步就是一种等待机制,多个需要访问的线程进入这个对象的等待池
形成队列,等待前面的对象线程使用完毕之后,下一个线程再使用
2.1.2队列和锁
由于同一进程的多个线程共享一块存储空间,在带来方便的同事,为了保证数据在方法中被访问的正确性,在访问时加入锁机制 synchronized,当一个线程获得对象的排他锁,独占资源,那么其他线程必须等待,使用后释放锁即可(排队上厕所)
- 一个线程持有锁,会导致所有其他需要该资源锁的线程挂起
- 在多线程的竞争下,加锁,释放锁或导致比较多的上下文切换和调度延时,引起性能问题
- 如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题。
//线程不安全
public class UnSafeBuyTicket {
public static void main(String[] args) {
BuyTicket buyTicketStation = new BuyTicket();
new Thread(buyTicketStation,"无能的我").start();
new Thread(buyTicketStation,"牛逼的你").start();
new Thread(buyTicketStation,"万恶的黄牛").start();
}
}
class BuyTicket implements Runnable{
private int ticketsNum = 10;
boolean flag = true;
@Override
public void run() {
// sale ticket
while (flag){
buy();
}
}
private void buy(){
//if have tickets
if(ticketsNum <= 0){
System.out.println("no tickets now");
flag=false;
return;
}
//模拟延时
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticketsNum--;
System.out.println(Thread.currentThread().getName()+"get the "+ ticketsNum);
}
}
2.1.3同步方法
synchronized 方法控制对"对象"的访问,每一个对象对应一把锁,每个synchronized方法都必须获得电泳该方法的锁才能执行,否则线程阻塞,方法一旦执行则独占该锁,知道方法返回才释放锁,后面阻塞的线程参能获得这个锁继续执行
缺点:影响效率
- 同步块:synchronized(obj){}
- obj 称之为同步监视器 obj可以是任何对象,推荐使用共享资源作为同步监视器
- 同步方法中无需指定同步监视器,默认this,该对象或者是class
- 同步监视器的执行过程
- 第一个线程访问,锁定同步监视器,执行代码块
- 第二个线程访问,发现同步监视器锁定,无法访问
- 第一个线程访问完毕,解锁同步监视器
- 第二个线程访问,发现监视器没u有锁,然后锁定并访问
2.1.4死锁
多个线程互相抱着对方所需要的资源,形成僵持状态
产生死锁的四个条件(缺一不可)
- 互斥条件:一个资源每次只能被一个线程使用
- 请求与保持条件: 一个进程因请求资源而阻塞,对已获得的资源保持不放
- 不剥夺条件:线程已获得资源,在未使用完之前,不能强行剥夺
- 循环的等待条件:若干线程之间形成一种头尾相接的循环资源等待
public class DeadLock {
public static void main(String[] args) {
MakeUp girl1 = new MakeUp(0, "灰姑娘");
MakeUp girl2 = new MakeUp(1, "白雪公主");
girl1.start();
girl2.start();
}
}
class Lipstick {
}
class Mirror {
}
class MakeUp extends Thread {
private static Lipstick lipstick = new Lipstick();
private static Mirror mirror = new Mirror();
int choice;
String name;
public MakeUp(int choice, String name) {
this.choice = choice;
this.name = name;
}
@Override
public void run() {
if (choice == 0) {
synchronized (lipstick) {
System.out.println(this.name + " get the lock of lipstick");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (mirror) {
System.out.println(this.name + " get the lock of mirror");
}
}
} else {
synchronized (mirror) {
System.out.println(this.name + " get the lock of mirror");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lipstick) {
System.out.println(this.name + " get the lock of lipstick");
}
}
}
}
}
2.1.5锁Lock
控制多个线程对共享资源的进行访问的工具,提供了对共享资源的独占访问,每次只能有一个线程对lock对象加锁,线程开始访问共享资源之前应先获得lock锁