JAVA多线程的总结学习-基础

一、概念

1、进程与线程

进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程。

(进程是资源分配的最小单位)

线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。

(线程是cpu调度的最小单位)

多进程是指操作系统能同时运行多个任务(程序)。

多线程是指在同一程序中有多个顺序流在执行。

在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。


2、线程实现方式

(1)继承Thread

(2)实现Runable接口

(3)实现Callable接口,配合FutureTask使用


3、Thread和Runable的区别总结:

如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享


4、实现Runnable接口比继承Thread类所具有的优势:

1):适合多个相同的程序代码的线程去处理同一个资源

2):可以避免java中的单继承的限制

3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立

4):线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类


5、线程生命周期

(1)新建状态(New):新创建了一个线程对象。
(2)就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
(3)运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
(4)阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
a、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)
b、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
c、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程                                             终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(注意,sleep是不会释放持有的锁)
(5)死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期
                           

6、线程中方法介绍

(1)调整线程优先级:Java线程有优先级,优先级高的线程会获得较多的运行机会。线程的优先级用整数表示,取值范围是1~10。
  Thread类有以下三个静态常量:
static int MAX_PRIORITY:线程可以具有的最高优先级,取值为10。
static int MIN_PRIORITY: 线程可以具有的最低优先级,取值为1。
static int NORM_PRIORITY:分配给线程的默认优先级,取值为5。
  方法:   setPriority()设置线程的优先级
     getPriority()获取线程的优先级
  每个线程都有默认的优先级。主线程的默认优先级为Thread.NORM_PRIORITY。
线程的优先级有继承关系,比如A线程中创建了B线程,那么B将和A具有相同的优先级。
 
(2)线程睡眠:Thread.sleep(long millis)方法,使线程转到阻塞状态。millis参数设定睡眠的时间,以毫秒为单位。当睡眠结束后,就转为就绪(Runnable)状态。sleep()平台移植性好。
 
(3)线程等待:Object类中的wait()方法,导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 唤醒方法。这个两个唤醒方法也是Object类中的方法,行为等价于调用 wait(0) 一样。

(4)线程唤醒:Object类中的notify()方法,唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的。类似的方法还有一个notifyAll(),唤醒在此对象监视器上等待的所有线程。
 
(5)线程让步:Thread.yield() 方法,暂停当前正在执行的线程对象,把执行机会让给相同或者更高优先级的线程。
 
(6)线程加入:join()方法,等待其他线程终止。在当前线程中调用另一个线程的join()方法,则当前线程转入阻塞状态,直到另一个进程运行结束,当前线程再由阻塞转为就绪状态。

二、例子

1、继承Thread类

package thread;

//通过继承Thread 实现线程
public class MyThread extends Thread{
	
	private int i=0;
	
	public void run(){
		System.out.println("in MyThread");
		for(i=0;i<50;i++){
			System.out.println(Thread.currentThread().getName()+i);
		}
	}
	
	public static void main(String[] args) {
		for(int i=0;i<50;i++){
			System.out.println(Thread.currentThread().getName()+i);
			if(i==25){
				MyThread t1 = new MyThread();
				MyThread t2 = new MyThread();
				
				t1.start();
				t2.start();
			}
		}
	}
	
}

说明:
继承Thread,重写run方法
直接new对象MyThread,start启动线程

2、实现Runnable接口
package thread;

//通过实现Runnable接口, 实现线程
public class MyRunnable implements Runnable{
	
	private int i = 0;
	//private Boolean stopFlag = false;
	
	@Override
	public void run() {
		System.out.println("in MyRunnable");
		for(i=0;i<100;i++){
			System.out.println(Thread.currentThread().getName()+i);
		}
	}
	/*
	public void stopThread(){
		this.stopFlag = true;
	}*/
	
	public static void main(String[] args) {
		for(int i=0;i<50;i++){
			System.out.println(Thread.currentThread().getName()+i);
			if(i==15){
				MyRunnable r = new MyRunnable();
				Thread t = new Thread(r);
				
				t.start();
			}
		}
	}
}

3、实现Callable接口

package thread;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

//通过实现Callable接口, 实现线程
public class MyCallable implements Callable<Integer>{

	private int i = 0;
	
	@Override
	public Integer call() throws Exception {
		int sum = 0;
		for(i=0;i<3;i++){
			System.out.println(Thread.currentThread().getName()+i);
			sum +=i;
		}
		return sum;
	}
	
