java线程创建
1.为实现 Runnabl 接口的 run 方法
public class ThreadTest {
public static class MyThread ext ends Thread {
@Override
public void run(){
System out print ("I am a child thread");
}
public static void main (String[] args) {
//创建线程
MyThread thread= new MyThread ();
// 启动线程
thread .start();
}
}
2.继承 hread 重写 run 方法
public static class RunableTask implements Runnable{
@Override
public void run() {
System.out.println ("I am a child thread" );
}
public static void main(String[] args) throws terruptedException{
RunableTask task =new RunableTask();
new Thread(task).start();
new Thread(task).start();
}
}
3.使用futureTask方式
//创建任务类,类似runable
public stat class CallerTask implements Callable<String>{
@Override
public String call () throws Except on {
return "hello ”;
}
public static void main(String[] args) throws InterruptedException {
// 创建异步任务
FutureTask<String> futureTask =new FutureTask<>(new CallerTask()) ;
//启动线程
new Thread(futureTask). start () ;
try {
//等待任务执行完毕,并返回结果
String result = futureTask.get ();
System out printl口( result );
}
catch (ExecutionExcept on e) {
e.printStackTrace() ;
}
}
小结:使用继承方式的好处是方便传参,你可以在子类里面添加成员变量 ,通过 set 方法设置参数或者通过构造函数进行传递,而如果使用 Runnable 方式,则只能使用主线程里面被声明为 final 变量。不好的地方是 Java 支持多继承,如果继承了 Thread 类, 那么子 不能再继承其他 ,而 Runnable 则没有这个限制 。前两种方式都没办法拿到任务 的返回结果,但是 futuretask 方式可以.
线程通知与等待
wait()函数
当一个线程调用一个共享变量的wait()方法时,该线程会阻塞挂起直到(释放锁)
①其他线程调用该共享对象的notify()或notifyAll()方法
②其他线程调用该线程的interrupt()方法,该线程抛出InterruptedException异常返回
调用wait方法前线程需要事先获取该对象的监视器锁(使用synchronied)
wait(long timeout)函数:该方法相 wait() 方法多了一个超时参数,它的不同之处在于,如果一个线程调用共 享对象的该方法挂起后 没有在指定的 timeout ms 内被其线程调用该共享变量的 notify () 或者 notifyAll () 方法唤醒,那么该函数还是会因为超时而返回。如果将 timeout 置为0则和 wait 方法效果一样,因为在 wait 方法内部就是调用了 wait 需要注意的是, 如果在调用该函数时传递了一个负的timeout 会抛出IllegalArgumentException异常
wait(long timeout, int nanos )函数:在其内部调用的是 wait(long timeout)函数,只有在 nanos>0 时才使参数 timeout 递增1.
notify函数和notifyAll函数
notify时唤醒一个在该共享变量上调用wait系列方法被挂起的线程。notifyAll时唤醒所有。
等待线程执行终止的 join 方法
等待线程完成才进行下面代码
另外,线程调用线程 join 方法后会被阻塞,当其它线程调用了线程 interrrupt()方法中断了线程 时,线程会抛出 InterruptedException 异常而返回。
让线程睡觉的sleep方法
调用sleep线程会让出指定时间的执行权,不参与CPU调度,不过锁还是持有不让出,时间一到线程处于就绪状态
,如果一个线程中断了它,会在sleep方法处抛出异常。如果sleep传递参数为负,会抛IllegalArgumentException异常。
让出CPU执行权的yield方法
Thread 有一个静态 yield 方法,当一个线程调用 yield 方法时,实际就是在暗示线程调度器当前线程请求让出自己 CPU 使用,但是线程调度器可以无条件忽略这个暗示。我们知道操作系统是为每个线程分配一个时间片来占有CPU 的, 正常情况下当一个 线程分配给自己的时间片使用完后,线程调度器才会进行下一轮的线程调度,而当一个线程调用了 Thread 类的静态方法 yield 时,是在告诉线程调度器自己占有的时间片中还没 有使用完的部分自己不想使用了,这暗示线程调度器现在就可以进行下一轮的线程调度
当一个线程调用 yield 方法时, 当前线程会让出 CPU 使用权,然后处于就绪状态,线程调度器会从线程就绪队列里面获取一个线程优先级最高的线程,当然也有可能会调度到 刚刚让出CPU 的那个线程来获取 CPU 执行权。
总结 :sleep 和yield 方法的区别在于,当线程调用 sleep 方法时调用线程会被阻塞挂起指定的时间,在这期间线程调度器不会去调度该线程 ;而调用 yield方法时,线程只是让出自己剩余的时间片,并没有被阻塞挂起,而是处于就绪状态,线程调度器下一次调度时就有可能调度到当前线程执行
线程中断
void interrupt()方法:中断线程A。线程A运行时设置标志位,如线程A调用了wait系列函数、join方法或者sleep方法而被阻塞,则会抛出异常并返回。
boolean isInterrupted()方法:检测当前线程是否中断,是返回true,否返回false
boolean interrupted()方法:检测当前线程是否中断,是返回true,否返回false,并重置当前线程中断标志,是Thread一个静态方法
守护进程与用户进程
Java 中的线程分为两类,分别为 daemon 线程(守护线程)和 user 线程(用户线程)。 在JVM 启动会调用 main 函数, main 数所在的线程就是一个用户线程,其实在 JVM 内部同时-还启动了好多守护线程, 比如垃圾回收线程。那么守 线程和用户线程有什么 区别呢?区别之一是当最后一个非守护线程结束时, JVM 正常退出,而不管当前是否有守护线程,也就是说守护线程是否结束并不影响 JVM 退出。言外之意,只要有一个用 户线程还没结束 正常情况下 JVM 就不会退出。
创建守护进程:
public static void main(String[] args) {
Thread daemonThread = new Thread (new Runnable() {
public void run() {
}
}) ;
//设置 为守护线程
daemonThread.setDaemon(true) ;
daemonThread.start ();
}
ThredLocal
当创建一个变量后 每个线程对其进行访问的时候访问的是自己线程的变量
ThreadLocal JDK 包提供的,它提供了线程本地变量,也就是如果你创建了 ThreadLocal ,那么访问这个变量的每个线程都会有这个变量的一个本地副本
static ThreadLocal<String> localVariable = new ThreadLocal<> () ;
三个方法:
void set(T value)
T get()
void remove()
ThreadLocal不支持继承性,子线程不可访问父线程的本地变量,可以用InheritableThreadLocal类
public static ThreadLocal<String> threadLocal = new InheritableThreadLocal<String> ();