线程的基本操作(读书笔记)
Posted Darcy_wang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线程的基本操作(读书笔记)相关的知识,希望对你有一定的参考价值。
- 新建线程
新建线程很简单,只要使用new关键字创建一个线程对象.并且将它start()起来即可.那么线程start()后,会干什么呢?这才是问题的关键.线程Tread,有一个run()方法.start()方法就会新建一个线程并让这个线程执行run()方法
Thread t1 = new Thread(); t1.start();
这里要注意 下面的代码也能通过编译,也能正常运行.但是 却不能新建一个线程,而是在当前线程中调用run方法,只是当做一个普通方法调用
Thread t2 = new Thread(); t1.run();
注意:不要用run()方法来开启新线程.他只会在当前线程中,串行执行run()中的代码
创建线程的两种方法:
Thread t1 = new Thread() { @Override public void run() { System.out.println("Hello,I am 他"); } }; t1.start(); //Java8 版本 Thread t2 = new Thread(() -> System.out.println("Hello,I am 他")); t2.start(); public class CreateThread implements Runnable { public static void main(String[] args) { Thread t1 = new Thread(new CreateThread()); t1.start(); } /** * When an object implementing interface <code>Runnable</code> is used * to create a thread, starting the thread causes the object‘s * <code>run</code> method to be called in that separately executing * thread. * <p> * The general contract of the method <code>run</code> is that it may * take any action whatsoever. * * @see Thread#run() */ @Override public void run() { System.out.println("Oh, I am Runnable"); } }
下面的方法是最常用的!
- 终止线程
在jdk中 我们发现Thread提供了一个stop()方法,但是stop方法却被废弃了.原因是这个方法太过去暴力了.强行把执行到一半的线程终止.可能会引起一些数据不一致的问题.
因为他释放了这个线程所持有的所有锁,而这些锁恰恰是用来维持一个对象一致性的.例如写数据写到一半,并强行终止.那么对象就会被写坏.同时 由于锁已经被释放了,另一个等待的读线程顺理成章的读到了这个不一致的对象,悲剧也就发生了!
那如果需要停止一个线程时,其实方法很简单,.只需要由我们自行决定线程何时退出就可以了.
public class CreateThread implements Runnable { volatile boolean stopme = false; public void stopMe() { stopme = true; } @Override public void run() { while (true) { if (stopme) { System.out.println("exit by stop me"); break; } synchronized (u) { int v = (int) (System.currentTimeMillis() / 1000); u.setId(v); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } u.setName(String.valueOf(v)); } Thread.yield(); } } }
- 线程终端 在Java中 线程中断是一种重要的线程协作方式,从表面理解,中断就是让目标线程停止执行的意思,实际上并非完全如此,
严格意义上说, 线程中断并不是线程立即退出,而是给线程发送一个通知,告知目标线程,有人希望你退出啦.至于目标线程得到通知后如何处理,则完全由目标线程自行决定,这点很重要!
与线程中断有关的,有三个方法,
Thread t1 = new Thread(new CreateThread()); t1.interrupt();//中断线程 实例方法 t1.isInterrupted();//判断是否中断 实例方法 Thread.interrupted();//判断是否中断,并清除当前中断状态 静态方法 public class CreateThread implements Runnable { @Override public void run() { while (true) { if (Thread.currentThread().isInterrupted()) { System.out.println("Interrupted"); break; } try { Thread.sleep(2000);//实例线程 休眠2s 当线程休眠中被中断 抛出InterruptedException 异常 } catch (InterruptedException e) { System.out.println("Interrupted When Sleep"); Thread.currentThread().interrupt(); } Thread.yield(); } } public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new CreateThread()); t1.start(); Thread.sleep(1000);//main 休眠1s 后执行中断 t1.interrupt(); } }
- 等待(wait)和通知(notify)
为了支持多线程间的协作,JDK提供了两个非常重要的接口线程等待wait()方法和通知notify()方法,这两个方法并不是在Thread类中的 而是在Object类中,这意味着任何对象都可以调用这两个方法.
当在一个对象实例上调用wait()方法后,当前先吃就会在这个对象上等待,比如线程A中,调用了obj.wait()方法,那么线程A就会停止继续执行,而转为等待状态,等待何时结束呢?线程A会一直等到其他线程调用了obj.notify()方法为止,这时,obj对象就俨然成为了多线程之间通信的有效手段.
那wait()和notify()究竟是如何工作的呢?如果一个线程调用了object.wwait() 那么他就会进入object对象的等待队列,这个等待队列中,可能会有多个线程,因为系统运行多个线程同时等待一个对象.当object.notify()被调用时,他就会从这个等待队列中,随机选择一个线程,并将其唤醒,这个选择是不公平的,并不是先等待的线程就会优先选择,这个选择是完全随机的.
除了notify()方法外,Object对象还有一个类似的notifyAll()方法,.他和notify()的功能基本一样,区别在于他会唤醒等待队列中所有等待的线程,而不是随机的一个,
这里需要注意,Object.wait()方法并不是可以随便调用的.他必须包含在对应的synchronized语句中.无论是wait()和notify()都需要首先获得目标对象的一个监听器,
public class SimpleWN { final static Object object = new Object(); public static class T1 extends Thread { public void run() { synchronized (object) { System.out.println(System.currentTimeMillis() + ":T1 start!"); try { System.out.println(System.currentTimeMillis() + ":T1 wait for object "); object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(System.currentTimeMillis() + ":T1 end! "); } } } public static class T2 extends Thread { public void run() { synchronized (object) { System.out.println(System.currentTimeMillis() + ":T2 start! notify one thread "); object.notify(); System.out.println(System.currentTimeMillis() + ":T2 end! "); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) { Thread t1 = new T1(); Thread t2 = new T2(); t1.start(); t2.start(); } }
上述代码 开启了2个线程T1和T2,T1执行了wait(),注意 T1在执行等待之前,现获取了object对象锁,然后执行等待 T1会停下来等待,并释放对象锁,T2在执行notify()方法之前 获取object对象锁后,执行notify(),为了让效果明显.我们特意T2执行notify()后休眠2秒,,这样做可以明显了看出,T1得到通知后,还会先尝试重新获取对象锁,
注意 Object.wait()和Thread.sleep()两个方法都可以让线程等待若干时间.除了wait()可以被唤醒外,wait()执行完会释放目标对象的锁,而sleep()不会释放任何资源!
- 等待线程结束(join)和谦让(yield)
很多时候 一个线程的输入可能非常依然于另一个或者多个线程的输出.此时 这个线程就需要等待依赖线程执行完毕,才能继续执行,JDK提供了join()操作来实现这个功能,
public final synchronized void join(long millis)throws InterruptedException public final void join() throws InterruptedException
第一个方法给出了一个最大等待时间,如果超出给定时间目标线程还在执行,当前线程也会因为等待不及了,而继续执行下去 第二方法表示无线等待.它会阻塞当前线程,知道目标线程执行完毕.
下面提供一个简单的join实例,
public class JoinMain { public volatile static int i = 0; public static class AddThread extends Thread { public void run() { for (i = 0; i < 10000000; i++) ; } } public static void main(String[] args) throws InterruptedException { AddThread at = new AddThread(); at.start(); at.join(); System.out.println(i); } }
主函数中,如果不是用join()等待AddThread, 那么得到的i 很可能是0或者一个非常小的数字,但在join()后,表示主线程愿意等待AddThread执行完毕,跟着AddThread一起往前走,故在join()返回时.AddThread已经执行完了.故i 永远是10000000.
注意 不要在应用程序中 在Thread对象实例上使用类似wait()或者notify等方法, 因为折痕有可能会影响系统API的运行,或者被系统API所影响!
另外一个比较有趣的方法是Thread.yield();
public static native void yield();
这是一个静态方法,一旦执行,它会使当前线程让出CPU.当时注意,让出CPU并不代表当前线程不执行了.当前线程让出CPU后,还会进行CPU资源的争夺,但是是否能够再次被分配到,就不一定了,因此 对Thread.yield()的调用就好像在说:我已经完成一些最重要的工作 了.我应该休息一下了.可以给其他线程一些工作机会了!
如果你觉得一个线程不那么重要,或者优先级很低,而且害怕他会占用太多CPU资源,那么可以适当的时候调用这个方法,.给予其他重要的线程更多的工作机会!
- volatile Java使用了一些特殊的操作或者关键字来申明,告诉虚拟机,在这个地方,要尤其注意,不能随意变动优化目标指令. 易变得 不稳定的.
当你用volatile声明一个变量时,就等于告诉虚拟机,这个变量极有可能会被某些程序或者线程修改.为了确保可见性,虚拟机做了特殊的手段,保证这个变量的可见性!
以上是关于线程的基本操作(读书笔记)的主要内容,如果未能解决你的问题,请参考以下文章
《C++ Concurrency in Action》读书笔记三 同步并发操作
C++并发编程----并发代码的设计(《C++ Concurrency in Action》 读书笔记)