线程的生命周期

1

大多数的程序语言只能循序运行单独一个程序块,但无法同时运行不同的多个程序块。Java的“多线程”恰可弥补这个缺憾,它可以让不同的程序块一起运行,如此一来可让程序运行更为顺畅,同时也可达到多任务处理的目的

1 新建线程

1.1 继承Thread类

package cn.sz.gl.no5;

public class MyThread extends Thread {

    @Override
    public void run() {
        System.out.println("执行了线程的run方法_");
    }
}
package cn.sz.gl.no6;

public class Test {

    public static void main(String[] args) {
        MyRunnable mra = new MyRunnable();
        MyRunnable mrb = new MyRunnable();
        MyRunnable mrc = new MyRunnable();
        MyRunnable mrd = new MyRunnable();
        MyRunnable mre = new MyRunnable();

        new Thread(mra).start();
        new Thread(mrb).start();
        new Thread(mrc).start();
        new Thread(mrd).start();
        new Thread(mre).start();

    }
}

1.2 实现Runnable接口 

package cn.sz.gl.no6;

public class MyRunnable implements Runnable {

    @Override
    public void run() {
        System.out.println("我的线程...");
    }

}
package cn.sz.gl.no6;

public class Test {

    public static void main(String[] args) {
        MyRunnable mra = new MyRunnable();
        MyRunnable mrb = new MyRunnable();
        MyRunnable mrc = new MyRunnable();
        MyRunnable mrd = new MyRunnable();
        MyRunnable mre = new MyRunnable();

        new Thread(mra).start();
        new Thread(mrb).start();
        new Thread(mrc).start();
        new Thread(mrd).start();
        new Thread(mre).start();

    }
}

 1.3 实现Callable接口

