多进程、多线程、java多线程
实质:操作系统的执行单元是进程(程序),每个jvm实例都是一个进程,系统中可以同时有多个jvm实例,也就是有多个java进程,每个jvm中可以有多个线程,它们共享方法区和堆内存,所以线程间可以共享方法体中的常量、静态变量和堆内存中的全局对象。多个进程之间则完全不能共享内存。(同一个程序运行多次就是多个进程)
比较
- 多进程:充分利用多个cpu,真正的同时运行。
- 多线程:充分利用单个cpu,通过将cpu时间切片分给多个线程使用,减少单线程时等待IO导致的cpu等待,充分利用cpu计算资源。
多线程状态:
- 调用join()和sleep()方法,sleep()时间结束或被打断,join()中断,IO完成都会回到Runnable状态,等待JVM的调度。
- 调用wait(),使该线程处于等待池(wait blocked pool),直到notify()/notifyAll(),线程被唤醒被放到锁定池(lock blocked pool ),释放同步锁使线程回到可运行状态(Runnable)
- 对Running状态的线程加同步锁(Synchronized)使其进入(lock blocked pool ),同步锁被释放进入可运行状态(Runnable)。
多线程的实现:实现Runnable接口、继承Thread类
public class ThreadExample{
public static void main(String[] args){
//继承Thread的启动方法
Thread t1=new MyThread();
t1.start();
//实现Runnable的启动方法
Thread t2=new Thread(new MyThread2());
t2.start();
}
}
class MyThread extends Thread{
@Override
public void run() {
try {
Thread.sleep(Math.round(Math.random()*100));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("1");
}
}
class MyThread2 implements Runnable{
@Override
public void run() {
try {
Thread.sleep(Math.round(Math.random()*100));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("2");
}
}
多线程间通信:
由于多线程共享堆内存,可以在主程序中创建对象封装数据,作为参数传给每个子线程,则子线程都可以对该对象做操作。
public class MultyThreadShareData {
public static void main(String[] args){
ShareData shareData = new ShareData();
new Thread(new MyIncreRunnable(shareData)).start();
new Thread(new MyDecreRunanble(shareData)).start();
}
}
class MyIncreRunnable implements Runnable{
private ShareData shareData;
public MyIncreRunnable(ShareData shareData) {
this.shareData = shareData;
}
@Override
public void run() {
for (int i = 1;i<=10;i++){
try {
Thread.sleep(Math.round(Math.random()*100));
} catch (InterruptedException e) {
e.printStackTrace();
}
shareData.increment();
}
}
}
class MyDecreRunanble implements Runnable{
private ShareData shareData;
public MyDecreRunanble(ShareData shareData) {
this.shareData = shareData;
}
@Override
public void run() {
for(int i=1;i<=10;i++){
try {
Thread.sleep(Math.round(Math.random()*100));
} catch (InterruptedException e) {
e.printStackTrace();
}
shareData.decrement();
}
}
}
// 共享数据,对共享数据的操作也在这个对象中完成
class ShareData{
private int count = 0;
public synchronized void increment(){
count++;
System.out.println(Thread.currentThread().getName()+" inc "+count);
}
public synchronized void decrement(){
count--;
System.out.println(Thread.currentThread().getName()+" dec " +count);
}
}
线程池
创建销毁线程代价较大,java中使用Executor框架创建各类线程池。
FixedThreadPool+Callable/Runnable(固定大小线程池+带返回结果/不带返回结果)
实用于数据多线程导入数据库、多线程爬虫
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
public class threads {
public static void main(String[] args) throws InterruptedException, ExecutionException {
// TODO Auto-generated method stub
int tasksize=8;//线程数
ExecutorService pool = Executors.newFixedThreadPool(tasksize);
//不带返回结果时直接pool.execute(new Runnable)
List<Future> list = new ArrayList<Future>();
for(int i=0;i<tasksize;i++){
list.add(pool.submit(new one(i)));
}
pool.shutdown();
//收集结果
String r="";
for(Future f:list){
r+=f.get()+" ";
}
System.out.println("OUTSIDE:"+r);
}
}
class one implements Callable<Object> {
int id;
public one(int i){
this.id=i;
}
public Object call() throws Exception {
//重写此方法,此处为线程内程序
int a=this.id;
Thread.currentThread().sleep((a%3)*1000);
System.out.println("INSIDE:"+a);
return a;
}
}
java内存模型(JMM)
每个线程从共享内存中复制数据到自己私有的内存,线程只能对自己的内存进行操作,然后将数据刷入共享内存。共享内存对应方法区和堆内存,私有内存对应栈区的部分区域。JMM限制了编译器和处理器的重排序,禁止会改变结果的重排序、放行不影响结果的重排序。
volatile关键词
每个线程在执行时将堆内存中的数据存到自己的栈中,多线程时可能主存中的变量已被其他线程改变,但本线程栈中的副本没变,导致一致性问题。使用volatile关键词要求线程修改数据后立即刷入主存,读取数据时直接从主存读取。
原子性、可见性、有序性
- 原子性:意味着操作要么全部成功,要么全部失败,synchronized和lock可以排他性的占有变量和方法,可以实现他们的原子性。
- 可见性:当一个线程修改了共享变量后,其他线程能够立即得知这个修改。synchronized和volatile都要求了立即刷新写入、从主存读取,都有可见性。
- 有序性:意味着线程内各语句的顺序是固定的。重排序会破坏有序性,synchronized和volatile限制里重排序,保证了有序性。
synchronized:原子性、可见性、有序性
volatile:可见性、有序性
synchronized、lock
- synchronized:会自动释放;在jvm层面(字节码限制)实现特性;上锁后其他线程完全禁止访问。
- lock:必须手动释放;是一个类;可以实现读锁、写锁,更灵活。
生产者消费者模型:一个线程生成数据,给其他线程使用。
其他小知识:
子线程异常终止,主线程和其他子线程不受影响。
Thread.holdsLock(Object ob);查看当前线程是否获得了某对象的锁
join:在一个线程中start另一个子线程会使两者同时运行,但是使用使用start()后再对子线程join(),就会等待子线程结束再继续运行主线程。
//此处加入join()后会先执行完成t1,然后main继续执行,再另启动一个子线程t2,t1、t2的顺序完全固定为串行。
public static void main(String[] args)throws InterruptedException{
ShareData shareData = new ShareData();
Thread t1=new Thread(new MyDecreRunanble(shareData));
t1.start();
t1.join();
Thread t2=new Thread(new MyIncreRunnable(shareData));
t2.start();
}
https://www.cnblogs.com/dolphin0520/p/3920373.html
https://www.cnblogs.com/wxd0108/p/5479442.html
https://www.cnblogs.com/dolphin0520/p/3932934.html
https://www.cnblogs.com/lemingyin/p/8878513.html
https://www.cnblogs.com/bsjl/p/7693029.html