基本概念
程序
是为了完成特定任务、用某种语言编写的一组指令的集合
进程
线程是程序的一次执行过程,进程是执行的程序
进程是资源分配的单位
线程
线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务
每个线程拥有独立的运行栈和程序计数器
一个Java应用程序java.exe,最少有三个线程:main()主线程,gc()垃圾回收线程,异常处理线程。
并行
多个cpu同时执行多个任务。
并发
一个cpu同时执行多个任务。
多线程的四种实现方式
继承 Thread类并重写run()
package multiThread;
public class ByThread extends Thread{
@Override
public void run() {
for(int i = 0; i < 10; i++){
System.out.println("新线程:"+i);
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
ByThread byThread = new ByThread();
byThread.start();
for(int i = 0; i < 10; i++){
System.out.println("主线程线程:"+i);
sleep(10);
}
}
}
实现Runnable接口重写run()
package multiThread;
public class ByRunnable implements Runnable{
@Override
public void run() {
for(int i = 0; i < 10; i++){
System.out.println(Thread.currentThread().getName());
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
ByRunnable byRunnable = new ByRunnable();
new Thread(byRunnable).start();
for(int i = 0; i < 10; i++){
System.out.println(Thread.currentThread().getName());
Thread.currentThread().sleep(10);
}
}
}
实现Callable接口重写call方法
package multiThread;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class ByCallable implements Callable {
@Override
public Object call() throws Exception {
for(int i = 0; i < 10; i++){
System.out.println(Thread.currentThread().getName());
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
public static void main(String[] args){
ByCallable byCallable = new ByCallable();
FutureTask futureTask = new FutureTask(byCallable);
new Thread(futureTask).start();
for(int i = 0; i < 10; i++){
System.out.println(Thread.currentThread().getName());
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
同步代码块
synchronized(同步监视器){
//需要被同步的代码
}
- 操作共享数据的代码即为需要被同步的代码。
- 共享数据:带哦个线程共同操作的变量。比如:ticket就是共享数据。
- 同步监视器,俗称:所。仍和一个类的对象都可以充当锁。要求:多个线程必须要共用一把锁。
- 同步的方式,解决了线程的安全问题。—好处
- 操作同步代码块时,只能有一个线程参与,其他线程等待。相当于是一个单线程的过程,效率低。–坏处
同步方法
默认的同步监视器默认为:this
当使用的线程类不唯一但操作的数据唯一时,需要将同步方法改为静态方法。
lock
需要手动实例化 lock, 手动锁定lock,手动解锁
JDK1.5新增,建议优先使用Lock
Lock -> 同步代码块 -> 同步方法
volatile
volatile修饰符适用于以下场景:
某个属性被多个线程共享,其中有一个线程修改了此属性,其他线程可以立即得到修改后的值,比如booleanflag;或者作为触发器,实现轻量级同步。
volatile属性的读写操作都是无锁的,它不能替代synchronized,因为它没有提供原子性和互斥性。因为无锁,不需要花费时间在获取锁和释放锁_上,所以说它是低成本的。
volatile只能作用于属性,我们用volatile修饰属性,这样compilers就不会对这个属性做指令重排序。volatile提供了可见性,任何一个线程对其的修改将立马对其他线程可见,volatile属性不会被线程缓存,始终从主 存中读取。
volatile提供了happens-before保证,对volatile变量v的写入happens-before所有其他线程后续对v的读操作。
volatile可以使得long和double的赋值是原子的。volatile可以在单例双重检查中实现可见性和禁止指令重排序,从而保证安全性。