多进程、多线程、java多线程

实质:操作系统的执行单元是进程(程序),每个jvm实例都是一个进程,系统中可以同时有多个jvm实例,也就是有多个java进程,每个jvm中可以有多个线程,它们共享方法区和堆内存,所以线程间可以共享方法体中的常量、静态变量和堆内存中的全局对象。多个进程之间则完全不能共享内存。(同一个程序运行多次就是多个进程)

比较

  • 多进程:充分利用多个cpu,真正的同时运行。
  • 多线程:充分利用单个cpu,通过将cpu时间切片分给多个线程使用,减少单线程时等待IO导致的cpu等待,充分利用cpu计算资源。

多线程状态

  1. 调用join()和sleep()方法,sleep()时间结束或被打断,join()中断,IO完成都会回到Runnable状态,等待JVM的调度。
  2. 调用wait(),使该线程处于等待池(wait blocked pool),直到notify()/notifyAll(),线程被唤醒被放到锁定池(lock blocked pool ),释放同步锁使线程回到可运行状态(Runnable)
  3. 对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();
}

http://www.noobyard.com/article/p-zmejnohc-hw.html

http://www.noobyard.com/article/p-ragctmna-cz.html

http://www.noobyard.com/article/p-xmkmnyxh-ct.html

https://www.cnblogs.com/lemingyin/p/8878513.html

https://www.cnblogs.com/bsjl/p/7693029.html

https://www.cnblogs.com/bsjl/p/7693029.html

http://www.noobyard.com/article/p-wlfnzmug-sg.html