Java线程
Java线程
-
使用线程
-
实现Runnable接口
-
实现Callable接口
-
继承Thread类
实现Runnable和Callable接口的类只能当做一个可以在线程中运行的任务,不是真正意义上的线程,因此最后还需要通过Thead来调用,可以理解为任务是通过线程驱动从而执行的。 -
实现
//实现Runnable接口 public class MyRunnable implements Runnable { @Override public void run() { // ... } } public static void main(String[] args) { MyRunnable instance = new MyRunnable(); Thread thread = new Thread(instance); //调用start()方法来启动线程 thread.start(); } //实现Callable接口 public class MyCallable implements Callable<Integer> { public Integer call() { return 123; //和Runnable不同,有返回值 } } public static void main(String[] args) throws ExecutionException, InterruptedException { MyCallable mc = new MyCallable(); FutureTask<Integer> ft = new FutureTask<>(mc); Thread thread = new Thread(ft); thread.start(); System.out.println(ft.get()); } //继承Thread public class MyThread extends Thread { public void run() { // ... } } public static void main(String[] args) { MyThread mt = new MyThread(); mt.start(); } 总结: 实现接口会更好一些,因为: Java不支持多重继承,因此继承了Thread类就无法继承其他类,但是可以实现多个接口 类可能只要求可执行就行,继承整个Thread类开销过大
-
-
基础线程机制
-
Executor管理多个异步任务的执行,而无需程序员显式的管理线程的生命周期,这里的异步是指多个任务的执行互不干扰,不需要进行同步操作。
-
CachedThreadPool:一个任务创建一个线程
-
FixedThreadPool:所有任务只能使用固定大小的线程
-
SingleThreadPool:相当于大小为1的FixedThreadPool
public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool(); for (int i = 0; i < 5; i++) { executorService.execute(new MyRunnable()); } executorService.shutdown(); }
-
-
Daemon
-
守护线程是程序运行时在后台提供服务的线程,不属于程序中不可或缺的部分,当所有的非守护线程结束时,程序也就终止,同时会杀死所有的守护线程,main()属于非守护线程,在线程启动之前使用setDaemon()方法可以将一个线程设置为守护线程
public static void main(String[] args) { Thread thread = new Thread(new MyRunnable()); thread.setDaemon(true); }
-
-
sleep
-
Thread.sleep(millisec)方法会休眠当前只在执行的线程,millisec单位为毫秒
-
sleep()可能会抛出InterrupException,因为异常不能跨线程传播会main()中,因此必须在本地进行处理,线程中抛出的其他异常也同样需要在本地进行处理。
public void run() { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } }
-
-
yield()
-
对于静态方法Thread.yield()的调用声明了当前线程已经完成了生命周期中最重要的部分,可以切换给其他线程来执行,该方法只是对线程调度器的一个建议,而且也只是建议具有相同优先级的其他线程可以运行
public void run() { Thread.yield(); }
-
-
-
中断
一个程序执行完毕之后会自动结束,如果在运行过程中发生异常一会提前结束
-
InterruptedException
-
通过调用一个线程的interrupt来中断该线程,如果该线程处于阻塞、限期等待或者无限期等待状态,那么就会抛出InterruptedException,从而提前结束该线程,但是不能中断I/O阻塞和synchronized锁阻塞
public class InterruptExample { private static class MyThread1 extends Thread { @Override public void run() { try { Thread.sleep(2000); System.out.println("Thread run"); } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) throws InterruptedException { Thread thread1 = new MyThread1(); thread1.start(); thread1.interrupt(); System.out.println("Main run"); } //对于以上代码,在main()中启动一个线程之后在中断它,由于线程中调用了Thread.sleep()方法,因此会抛出一个InterrupedException,从而提前结束线程,不执行之后的语句
-
-
interrupted()
-
如果一个线程的run()方法执行了一个无限循环,并且没有执行sleep()等会抛出一个InterruptedException的操作,那么调用现成的interrupt()方法就无法使线程提前结束
但是调用interrupt()方法会设置线程的中断标记,此时调用interrupted()会返回true,因此可以在循环体中使用interrupted()方法来判断线程是否处于中断状态,从而提前结束线程。public class InterruptExample { private static class MyThread2 extends Thread { @Override public void run() { while (!interrupted()) { // .. } System.out.println("Thread end"); } } } public static void main(String[] args) throws InterruptedException { Thread thread2 = new MyThread2(); thread2.start(); thread2.interrupt(); }
-
-
Executor的中断操作
-
调用Executor的shutdown()方法会等待线程都执行完毕之后在关闭,但是如果调用的使shutdownNow()方法,则相当于调用每个线程的interrupt()方法
public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool(); executorService.execute(() -> { //使用Lanmbda创建线程,相当于创建了一个匿名内部线程 try { Thread.sleep(2000); System.out.println("Thread run"); } catch (InterruptedException e) { e.printStackTrace(); } }); executorService.shutdownNow(); System.out.println("Main run"); } -
如果只想中断Executor的一个线程,可以通过使用submit()方法来提交一个线程,它会返回一个Future<?>对象,通过调用该对象的cancel(true)方法就可以中断线程
Future<?> future = executorService.submit(() -> { // .. }); future.cancel(true);
-
-
-
互斥同步
java提供了两种锁机制来控制多个线程对共享资源的互斥访问,第一个使JVM实现的synchronized,而另一个是JDK实现的ReentrantLock
-
synchronized
-
同步代码块
public void func() { synchronized (this) { // ... } } //它只作用于同一个对象,如果调用两个对象上的同步代码块,就不会进行同步 //对于以下代码,使用ExecutorService执行了两个线程,由于调用的是同一个对象的同步代码块,因此这两个线程会进行同步,当另一个线程进行同步语句块时,另一个线程就必须等待. public class SynchronizedExample { public void func1() { synchronized (this) { for (int i = 0; i < 10; i++) { System.out.print(i + " "); } } } } public static void main(String[] args) { SynchronizedExample e1 = new SynchronizedExample(); ExecutorService executorService = Executors.newCachedThreadPool(); executorService.execute(() -> e1.func1()); executorService.execute(() -> e1.func1()); } //对于一下代码,两个线程调用了不同对象的同步代码块,因此这两个线程就不需要同步,从输出结果可以看出,两个线程交叉执行 public static void main(String[] args) { SynchronizedExample e1 = new SynchronizedExample(); SynchronizedExample e2 = new SynchronizedExample(); ExecutorService executorService = Executors.newCachedThreadPool(); executorService.execute(() -> e1.func1()); executorService.execute(() -> e2.func1()); } -
同步一个方法
//它和同步代码块一样,作用于同一个对象 public synchronized void func () { // ... } -
同步一个类
public void func() { synchronized (SynchronizedExample.class) { // ... } } //作用于整个类,也就是说两个线程调用同一个类的不同对象上的这种同步语句,也会进行同步 public class SynchronizedExample { public void func2() { synchronized (SynchronizedExample.class) { for (int i = 0; i < 10; i++) { System.out.print(i + " "); } } } } public static void main(String[] args) { SynchronizedExample e1 = new SynchronizedExample(); SynchronizedExample e2 = new SynchronizedExample(); ExecutorService executorService = Executors.newCachedThreadPool(); executorService.execute(() -> e1.func2()); executorService.execute(() -> e2.func2()); } -
同步一个静态方法
public synchronized static void fun() { // ... } //作用于整个类
-
-
ReentrantLock
ReentrantLock是java.util.concurent(JUC)包中的锁
public class LockExample { private Lock lock = new ReentrantLock(); public void func() { lock.lock(); try { for (int i = 0; i < 10; i++) { System.out.print(i + " "); } } finally { lock.unlock(); // 确保释放锁,从而避免发生死锁。 } } } public static void main(String[] args) { LockExample lockExample = new LockExample(); ExecutorService executorService = Executors.newCachedThreadPool(); executorService.execute(() -> lockExample.func()); executorService.execute(() -> lockExample.func()); }ReentrantLock和synchronized的区别: 1.synchronized是JVM实现的,ReentrantLock是JDK实现的 2.新版本java对synchronized加了自旋锁等,synchronized与ReentrantLock大致相同 3.ReentrantLock可中断,synchronized不行 4.synchronized中的锁是非公平的,ReentrantLock默认情况下也是非公平的,但是也可以是公平的 5.一个ReentrantLock可以同时绑定多个Condition对象 使用选择: 除非需要使用ReentrantLock的高级功能,否则优先使用synchronized。因为synchronized是JVM实现的一种锁机制,JVM原生的支持它,而ReentrantLock不是所有的JDK版本都支持,并且使用synchronized不用担心没有释放锁而导致死锁问题,因为JVM会确保锁的释放。
-
-
线程之间的协作
-
join()
在线程中调用另一个现成的join()方法,会将当前线程挂起,而不是忙等待,知道目标线程结束 -
wait()
调用wait()使得线程等待某个条件满足,线程在等待是会被挂起,当其他现成的运行使得这个条件满足时,其他线程会调用notify()或者notifyAll()来唤醒挂起的线程 使用wait()挂起期间,线程会释放锁,这是因为如果没有释放锁,其他线程就无法进入对象的同步方法或者同步控制块中,那么就无法执行notify()或者notifyAll()来唤醒挂起的线程,造成死锁 -
wait()和sleep()的区别
- wait()是Object的方法,而sleep()是Thead的静态方法
- wait()会释放锁,sleep()不会
-
TODO
-
-
线程状态
-
新建
新建后尚未启动 -
可运行
正在Java虚拟机中运行,但是在操作系統层面,它可能处于运行状态,也可能等待资源调度,资源调度完成就进入运行状态。所以该状态的可运行是指可以被运行,具体有没有运行要看底层操作系统的资源调度 -
阻塞
请求获取monitor lock从而进入synchronized函数或者代码块,但是其他线程已经占用了该monitor lock,所以处于阻塞状态,要结束该状态进入从而RUNABLE需要其他线程释放monitor lock -
无限期等待
等待其他线程显示的唤醒 阻塞和等待的区别在于,阻塞时被动的,他是在等待获取monitor lock。而等待是主动的,通过调用Object.wait()等方法进入 -
限期等待
无需等待其他线程显示的唤醒,在一定时间之后会被系統自动唤醒 -
死亡
可以是线程结束任务之后自己结束,或者产生了异常而结束
-

浙公网安备 33010602011771号