(1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。

(2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。

(3)使用FutureTask对象作为Thread对象的target创建并启动新线程。

(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

package Thread;

import java.util.concurrent.*;

public class TestThread {
   public static void main(String[] args) throws Exception {
       testCallable();
   }

   public static void testCallable() throws Exception {
       Callable callable = new MyThreadCallable();
       FutureTask task = new FutureTask(callable);
       new Thread(task).start();
       System.out.println(task.get());
       Thread.sleep(10);//等待线程执行结束
       //task.get() 获取call()的返回值。若调用时call()方法未返回,则阻塞线程等待返回值
       //get的传入参数为等待时间,超时抛出超时异常;传入参数为空时,则不设超时,一直等待
       System.out.println(task.get(100L, TimeUnit.MILLISECONDS));
   }
}

class MyThreadCallable implements Callable {

   @Override
   public Object call() throws Exception {
       System.out.println("通过实现Callable,线程号:" + Thread.currentThread().getName());
       return 10;
   }
}

1.4 情况A

在以上的代码中,线程一般为情况A,b,线程创建后使用start()函数,在就绪状态下各个线程互相抢夺CPU后进人运行状态.在运行状态下,有可能CPU调度使进程失去了执行权,调入了就绪状态,线程一旦完成会删除线程,进入死亡状态.

2.其他阻塞

Thread.sleep(毫秒数),让线程休眠,时间到,自动唤醒并继续执行

join():当前线程暂停执行,新加入的线程开始执行,当新线程执行完之后,再执行当前线程

可以使这些代码阻塞进程的运行,使部分的进程按顺序执行

Mythread3 t3 = new Mythread3();
        t3.start();

        Thread.sleep(1000);
        Mythread3 t4 = new Mythread3();
        t4.start();

        Mythread3 t5 = new Mythread3();
        t5.start();

        Mythread3 t6 = new Mythread3();
        t6.start();
        Mythread3 t7 = new Mythread3();
        t7.start();

在这种情况下由于主线程执行后,1秒内仅有t3一个进程,大概t3会第一个被执行

3.同步阻塞 

处理多线程的问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象,这个时候我们就需要线程同步,线程同步其实就是一种等待机制,多个需要同时访问该对象的线程进入这个对象的等待池中,形成队列,等待前面线程使用完成,下一个线程再使用

​ 在Java语言中,引入对象互斥锁的概念,保证共享数据操作的完整性;

​ 每个对象都对应于一个可称为"互斥锁"的标记,这个标记保证在任一时刻,只能有一个线程访问对象;

​ 用关键字synchronized给对象加互斥锁.

3.1 同步代码块

synchronized放在对象前面限制一段代码的执行,其语法如下:

Object obj = new Object();
...
synchronized(this){//this被加锁,任何其他要锁this的方法和代码块被阻塞.
    需要同步的代码;
}
...
synchronized(obj){//obj被加锁,任何其他要锁obj的代码块被阻塞.
    需要同步的代码;
}
new Thread(()->{
            while (true) {
                synchronized (lock) {
                    System.out.println("----------------------start");
                    System.out.println("111111111111111");
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println("----------------------end");
                }
            }
        },"t1").start();

 3.2 同步方法

同步非静态方法:synchronized放在方法声明中,表示整个方法为同步方法,锁定this对象,如果有一个线程进入了该方法,其他线程要想使用当前this对象的任何同步方法,都必须等待前一个线程执行完该同步方法之后

如下:

public synchronized void method1(){
    …
}
public synchronized void method2(){
    …
}
public synchronized void method3(){
    …
}

同步static方法: synchronized放在static方法声明中,表示锁定该类的class对象(xxx.class,是Class类型的,是描述一个类的信息的对象)

​ 如果有一个线程进入了该方法,其他线程要想使用当前类中的任何同步静态方法,都必须等待前一个线程执行完该同步方法之后;其他非同步方法及非静态的同步方法的执行不受影响

如下:

public synchronized static void method1(){
    …
}
public synchronized static void method2(){
    …
}

注意:

​ 当前线程调用本同步方法时,其他线程是不允许调用**本对象**中其他同步方法

​ 当前线程在调用本同步静态方法时,其他线程是不能够调用本类中的其他同步静态方法的,但是可以调用本类中的其他非同步方法或者非静态同步方法

​ 对于同步静态方法,此时锁定的是对应的类型(类名.class),所有属于该类型的对象,都在其锁定范围内,此时可以实现方法的同步调用

线程同步的引发的问题:

* 一个线程持有锁会导致其他所有需要该锁的线程阻塞

package com.shadow.test.thread;

import java.sql.Time;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * 锁的使用
 *
 * 7个程序员和一个老板的故事
 */
public class TestWait {

    Lock lock = new Lock();

    boolean isMoney =false;
    boolean isTime =false;

    public static void main(String[] args) throws InterruptedException {

        TestWait tw = new TestWait();
        tw.init();

    }

    public void init() throws InterruptedException {
        new Thread(()->{
            jackRun();
        },"jack").start();

        new Thread(()->{
            roseRun();
        },"rose").start();


//        TimeUnit.MILLISECONDS.sleep(100);//保证jack线程先启动---不能百分百保证

//        for (int i = 1; i < 6; i++) {
//            new Thread(()->{
//                run();
//            },"t"+i).start();
//        }


        TimeUnit.SECONDS.sleep(3);

        new Thread(()->{
            synchronized (lock) {
                System.out.println(currentTimeAndThreadName()+" 愿意给钱");
                isMoney = true;

                //把所有waitSet当中的线程全部唤醒起来--->虚假唤醒
                lock.notifyAll();

                //lock.notify();//随机唤醒一个waitSet当中的线程
            }
        },"boss").start();
    }


    public static String currentTimeAndThreadName(){

        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("hh:mm:ss.SSS");
        String format = simpleDateFormat.format(new Date());
        return format+"-"+Thread.currentThread().getName()+" ";
    }


    public void roseRun() {
        System.out.println(currentTimeAndThreadName() + "rose 来加班了");
        synchronized (lock){
            while (!isTime){//条件不满足---没有调休
                System.out.println(currentTimeAndThreadName()+" 条件不满足");
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println(currentTimeAndThreadName()+" rose 醒来了");
            }

            System.out.println(currentTimeAndThreadName()+" rose 干活了");
            System.out.println(currentTimeAndThreadName()+" rose 线程结束了");
        }

    }


    public void jackRun() {
        System.out.println(currentTimeAndThreadName() + "jack 来加班了");
        synchronized (lock) {
            while (!isMoney) {//没钱
                //休息
                try {
                    System.out.println(currentTimeAndThreadName()+" 条件不满足");
                    //sleep不会释放
                    //TimeUnit.SECONDS.sleep(60*60);
                    lock.wait();//让当前线程阻塞  主动调用一个方法才能醒来
                    //释放锁
                    System.out.println(currentTimeAndThreadName() + "jack 醒来了");
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println(currentTimeAndThreadName() + " jack 干活了有钱了");
            System.out.println(currentTimeAndThreadName() +"jack 线程结束了");
        }
    }

    //加班的方法
    public  void run()  {
        synchronized(lock) {
            System.out.println(currentTimeAndThreadName() + "加班------");
        }
    }
}

如果代码使用了TimeUnit.SECONDS.sleep(60*60);jack拿到锁后,进入其他阻塞状态,不会释放锁,他人无法获取锁,只能等jack休息完才能工作.
* 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题,所以一般不会使用同步方法,此时方法内的所有内容运行将如单线程一般,毫无多线程的优点
* 线程同步可能会造成死锁

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值