进程与线程的关系与区别:
1.进程包含线程
2.进程之间相互隔离
3.同一个进程的线程之间共享一些资源,尤其是内存资源,一个线程挂了,很可能导致整个进程无法工作
4.进程是系统资源分配和管理的最小单位。线程是最小的
在java中,用Thread类来表示线程
java中的Thread对象就和操作系统内部的一个线程是一一对应的
线程的相关操作:
1.创建线程
a)通过继承Thread类,重写run方法
b)通过实现Runnable接口,把Runnable接口的实例赋值给Thread
c)通过Thread的匿名内部类实现(类似于a)
Thread t = new Thread();
t.start();
d)通过Runnable的匿名内部类创建线程(类似于b)
e)使用lambda表达式创建线程 (java8 引入的语法)
Thread t = new Thread() -> {
System.out.println("Holle world!");
}
后台 线程,不影响整个进程的结束
前台线程,会影响整个进程的结束
调用start是创建一个新线程
调用run,只是一个方法调用,不会创建新线程
(run方法应该由start内部来调用,不应该手动调用)
中断线程:
a)让线程的入口方法执行完毕
b)使用Thread类提供的interrupt方法
Thread.currentThread().isinterrutped()
等待线程:
当我们创建多个线程 后,每个线程都是一个独立的执行流
这些线程的执行先后顺序不确定,完全取决于操作系统的调度
等待机制就是一种确定线程先后的顺序的方式。确定线程的结束顺序
无法保证线程的开始顺序,但是能保证线程的结束顺序
t.join()
当main方法执行到join()是会阻塞等待,等待t线程执行结束
thread类的常见方法:
1.Thread() 创建线程对象
2.Thread(Runnable target) 使用 Runnable 对象创建线程对象
3.Thread(String name) 创建线程对象,并命名
thread的常见属性
ID getId()
名称 getName()
状态 getState()
优先级 getPriority()
是否后台线程 isDaemon()
是否存活 isAlive()
是否被中断 isInterrupted()
ID 是线程的唯一标识,不同线程不会重复
名称是各种调试工具用到
状态表示线程当前所处的一个情况,下面我们会进一步说明
优先级高的线程理论上来说更容易被调度到
关于后台线程,需要记住一点:JVM会在一个进程的所有非后台线程结束后,才会结束运行。
是否存活,即简单的理解,为 run 方法是否运行结束了
run方法是创建一个线程但未启动,启动线程需要start方法。
多线程的不安全问题
1.原子性
2.内存可见性
3.指令重排序
解决方法:
1.synchronized关键字
synchronized的底层是使用操作系统的mutex lock实现的。
当线程释放锁时,JMM会把该线程对应的工作内存中的共享变量刷新到主内存中。当线程获取锁时,JMM会把该线程对应的本地内存置为无效。从而使得被监视器保护的临界区代码
必须从主内存中读取共享变量
synchronized用的锁是存在Java对象头里的
synchronized同步快对同一条线程来说是可重入的,不会出现自己把自己锁死的问题
2.violet关键字
用于修饰变量,保证变量的原子性、代码循序性,防止发生脏读。
3.wait()方法
wait()的实际作用就是使线程进入等待状态,wait()是object类下的方法,调用wait()方法使线程停止运行,直到调用notify()或者notifyAll()唤醒。
notify()是唤醒单个线程
notifyAll()唤醒所有等待线程
wait和sleep
其实理论上 wait 和 sleep 完全是没有可比性的,因为一个是用于线程之间的通信的,一个是让线程阻塞
对比:
- wait 之前需要请求锁,而wait执行时会先释放锁,等被唤醒时再重新请求锁。这个锁是 wait 对像
上的 monitor lock - sleep 是无视锁的存在的,即之前请求的锁不会释放,没有锁也不会请求。
- wait 是 Object 的方法
- sleep 是 Thread 的静态方法
多线程单例模式
饿汉模式
public class hungrysingle{
hungrysingle h1 = new hungrysingle();
private hungrysingle(){}
public static hungrysingle geth1(){
return h1;
}
}
懒汉模式单线程版
class Singleton {
private static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
懒汉模式多线程版
class Singleton {
private static Singleton instance = null;
private Singleton() {}
public synchronized static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
懒汉模式多线程版(二次判断) 性能高
class Singleton {
private static volatile Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
线程池
线程池的好处就是减少每次启动、销毁线程的损耗。
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class ThreadPool {
private static class Worker extends Thread {
}
private BlockingQueue<Runnable> jobQueue;
private int nCurrentThreads;
private int nThreads;
private Worker[] workers;
public ThreadPool(int nThreads, int nCachedJobs) {
this.jobQueue = new ArrayBlockingQueue<>(nCachedJobs);
this.nCurrentThreads = 0;
this.nThreads = nThreads;
this.workers = new Worker[nThreads];
}
public void execute(Runnable command) throws InterruptedException {
if (nCurrentThreads < nThreads) {
Worker worker = new Worker();
workers[nCurrentThreads++] = worker;
worker.start();
} else {
jobQueue.put(command);
}
}
}