	public static void main(String[] args) {
		Callable<Integer> c = new MyCallable();
		FutureTask<Integer> ft = new FutureTask<Integer>(c);
		for(int i=0;i<10;i++){
			System.out.println(Thread.currentThread().getName()+i);
			if(i==5){
				Thread t = new Thread(ft);
				t.start();
			}
		}
		System.out.println("FOR循环执行完毕=======");
		try {
			int sum = ft.get();
			System.out.println(sum);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
	}

	
}

说明:
实现Callable接口,实现call方法
需要FutureTask配合使用

4、线程同步synchronized

package thread;

//线程同步
public class ThreadTest extends Thread{
	private int count = 5;
	
	//延伸问题,竞争锁(当其中一个线程释放锁时,其他同时进行竞争,导致cpu瞬间飚满。。。。)
	public void run(){
		count--;
		System.out.println(Thread.currentThread().getName()+"=== count:"+count);
	}
	
	public static void main(String[] args){
		ThreadTest t = new ThreadTest();
		Thread t1 = new Thread(t,"t1");
		Thread t2 = new Thread(t,"t2");
		Thread t3 = new Thread(t,"t3");
		Thread t4 = new Thread(t,"t4");
		Thread t5 = new Thread(t,"t5");
		
		t1.start();
		t2.start();
		t3.start();
		t4.start();
		t5.start();
	}
}

运行结果: count并没有一次递减
t2=== count:3
t3=== count:2
t5=== count:1
t4=== count:0
t1=== count:3

在run方法上加上synchronized修饰,运行结果如下:
t2=== count:4
t5=== count:3
t4=== count:2
t3=== count:1
t1=== count:0
说明:加上synchronized,count顺序执行了,因为加了synchronized,一个线程进入就锁死了,得释放时,其他线程才能再进入,再运行

5、多个对象,多个线程synchronized

package thread;

//多个对象,多个线程,同步锁
public class MultiThread {
	private static int num = 0;
	
	public synchronized void printNum(String tag){
		if("a".equals(tag)){
			num = 100;
			System.out.println("tag="+tag+"---start---");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}else{
			num = 200;
			System.out.println("tag="+tag+"---start---");
		}
		System.out.println("num="+num);
	}
	
	public static void main(String[] args){
		
		//两个不同的对象
		final MultiThread m1 = new MultiThread();
		final MultiThread m2 = new MultiThread();
		
		Thread t1 = new Thread(new Runnable(){

			@Override
			public void run() {
				m1.printNum("a");
			}
			
		});
		
		Thread t2 = new Thread(new Runnable(){

			@Override
			public void run() {
				m2.printNum("b");
			}
			
		});
		
		
		t1.start();
		t2.start();
	
	}
}

运行结果:线程t1对应的num值未打印
tag=a---start---
tag=b---start---
num=200
num=200
说明:虽然已经加上了synchronized关键字修饰,但是因为M1、m2是两个不同的对象,他们对应的t1和t2是分别的线程,所以只加synchronized,并不能锁定住两个对象的               执行顺序

在printNum方法上,加上static关键字,运行结果如下
tag=a---start---
num=100
tag=b---start---
num=200
说明:静态方法加上synchronized关键字,则线程调用方法时,获得的是类级别的锁,而不是方法级别的锁

6、锁重入

package thread;

/**
 * 锁重入
 * 每个方法都加了synchronized关键字
 * 方法1里调用了方法2,方法2里调用了方法3
 * 当一个线程获取了一个对象的锁后,当再次请求这个对象时,可以再次获得该对象的锁
 */
public class SyncDubbo1 {
	public synchronized void method1(){
		System.out.println("method1.....");
		method2();
	}
	
	public synchronized void method2(){
		System.out.println("method2.....");
		method3();
	}
	
	public synchronized void method3(){
		System.out.println("method3.....");
	}
	
	public static void main(String[] args) {
		final SyncDubbo1 sd = new SyncDubbo1();
		
		Thread t = new Thread(new Runnable(){

			@Override
			public void run() {
				sd.method1();
			}
			
		});
		
		t.start();
	}
}

运行结果:
method1.....
method2.....
method3.....

7、脏读

package thread;

/**
 * 脏读
 */
public class DirtyRead {
	private String name="zhangsan";
	private String pwd = "123";
	
