线程基础知识
Posted 听风者-better
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线程基础知识相关的知识,希望对你有一定的参考价值。
线程基础知识
1.并发和并行
- 并发是没有时间上的重叠的,两个任务是交替执行的,由于切换的非常快,对于外界调用者来说相当于同一时刻多个任务一起执行了;
- 并行可以看到时间上是由重叠的,也就是说并行才是真正意义上的同一时刻可以有多个任务同时执行。
2.线程的状态
线程一般分为:新建,就绪,运行,阻塞,死亡五中状态
当创建一个线程后,并没有运行,处于新建状态,需要通过调用start方法,让线程处于就绪状态,但是否运行取决于cpu分配的执行机会,当得到cpu的执行机会后马上运行,一个正在执行的线程可以通过很多方式进入阻塞状态,当执行完所有操作后就进入死亡状态。
3.线程的实现方式
1.继承Thread类
2.实现Runnable接口,重写run方法
3.实现Callable接口,需要重写call()方法
(Callable是一种具有类型参数的泛型,它的类型参数表的是从方法call()中返回的值,并且必须使用ExecutorServices.submit()方法调用它)
package thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Test
public static void main(String[] args)
MyThread thread1 = new MyThread();
Thread thread2 = new Thread(new MyRunable());
ExecutorService threadPool = Executors.newSingleThreadExecutor();
Future<String> future = threadPool.submit(new MyCallable());
thread1.start();
thread2.start();
try
//获取返回值
System.out.println(future.get());
catch (InterruptedException | ExecutionException e)
e.printStackTrace();
finally
threadPool.shutdown();
class MyThread extends Thread
@Override
public void run()
System.out.println("MyThread"+ Thread.currentThread().getName());
class MyRunable implements Runnable
@Override
public void run()
System.out.println("MyRunable"+ Thread.currentThread().getName());
class MyCallable implements Callable<String>
@Override
public String call() throws Exception
System.out.println("MyCallable 线程:" + Thread.currentThread().getName());
return "MyCallable";
运行结果:
4.什么是线程同步,什么是线程安全
同步:当两个或两个以上的线程需要共享资源,通过同步方法限制资源在一次仅被一个线程占用
线程安全:线程安全就是多线程操作同一个对象不会产生数据污染,
线程同步一般来保护线程安全,final修饰的也是线程安全
5.什么是死锁,死锁发生的几个条件是什么
死锁就是当有两个或两个以上的线程都获得对方的资源,但彼此都不肯放开,处于僵持阶段,此时就造成了死锁
条件:两个或两个以上的线程,同时想要获得对方的资源,彼此又不肯放开
6.什么是同步和异步,分别用例子说明,同步有几种方式?
同步是排队去做事情,异步就是各做各的
7.原子性、可见性、有序性
事物有原子性,这个概念大概都清楚,即一个操作或多个操作要么执行的过程中不被任何因素打断,要么不执行。
可见性指当一个线程修改了共享变量的值,其它线程能够立即得知这个修改。Java 内存模型是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值来实现可见性的。
主要有有三种实现可见性的方式:
volatile
synchronized,对一个变量执行 unlock 操作之前,必须把变量值同步回主内存。
final,被 final 关键字修饰的字段在构造器中一旦初始化完成,并且没有发生 this 逃逸(其它线程通过 this 引用访问到初始化了一半的对象),那么其它线程就能看见 final 字段的值。
有序性指在本线程内观察,所有操作都是有序的。在一个线程观察另一个线程,所有操作都是无序的,无序是因为发生了指令重排序。在 Java 内存模型中,允许编译器和处理器对指令进行重排序,重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。
8.volatile关键字
1、volatile可以使变量在多个线程间可见(可见性)。
2、不能保证操作的原子性。
3、volatile 关键字通过添加内存屏障的方式来禁止指令重排,即重排序时不能把后面的指令放到内存屏障之前,实现有序性
9.synchronized和lock
Java 提供了两种锁机制来控制多个线程对共享资源的互斥访问,第一个是 JVM 实现的 synchronized,而另一个是 JDK 实现的 ReentrantLock。
synchronized(隐式锁):在需要同步的对象中加入此控制,synchronized 可以加在方法上,也可以加在特定代码块中,括号中表示需要锁的对象。
lock(显示锁):需要显示指定起始位置和终止位置。一般使用 ReentrantLock 类做为锁,多个线程中必须要使用一个 ReentrantLock 类做为对象才能保证锁的生效。且在加锁和解锁处需要通过 lock() 和 unlock() 显示指出。所以一般会在 finally 块中写 unlock() 以防死锁。
synchronized 原始采用的是 CPU 悲观锁机制,即线程获得的是独占锁。独占锁意味着其他线程只能依靠阻塞来等待线程释放锁。
Lock 用的是乐观锁方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。乐观锁实现的机制就是 CAS 操作。
10.死锁
第一种synchronized方式死锁:
线程thread1先获取锁locka,然后在同步块里嵌套竞争锁lockb。而线程thread2先获取锁lockb,然后在同步块里嵌套竞争锁locka(此时已经被线程thread1拥有,而thread1在等待lockb,而lockb被thread2拥有,thread2在等待locka……无线循环)。
public class SyncDeadLock
private static Object locka = new Object();
private static Object lockb = new Object();
public static void main(String[] args)
new SyncDeadLock().deadLock();
private void deadLock()
Thread thread1 = new Thread(new Runnable()
@Override
public void run()
synchronized (locka)
try
System.out.println(Thread.currentThread().getName() + " get locka ing!");
Thread.sleep(500);
System.out.println(Thread.currentThread().getName() + " after sleep 500ms!");
catch (Exception e)
e.printStackTrace();
System.out.println(Thread.currentThread().getName() + " need lockb!Just waiting!");
synchronized (lockb)
System.out.println(Thread.currentThread().getName() + "get lockb ing!");
);
Thread thread2 = new Thread(new Runnable()
@Override
public void run()
synchronized (lockb)
try
System.out.println(Thread.currentThread().getName() + " get lockb ing!");
Thread.sleep(500);
System.out.println(Thread.currentThread().getName() + " after sleep 500ms!");
catch (Exception e)
e.printStackTrace();
System.out.println(Thread.currentThread().getName() + " need locka!Just waiting!");
synchronized (locka)
System.out.println(Thread.currentThread().getName() + "get locka ing!");
);
thread1.start();
thread2.start();
运行结果:
第二种concurrent包Lock错误使用,导致死锁:
lock.unlock();释放锁使用地方不规范,导致死锁不能正常释放
建议在finally块里去释放锁
11.乐观锁和悲观锁
悲观锁:假定会发生并发冲突,独占锁,屏蔽一切可能违反数据完整性的操作。
乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。乐观锁不能解决脏读的问题。
12.对象锁、类锁
1.一个锁的是类对象,一个锁的是实例对象
2.若类对象被lock,则类对象的所有同步方法全被lock
3.若实例对象被lock,则该实例对象的所有同步方法全被lock
参考资料:Synchronized(对象锁)和Static Synchronized(类锁)的区别
13.sleep和wait有什么区别
1.sleep 和 wait
①wait() 是 Object 的方法,而 sleep() 是 Thread 的静态方法;
②wait() 会释放锁,sleep() 不会。
2.有什么区别
①sleep() 方法(休眠)是线程类(Thread)的静态方法,调用此方法会让当前线程暂停执行指定的时间,将执行机会(CPU)让给其他线程,但是对象的锁依然保持,因此休眠时间结束后会自动恢复(线程回到就绪状态)。②wait() 是 Object 类的方法,调用对象的 wait() 方法导致当前线程放弃对象的锁(线程暂停执行),进入对象的等待池(wait pool),只有调用对象的 notify() 方法(或 notifyAll() 方法)时才能唤醒等待池中的线程进入等锁池(lock pool),如果线程重新获得对象的锁就可以进入就绪状态。
14.线程间是怎么通信的,通过调用几个方法来交互的
join()
在线程中调用另一个线程的 join() 方法,会将当前线程挂起,而不是忙等待,直到目标线程结束。
对于以下代码,虽然 b 线程先启动,但是因为在 b 线程中调用了 a 线程的 join() 方法,b 线程会等待 a 线程结束才继续执行,因此最后能够保证 a 线程的输出先于 b 线程的输出。
public class JoinExample
private class A extends Thread
@Override
public void run()
System.out.println("A");
private class B extends Thread
private A a;
B(A a)
this.a = a;
@Override
public void run()
try
a.join();
catch (InterruptedException e)
e.printStackTrace();
System.out.println("B");
public void test()
A a = new A();
B b = new B(a);
b.start();
a.start();
public static void main(String[] args)
JoinExample example = new JoinExample();
example.test();
运行结果:
wait() notify() notifyAll()
调用 wait() 使得线程等待某个条件满足,线程在等待时会被挂起,当其他线程的运行使得这个条件满足时,其它线程会调用 notify()(随机叫醒一个) 或者 notifyAll() (叫醒所有 wait 线程,争夺时间片的线程只有一个)来唤醒挂起的线程。
它们都属于 Object 的一部分,而不属于 Thread。
只能用在同步方法或者同步控制块中使用! 否则会在运行时抛出 IllegalMonitorStateExeception。
使用 wait() 挂起期间,线程会释放锁。这是因为,如果没有释放锁,那么其它线程就无法进入对象的同步方法或者同步控制块中,那么就无法执行 notify() 或者 notifyAll() 来唤醒挂起的线程,造成死锁。
public class WaitNotifyExample
public synchronized void before()
System.out.println("before");
notifyAll();
public synchronized void after()
try
wait();
catch (InterruptedException e)
e.printStackTrace();
System.out.println("after");
public static void main(String[] args)
ExecutorService executorService = Executors.newCachedThreadPool();
WaitNotifyExample example = new WaitNotifyExample();
executorService.execute(() -> example.after());
executorService.execute(() -> example.before());
15.线程池
线程是稀缺资源,如果被无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,合理的使用线程池对线程进行统一分配、调优和监控,有以下好处:
1.降低资源消耗;
2.提高响应速度;
3.提高线程的可管理性。
以上是关于线程基础知识的主要内容,如果未能解决你的问题,请参考以下文章