多线程
Java线程生命周期
新建状态(
New
):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread()
;
就绪状态(
Runnable
):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;
运行状态(
Running
):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;
阻塞状态(
Blocked
):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:
1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
2.同步阻塞 – 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
3.其他阻塞 – 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
死亡状态(
Dead
):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
每个线程拥有自己独立的栈、程序计数器
多个线程共享同一个方法区、堆
多线程常用方法
start()--->启动当前线程,并调用run()
run()--->通常重写父类方法 将要执行的操作写进此方法
currentThread()--->静态方法 返回线程
getName()--->返回线程名
setName()--->设置线程名
yield()--->释放当前cpu执行权 重新分配也可能继续此线程
join()--->在线程a中调用b的join() a线程堵塞 直到b线程完全执行以后 线程a才结束堵塞 优先级高于yield()
stop()--->已过时 强行结束当前线程
sleep(long millitime)--->让当前线程阻塞指定时间
isAlive()--->判断线程是否存活
wait()与sleep()异同
相同:都能使线程阻塞
不同:声明位置不同,sleep()在Thread
类中,wait()在Object
类中,sleep可以在任何场景下使用 wait只能在同步代码块中。
sleep()不会释放监视器,wait()释放监视器synchronized
。
创建多线程方法
多线程方法一
继承
Thread
类,重写run()
,然后new
对象t1
,调用t1.strat()
,执行子类重写的run()
。
/**
* 多线程创建:方法一 继承Thread类
*/
class MyThread extends Thread{
public MyThread(String name){
setName(name);
}
@Override
public void run() {
for (int i=0;i<100;i=i+2){
System.out.println(currentThread().getName() + ":" + i);
}
}
}
public class ThreadTest {
public static void main(String[] args) {
MyThread t1 = new MyThread("线程一");
t1.start();
//匿名子类
new Thread(){
@Override
public void run() {
for (int i=1;i<100;i=i+2){
System.out.println(currentThread().getName() + ":" + i);
}
}
}.start();
Thread.currentThread().setName("主线程");
for (int i=1;i<100;i++){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
多线程方法二
实现Runnable接口,重写
run()
,然后new对象target,再new Thread(target)
(传参接口)对象t1 调用t1.strat()
,执行Thread类的run()。
Thread类的run()
先判断target是否为空,如果不为空就调用target.run()
,target.run()被子类MyTgread重写,所以执行子类重写的run()
。
/**
* 创建多线程方式二 实现Runnable接口
*/
class MThread implements Runnable{
@Override
public void run() {
for (int i=0;i<100;i=i+2){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
public class ThreadTest2 {
public static void main(String[] args) {
MThread m1 = new MThread();
Thread t1 = new Thread(m1);
Thread t2 = new Thread(m1);
t1.start();
t2.start();
}
}
多线程方法三
MyTgread类实现Callable接口 重写
call()
,然后new对象target,再new FutureTask(target)
对象futuretask,调用futuretask.get()
方法返回值。
在newThread(futuretask)并调用start()
方法。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* 创建多线程三 实现Callable接口
*/
class NumThread implements Callable{
@Override
public Object call() throws Exception {
int sum = 0;
for (int i = 1; i <=100 ; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i);
sum+=i;
}
return sum;
}
}
public class ThreadTest3 {
public static void main(String[] args) {
//创建Callable接口实现类对象
NumThread numthread = new NumThread();
//将对象作为实参传FutureTask构造器中,创建FutureTask对象
FutureTask futuretask = new FutureTask(numthread);
FutureTask futuretask2 = new FutureTask(numthread);
//将FutureTask对象作为实参传到Thread构造器中并调用start方法
new Thread(futuretask).start();
new Thread(futuretask2).start();
Object sum;
{
try {
//get方法返回即为FutureTask构造器中参数Callable接口实现类对象重写的call方法
sum = futuretask.get();
System.out.println("总和为" + sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
三比二强大
call()
相比 run()
有返回值 可以抛异常Callable
支持泛型
多线程方法四(推荐)
使用线程池,提高响应速度,降低资源消耗,便于管理。
corePoolSize 设置核心池大小
maxinumPoolSize 最大线程数
keepAliveTime 设置线程没有任务时多长时间后会终止
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 创建多线程方法四 使用线程池
*/
class Run implements Runnable{
@Override
public void run() {
for (int i = 1; i < 100; i=i+2) {
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
}
}
public class ThreadTest4 {
public static void main(String[] args) {
//提供指定线程数量的线程池,ExecutorService是抽象类,service此时不能进行"."
ExecutorService service = Executors.newFixedThreadPool(10);
//service对象是ThreadPoolExecutor类
System.out.println(service.getClass());
//强转之后就可以进行设置
ThreadPoolExecutor service2 = (ThreadPoolExecutor)service;
//执行指定的线程操作 需要实现Runnable接口或Callable接口
service2.execute(new Run());
service2.execute(new Run());
service2.execute(new Run());
//关闭连接池
service.shutdown();
}
}
开发中通常选择实现接口,解决单继承的局限性,更适合处理多个线程共享数据的情况。
解决多线程安全问题
synchronize
(有代码块锁和方法锁)与ReentrantLock
(JDK5.0之后,需要new对象,只有代码块锁,配合try finally
使用)。
异同:前者在执行完相应代码块以后,自动释放同步监视器,后者需要手动,启动同步lock()
,结束unlock()
唯一本类 本对象
synchronized (Window.class||this) {
//代码块
}
//非静态同步方法 监视器是this本对象的 多个对象时慎用
public synchronized void methon(){
//方法体
}
//静态同步方法 随着类的加载而加载 所以监视器是本类的
public static synchronized void methon(){
//方法体
}
关于同步方法的使用
//同步方法处理继承Thread线程安全问题
class Window_ extends Thread{
//三个窗口同时售100张票
private static int ticket = 100;
//是否有余票
private static boolean isFlag = true;
//构造方法设置线程名字
public Window_(String name){
super(name);
}
@Override
public void run() {
while (isFlag) {
methon();
}
}
//static静态方法,所有线程对象共用
private static synchronized void methon(){
if (ticket>0)
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket--);
else
isFlag = false;
}
}
public class WindowMethon {
public static void main(String[] args) {
Window_ t1 = new Window_("窗口一");
Window_ t2 = new Window_("窗口二");
Window_ t3 = new Window_("窗口三");
t1.start();
t2.start();
t3.start();
}
}
关于同步代码块的使用
//同步代码块处理继承Thread线程安全问题
class Window extends Thread{
//三个窗口同时售100张票
private static int ticket = 100;
public Window(String name){
super(name);
}
@Override
public void run() {
while (true) {
//代码块 这里传参不能为对象,否则会形成每个对象都独立拥有一个锁
synchronized (Window.class) {
if (ticket>0)
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket--);
else
break;
}
}
}
}
public class WindowTest {
public static void main(String[] args) {
Window t1 = new Window("窗口一");
Window t2 = new Window("窗口二");
Window t3 = new Window("窗口三");
t1.start();
t2.start();
t3.start();
}
}
关于Lock锁的使用
//Lock处理继承Thread线程安全问题
class Window3 extends Thread{
private static int ticket = 100;
static ReentrantLock lock = new ReentrantLock();
public Window3(String name){
super(name);
}
@Override
public void run() {
while (true) {
try{
//lock锁
lock.lock();
if (ticket>0)
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket--);
else
break;
}finally {
//手动释放
lock.unlock();
}
}
}
}
public class WindowLock {
public static void main(String[] args) {
Window3 t1 = new Window3("窗口一");
Window3 t2 = new Window3("窗口二");
Window3 t3 = new Window3("窗口三");
t1.start();
t2.start();
t3.start();
}
}
注意:
继承Thread类慎用 public synchronized void methon();
new多个对象就会有多个public synchronized void methon()
通常加 static
关于死锁
指在执行并发计算时,一组进程中的每个进程都在等待包括自身在内的其他进程释放资源的一种现象。
示例
//死锁问题
public class Sisuo {
public static void main(String[] args) {
StringBuffer a = new StringBuffer();
StringBuffer b = new StringBuffer();
new Thread(){
@Override
public void run() {
synchronized (a){
try {
//睡眠1s,增大死锁概率
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//此时双方都在等待对方释放自己所需要的资源
synchronized (b){
}
}
}
}.start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (b){
try {
//睡眠1s,增大死锁概率
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//此时双方都在等待对方释放自己所需要的资源
synchronized (a){
}
}
}
}).start();
}
}