	public synchronized void setValue(String name, String pwd){
		this.name = name;
				
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		
		this.pwd = pwd;
		System.out.println("当前的用户:"+name+",密码:"+pwd);
	}
	
	public void getValue(){
		System.out.println("当前的用户:"+name+",密码:"+pwd);
	}
	
	public static void main(String[] args){
		final DirtyRead dr = new DirtyRead();
		
		Thread t = new Thread(new Runnable(){

			@Override
			public void run() {
				dr.setValue("lisi", "456");
			}
			
		});
		t.start();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		dr.getValue();
	}
	
}

运行结果:
当前的用户:lisi,密码:123
当前的用户:lisi,密码:456
问题:已经变更了用户和密码,但是根据运行结果知道,密码并未变更

给getValue()方法加上synchronized关键字,运行结果如下:
当前的用户:lisi,密码:456
当前的用户:lisi,密码:456
说明:
* DirtyRead类里有两个方法:setValue、getValue
 * 当只给setValue加synchronized时,出现脏读现象
 * 所以脏读情况出现,是因为没有考虑整体的一个同步加锁情况
 * 避免脏读,给getValue方法加上synchronized

8、wait、notify线程应用

package thread;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
 * wait/notify 实现了线程之间的通信
 * wait/notify必须配合synchronized块使用
 * wait释放锁,notify不释放锁
 * 
 * 因为notify不释放锁,会造成不实时的问题
 * 解决方法:CountDownLatch(并发工具类),使用时不需要synchronized区块
 * 
 */
public class WaitNotifyThread {

	private static List list = new ArrayList();
	
	public static void add(){
		list.add("11111");
	}
	
	public int getSize(){
		return list.size();
	}
	
