多线程详解
定义
程序:指令和数据的有序集合,静态的概念; 进程:执行程序的一次执行过程,动态的概念,系统资源分配的单位 线程:一个进程包含若干个线程,一个进程至少有一个线程,是CPU调度和执行的单位
三种创建方式
Thread
public class TestThread1 extends Thread {
@override
public void run ( ) {
for ( int i = 0 ; i < 200 ; i++ ) {
system. out. println ( "我在看代码---" + i) ;
}
}
public static void main ( String[ ] args) {
TestThread1 testThread1 = new TestThread1 ( ) ;
testThread1. start ( ) ;
for ( int i = 0 ; i < 1000 ; i++ ) {
system. out. println ( "我在学习多线程--" + i) ;
}
}
}
Runnable
public class TestThread3 implements Runnable {
@override
public void run ( ) {
for ( int i = 0 ; i < 200 ; i++ ) {
system. out. println ( "我在看代码-——" + i) ;
}
}
public static void main ( string[ ] args) {
TestThread3 testThread3 = new TestThread3 ( ) ;
Thread thread = new Thread ( testThread3) ;
thread. start ( ) ;
for ( int i = 0 ; i < 1000 ; i++ ) {
system. out. println ( "我在学习多线程--" + i) ; }
}
}
Callable(重要)
public class TestCallable implements Callable {
@override
public Boolean call ( ) {
for ( int i = 0 ; i < 200 ; i++ ) {
system. out. println ( "我在看代码-——" + i) ;
}
return true ;
}
public static void main ( string[ ] args) {
TestCallable testCallable = new TestCallable ( ) ;
ExecutorService executorService = Executors. newFixedThreadPool ( 1 ) ;
Future< Boolean> future = executorService. submit ( testCallable) ;
boolean result = future. get ( ) ;
executorService. shutdownNow ( ) ;
for ( int i = 0 ; i < 1000 ; i++ ) {
system. out. println ( "我在学习多线程--" + i) ; }
}
}
线程状态
NEW 尚未启动的线程处于此状态。 RUNNABLE 在Java虚拟机中执行的线程处于此状态。 BLOCKED 被阻塞等待监视器锁定的线程处于此状态。 WAITING 正在等待另一个线程执行特定动作的线程处于此状态。 TIMED_WAITING 正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。 TERMINATED 已退出的线程处于此状态。
方法 说明 setPriority(int newPriority) 更改线程的优先级 static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠 void join() 等待该线程终止 static void yield() 暂停当前正在执行的线程对象,并执行其他线程 void interrupt() 中断线程,别用这个方式 boolean isAlive() 测试线程是否处于活动状态
线程停止
最好不要使用stop(),destory()方法,因为他们直接停止线程,容易丢失数据操作,建议使用一个标志位来标记是否停止.
package thread;
public class TestStop implements Runnable {
private boolean flag = true ;
@Override
public void run ( ) {
int i = 0 ;
while ( flag) {
System. out. println ( "run....Thread" + i++ ) ;
}
}
public void stop ( ) {
this . flag = false ;
}
public static void main ( String[ ] args) {
TestStop testStop = new TestStop ( ) ;
new Thread ( testStop) . start ( ) ;
for ( int i = 0 ; i < 1000 ; i++ ) {
System. out. println ( "main " + i) ;
if ( i == 900 ) {
testStop. stop ( ) ;
System. out. println ( "线程停止了" ) ;
}
}
}
}
线程休眠
Thread.sleep(1000); //休眠一秒 每一个对象都有一个锁,sleep不会释放锁 ;
线程礼让
Thread.yield() 礼让线程,让当前正在执行的线程暂停,但不阻塞 将线程从运行状态转为就绪状态 让cpu重新调度,礼让不一定成功!看CPU心情
线程强制执行
join(); Join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞
线程优先级
Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行。 线程的优先级用数字表示,范围从1~10. Thread.MIN_PRIORITY= 1; Thread.MAX_PRIORITY = 10; Thread.NORM_PRIORITY = 5;使用以下方式改变或获取优先级 getPriority() . setPriority(int xxx) 性能导致 优先级低只是意味着获得调度的概率低,并不是低就不会先被调用了
线程同步机制
由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突问题﹐为了保证数据在方法中被访问时的正确性,在访问时加入锁机制synchronized, 当一个线程获得对象的排它锁,独占资源﹐其他线程必须等待,使用后释放锁即可.存在以下问题:
一个线程持有锁会导致其他所有需要此锁的线程挂起; 在多线程竞争下﹐加锁﹐释放锁会导致比较多的上下文切换和调度延时,引起性能问题; 如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置﹐引起性能问题.
CopyOnWriteArrayList 线程安全,使用volatile
死锁
产生死锁的四个必要条件: 1.互斥条件:一个资源每次只能被一个进程使用。 2.请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。 3.不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。 4.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
Lock锁
从JDK 5.0开始,Java提供了更强大的线程同步机制——通过显式定义 同步锁对象来实现同步。同步锁使用Lock对象充当 java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象 ReentrantLock 类(可重入锁)实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁。
package thread. syn;
import java. util. concurrent. locks. ReentrantLock;
public class TestLock {
public static void main ( String[ ] args) {
TestLock2 testLock2 = new TestLock2 ( ) ;
new Thread ( testLock2) . start ( ) ;
new Thread ( testLock2) . start ( ) ;
new Thread ( testLock2) . start ( ) ;
}
}
class TestLock2 implements Runnable {
int ticketNums = 10 ;
private final ReentrantLock lock = new ReentrantLock ( ) ;
@Override
public void run ( ) {
while ( true ) {
try {
lock. lock ( ) ;
if ( ticketNums > 0 ) {
try {
Thread. sleep ( 1000 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
System. out. println ( ticketNums-- ) ;
} else {
break ;
}
} finally {
lock. unlock ( ) ;
}
}
}
}
Lock是显式锁(手动开启和关闭锁,别忘记关闭锁)synchronized是隐式锁,出了作用域自动释放 Lock只有代码块锁,synchronized有代码块锁和方法锁 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类) 优先使用顺序: Lock >同步代码块(已经进入了方法体,分配了相应资源)>同步方法(在方法体之外)
线程协作
生产者消费者模式
假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库﹐消费者将仓库中产品取走消费. .如果仓库中没有产品﹐则生产者将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止. 如果仓库中放有产品﹐则消费者可以将产品取走消费﹐否则停止消费并等待,直到仓库中再次放入产品为止.
方法名 作用 wait() 表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁 wait(long timeout) 指定等待的毫秒数 notify() 唤醒一个处于等待状态的线程 notifyAll() 唤醒同一个对象上所有调用wait()方法的线程﹐优先级别高的线程优先调度
管程法
package thread. PC;
public class TestPC {
public static void main ( String[ ] args) {
SynContainer container = new SynContainer ( ) ;
new Productor ( container) . start ( ) ;
new Consumer ( container) . start ( ) ;
}
}
class Productor extends Thread {
SynContainer synContainer;
public Productor ( SynContainer synContainer) {
this . synContainer = synContainer;
}
@Override
public void run ( ) {
for ( int i = 0 ; i < 100 ; i++ ) {
synContainer. push ( new Chicken ( i) ) ;
System. out. println ( "生产了" + i + "只鸡" ) ;
}
}
}
class Consumer extends Thread {
SynContainer synContainer;
public Consumer ( SynContainer synContainer) {
this . synContainer = synContainer;
}
@Override
public void run ( ) {
for ( int i = 0 ; i < 100 ; i++ ) {
System. out. println ( "消费了-->" + synContainer. pop ( ) . id + "鸡" ) ;
}
}
}
class Chicken extends Thread {
int id;
public Chicken ( int id) {
this . id = id;
}
}
class SynContainer {
Chicken[ ] chickens = new Chicken [ 10 ] ;
int count = 0 ;
public synchronized void push ( Chicken chicken) {
if ( count == chickens. length) {
try {
this . wait ( ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
}
chickens[ count] = chicken;
count++ ;
this . notifyAll ( ) ;
}
public synchronized Chicken pop ( ) {
if ( count == 0 ) {
try {
this . wait ( ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
}
count-- ;
Chicken chicken = chickens[ count] ;
this . notifyAll ( ) ;
return chicken;
}
}
信号灯法
package thread. PC;
public class TestPC2 {
public static void main ( String[ ] args) {
TV tv = new TV ( ) ;
new Player ( tv) . start ( ) ;
new Watcher ( tv) . start ( ) ;
}
}
class Player extends Thread {
TV tv;
public Player ( TV tv) {
this . tv = tv;
}
@Override
public void run ( ) {
for ( int i = 0 ; i < 20 ; i++ ) {
if ( i% 2 == 0 ) {
this . tv. play ( "唱歌" ) ;
} else {
this . tv. play ( "跳舞" ) ;
}
}
}
}
class Watcher extends Thread {
TV tv;
public Watcher ( TV tv) {
this . tv = tv;
}
@Override
public void run ( ) {
for ( int i = 0 ; i < 20 ; i++ ) {
tv. watch ( ) ;
}
}
}
class TV {
String voice;
boolean flag = true ;
public synchronized void play ( String voice) {
if ( ! flag) {
try {
this . wait ( ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
}
System. out. println ( "演员表演了:" + voice) ;
this . notifyAll ( ) ;
this . voice = voice;
this . flag = ! this . flag;
}
public synchronized void watch ( ) {
if ( flag) {
try {
this . wait ( ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
}
System. out. println ( "观看了:" + voice) ;
this . notifyAll ( ) ;
this . flag = ! this . flag;
}
}
线程池
背景∶经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。 好处:
提高响应速度(减少了创建新线程的时间) 降低资源消耗(重复利用线程池中线程,不需要每次都创建) 便于线程管理(…)
corePoolSize:核心池的大小 maximumPoolSize:最大线程数 keepAliveTime:线程没有任务时最多保持多长时间后会终止
JDK 5.0 起提供了线程池相关API: ExecutorService和ExecutorsExecutorService :真正的线程池接口。常见子类ThreadPoolExecutor
void execute(Runnable command)∶执行任务/命令,没有返回值,一般用来执行Runnable Future submit(Callable task):执行任务,有返回值,一般用来执行Callable void shutdown()∶关闭连接池 Executors :工具类、线程池的工厂类,用于创建并返回不同类型的线程池
package thread. pool;
import java. util. concurrent. ExecutorService;
import java. util. concurrent. Executors;
public class TestPool {
public static void main ( String[ ] args) {
ExecutorService service = Executors. newFixedThreadPool ( 10 ) ;
service. execute ( new MyThread ( ) ) ;
service. execute ( new MyThread ( ) ) ;
service. execute ( new MyThread ( ) ) ;
service. execute ( new MyThread ( ) ) ;
service. shutdown ( ) ;
}
}
class MyThread implements Runnable {
@Override
public void run ( ) {
System. out. println ( Thread. currentThread ( ) . getName ( ) ) ;
}
}