	public static void main(String[] args) {
		final WaitNotifyThread wnt = new WaitNotifyThread();
		
		final Object lock = new Object();
		
		Thread t1 = new Thread(new Runnable(){

			@Override
			public void run() {
				synchronized (lock) {
					for(int i=0;i<10;i++){
						wnt.add();
						System.out.println("当前线程"+Thread.currentThread().getName()+"添加一个元素");
						if(wnt.getSize() == 5){
							lock.notify();
							System.out.println("唤醒线程2...");
						}
					}
				}
			}
			
		},"t1");
		
		Thread t2 = new Thread(new Runnable(){

			@Override
			public void run() {
				synchronized (lock) {
					if(wnt.getSize() != 5){
						try {
							lock.wait();
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
					System.out.println("已唤醒,list大小=5,终止...");
					throw new RuntimeException();
				}
			}
			
		},"t2");
		
		t2.start();
		t1.start();
	}
}

运行结果:
当前线程t1添加一个元素
当前线程t1添加一个元素
当前线程t1添加一个元素
当前线程t1添加一个元素
当前线程t1添加一个元素
唤醒线程2...
当前线程t1添加一个元素
当前线程t1添加一个元素
当前线程t1添加一个元素
当前线程t1添加一个元素
当前线程t1添加一个元素
已唤醒,list大小=5,终止...
Exception in thread "t2" java.lang.RuntimeException
at thread.WaitNotifyThread$2.run(WaitNotifyThread.java:64)
at java.lang.Thread.run(Unknown Source)
说明:当容器大小=5时,唤醒线程2.
问题:因为notify不释放锁,会造成不实时的问题,用CountDownLatch(并发工具类)解决问题,使用时不需要synchronized区块
package thread;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
 * wait/notify 实现了线程之间的通信
 * wait/notify必须配合synchronized块使用
 * wait释放锁,notify不释放锁
 * 
 * 因为notify不释放锁,会造成不实时的问题
 * 解决方法:CountDownLatch(并发工具类),使用时不需要synchronized区块
 * 
 */
public class WaitNotifyThread {

	private static List list = new ArrayList();
	
	public static void add(){
		list.add("11111");
	}
	
	public int getSize(){
		return list.size();
	}
	
	public static void main(String[] args) {
		final WaitNotifyThread wnt = new WaitNotifyThread();
		
		final CountDownLatch cdl = new CountDownLatch(1);
		
		Thread t1 = new Thread(new Runnable(){

			@Override
			public void run() {
					for(int i=0;i<10;i++){
						wnt.add();
						System.out.println("当前线程"+Thread.currentThread().getName()+"添加一个元素");
						if(wnt.getSize() == 5){
							cdl.countDown();
							System.out.println("唤醒线程2...");
						}
					}
			}
			
		},"t1");
		
		Thread t2 = new Thread(new Runnable(){

			@Override
			public void run() {
					if(wnt.getSize() != 5){
						try {
							cdl.await();
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
					System.out.println("已唤醒,list大小=5,终止...");
					throw new RuntimeException();
			}
			
		},"t2");
		
		t2.start();
		t1.start();
	}
}

9、用wait/notify模拟queue

package thread;

import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 用wait/notify模拟queue(队列)
 * 队列:LinkedBlockingQueue  ArrayBlockingQueue
 * 方法:put:向队列里放对象,当队列长度达到上限时,put方法阻塞等待,直到再有空间,才执行
 *      take:从队列里取对象,当队列里空的时候,take方法阻塞等待,直到队列里再加入新的对象,再执行
 */
public class WaitNotifyQueue {

	//创建一个容器队列
	private LinkedList list = new LinkedList();
	
	//创建一个计数器
	private AtomicInteger count = new AtomicInteger(0);
	
	//上限、下限设置
	private final int minSize = 0;
	private final int maxSize ;
	
	private WaitNotifyQueue(int size){
		this.maxSize = size;
	}
	
	private final Object lock = new Object();
	//put方法
	public void put(Object o){
		synchronized (lock) {
			if(count.get() == this.maxSize){
				try {
					lock.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			list.add(o);
			count.incrementAndGet();
			lock.notify();
			System.out.println("成功增加一个对象"+o);
		}
	}
	
	public Object take(){
		Object o = null;
		synchronized (lock) {
			if(count.get() == this.minSize){
				try {
					lock.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			o = list.removeFirst();
			count.decrementAndGet();
			lock.notify();
			System.out.println("成功的移除了对象"+o);
		}
		return o;
	}
	
	public static void main(String[] args) {
		final WaitNotifyQueue wtq = new WaitNotifyQueue(5);
		wtq.put("a");
		wtq.put("b");
		wtq.put("c");
		wtq.put("d");
		wtq.put("e");
		
		Thread t1 = new Thread(new Runnable(){

			@Override
			public void run() {
				wtq.put("f");
				wtq.put("g");
			}
			
		},"t1");
		
		Thread t2 = new Thread(new Runnable(){

			@Override
			public void run() {
				wtq.take();
				wtq.take();
			}
			
		},"t2");
		
		t1.start();
		
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		t2.start();
	}
	
}


10、volatile声明线程变量

package thread;

/**
 * volatile 声明一个变量在多个线程间可见
 * 原因:jdk1.5之后,为Thread单独分配一个内存空间,存储线程用到的变量等内容,
 *      当外部修改变量时,线程内存空间内的变量值并没有进行修改,所以代码一直执行
 * 
 */
public class VolatileThread {
	private static volatile boolean flag = true;
	
	public void testVolatile(){
		System.out.println("---------start-----------");
		while(flag){
			
		}
		System.out.println("----------end-------------");
	}
	
	public static void main(String[] args) {
		final VolatileThread vt = new VolatileThread();
		
		Thread t = new Thread(new Runnable(){

			@Override
			public void run() {
				vt.testVolatile();
			}
			
		});
		
		t.start();
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		flag = false;
		System.out.println("flag=="+flag);
	}
}


11、volatile 不具有synchronized关键字的原子性DEMO
package thread;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * volatile 不具有synchronized关键字的原子性
 * 用atomic类库(原子类)使用,本身支持多线程并发的原子操作
 * 解析代码:如果volatile 具有原子性,那么count的值最终必定为10000(1000×10)
 *          但是运行结果是随机的
 *          用Atomic原子类,最终结果是正确的
 */
public class VolatileNoAtomicThread extends Thread{
	//private static Integer count = 0;
	private static AtomicInteger count = new AtomicInteger(0);
	
	public static void addCount(){
		for(int i=0;i<1000;i++){
			//count++;
			count.incrementAndGet();
		}
		System.out.println("count:"+count);
	}
	
	public void run() {
		addCount();
	}
	
	public static void main(String[] args) {
		VolatileNoAtomicThread[] arr = new VolatileNoAtomicThread[10];
		for(int i=0;i<10;i++){
			arr[i] = new VolatileNoAtomicThread();
		}
		
		for(int i=0;i<10;i++){
			arr[i].start();
		}
	}
	
}


























评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值