java的多线程机制(入门篇)

java的多线程机制(入门篇)

多线程的概念

程序、进程和多任务

(1)程序:是数据描述与操作代码的集合,是应用程序执行的脚本。
(2)进程:程序的一次执行过程,是操作系统运行程序的基本单位,程序是静态的,进程是动态的。
(3)指在一个系统中可以运行多个程序,即有多个独立运行的任务,每一个任务对应一个进程

线程

是比进程更小的运行单位,是程序中单个顺序的流量控制

多线程

指的是在一个程序中,可以定义多个线程并同时运行它们。
优点:同类多线程共享一块内存空间和一组系统资源,可以减少花费。
**多线程与多任务的区别:**多任务是针对一个系统而言,代表程序可以同时执行的程序个数,多线程是针对一个程序而言,代表一个程序可以同时执行的线程个数。

线程的生命周期与java的多线程机制

一个线程从创建、运行到消亡的过程,成为线程的生命周期。
线程的六个状态如下图:

官方代码(中文意思是自译的):

public enum State {
        /**
         * Thread state for a thread which has not yet started.
         * 未调用start()之前
         */
        NEW,

        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         * 表示可能正在执行或者在等待操作系统的处理(已经进入就绪的意思)
         */
        RUNNABLE,

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         * 等待监视器锁定时被阻止的线程的线程状态。
         * 处于阻塞状态的线程正在等待监视器锁进入同步块/方法,
         * 或在调用Object.wait后重新输入同步块/方法。
         */
        BLOCKED,

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         * 由于调用了Object.wait,Thread.join,LockSupport.park三者中的任意一种方法而进
         * 入了WAITING状态
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * is waiting for a specified thread to terminate.
         * 
         * 处于等待状态的线程正在等待另一个线程执行特定操作。例如,
         * 对某个对象调用Object.wait()的线程正在等待另一个线程对该对象
         * 调用Object.notify()或Object.notifyAll()。
         * 调用thread.join()的线程正在等待指定的线程终止。
         */
        WAITING,

        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * 具有指定等待时间的等待线程的线程状态。
         * 由于使用指定的正等待时间调用以下方法之一,线程处于定时等待状态:
         * 
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING,

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         * 线程完成执行
         */
        TERMINATED;
    }

简单说一下:
(1)新建状态(NEW)

尚未启动的线程处于此状态,就是还没有调用start()方法之前。

(2)运行状态(RUNNABLE)

在Java虚拟机中执行的线程处于此状态,JAVA 线程把操作系统中的就绪和运行两种状态统一称为“运行中” 。

(3)阻塞状态(BLOCKED)

被阻塞等待监视器锁定的线程处于此状态,,具有执行资格,等待cpu空闲时执行 ,阻塞状态有以下几种情况:

  • 等待阻塞:当 sleep() 结束、join ()线程终止或者线程被唤醒后,该线程从等待状态进入到阻塞状态,重新抢占锁后进行线程恢复。
  • 同步阻塞:运行线程在进行获取对象同步锁是,该同步锁被其他线程锁占用,那么该线程就无法获得该锁资源,就会被JVM放入到锁池中,从而进入了阻塞状态。
  • 发生I/O请求时,JVM会把当前线程设置为阻塞状态,等待I/O处理完成后恢复。

(4)无限(永久)等待状态(WAITING)

正在等待另一个线程执行特定动作的线程处于此状态,没有超时时间,或者被其他线程的中断操作。如调用Object.wait(),Thread.join(),LockSupport.park()

(5)休眠(睡眠)状态(TIMED_WAITING)

正在等待另一个线程执行动作达到指定等待时间的线程处于此状态,也称为超时等待状态。如调用Thread.slee(),Object.wait(long),Thread.join(long),
LockSupport.parkNanos(),LockSupport.parkUntil()

(6)死亡状态(TERMINATED)

已退出的线程处于此状态,表示线程已经执行完毕。

状态图:
在这里插入图片描述
扩展
与操作系统的进程的5种状态的区别:
(1)新建状态

  • 刚刚创建的进程,操作系统还没有把它加入到可执行进程组中,通常是还没有加载到主内存中的新进程。

(2)就绪状态

*正在被执行的进程

(3)运行状态

  • 进程做好了运行准备,只要获得CPU就可以开始执行

(4)阻塞状态

  • 进程做好了等待某些事件,如I/O操作。

(5)消亡状态

  • 操作系统从可执行进程组中撤销了进程,或者自身停止,或者因为某种原因被撤销。

创建线程对象

通过继承Thread类

//两个例子的主程序
public static void main(String[] args) {//主线程,启动就会存在jvm编译器和内存回收机制这两个线程
        method01();
//        method02();
    }
   public static void method01(){
        MyThread me=new MyThread("线程1");//线程的新建
        MyThread m1=new MyThread("线程2");//线程的新建
        me.start();//线程的启动
        m1.start();
    }
/**
 * 继承Thread实现线程
 */
class MyThread extends Thread{
    private String name;

    public MyThread() {}
    public MyThread(String name) {
        super(name);
    }
    //重写run方法:线程执行的代码
    @Override
    public void run() {
        for(int i=0;i<10;i++){
            try {
                sleep(1000);
            }catch (InterruptedException e){}
            System.out.println(this.getName()+"运行,i="+i);
    }
}
}

运行截图:
在这里插入图片描述

通过实现Runnable接口

public static void method02(){
//        MyRunable t1=new MyRunable("线程A");
//        MyRunable t2=new MyRunable("线程B");
//        new Thread(t1).start();
//        new Thread(t2).start();

        //只创建一个线程变量t
        MyRunable t=new MyRunable();
        new Thread(t,"线程A").start();
        new Thread(t,"线程B").start();
        new Thread(t,"线程C").start();
        new Thread(t,"线程D").start();
    }
/**
 * 实现接口Runable实现线程
 */
class MyRunable implements Runnable{

    private String name;

    public MyRunable() {
    }

    public MyRunable(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        for (int i=0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+"运行,i="+i);
        }
    }
}

运行截图;
在这里插入图片描述

两种方法的比较

(1)有继承Thread类创建的线程对象简单方便,可以直接操作线程,但不能再继承其他类,不可共享一个变量。
(2)用Runnable接口创建线程对象,需要实例化为Thread对象,可以再继承其他类,可共享一个变量。

线程的优先级与状态

线程类的方法

(1)线程的构造方法

public Thread(),public Thread(String name),public Thread(Runnable terget),
public Thread(Runnable terget,String name)等。
注意:
线程不可以重写get/set方法,且通过实现Runnable接口和继承Thread类创建线程的run()方法没有返回值,但实现Callable接口创建的线程的call()方法可以有返回值

(2)线程的类方法
以下是Thread类的主要静态方法,即直接可以在Thread类调用

  • CurrentThread() 返回正在运行的Thread对象名称
  • sleep(long) 让当前线程休眠n毫秒
  • sleep(long,int) 使一个线程休眠long毫秒int纳秒
  • yield() 挂起当前线程,使其他处于等待状态的线程运行

(3)实例方法
以下是Thread类的对象方法,即必须用Thread的对象实例来调用

  • activeCount() 返回该线程组当前激活的线程数目
  • checkAccess() 检测当前线程是否可以被修改
  • destroy() 终止一个线程,不清除其他相关的内容
  • getName() 返回当前线程的名称
  • getPriority() 返回线程的优先级
  • interrupt() 向一个线程发送一个中断信息
  • interrupted() 检查该线程是否被中断
  • isAlive() 检查是否处于被激活状态
  • isDaemon() 检查该线程是否常住内存(即后台运行)
  • isInterrupted() 检查另一个线程是否被中断
  • join(long) 中止当前线程,等待long毫秒,插入调用该方法的线程,其完成后再继续原线程(即抢占当前线程资源,进入调用该方法的线程)
  • join(long,int) 中止当前线程,等待long毫秒int纳秒,插入调用该方法的线程,其完成后再继续原线程(即抢占当前线程资源,进入调用该方法的线程)
  • join() 中止该线程,等待调用该方法的线程完成后再继续原线程
  • run() 整个线程的入口
  • setDaemon() 将该线程设置为Daemon(常驻内存)
  • setPriority() 设置线程优先级
  • start() 启动一个线程,这个线程将自动调用run()方法。同时,在新的线程开始执行时,调用start的那个线程将立即返回执行主线程。
  • stop() 终止一个线程
  • stop(Throwable) 终止一个线程,该线程是由Throwable类继续过来的
  • toString() 返回一个字符串
  • wait() 让当前线程进入等待状态,同时也会让当前线程释放它所持有的锁
  • wait(long) 让当前线程处于“超时等待状态”,如果在等待线程超过了设置的时间long,则需要通过竞争重新获取锁
  • wait(long,int) 让当前线程处于“超时等待状态”,如果在等待线程超过了设置的时间long毫秒int纳秒,则需要通过竞争重新获取锁
  • notify() 唤醒当前对象上等待的当个线程
  • notifyAll() 唤醒当前对象上等待的所有线程

其中一些方法的例子
(1)join()方法

public static void method01(){
        try {
            JoinThread j1=new JoinThread();
            Thread t1=new Thread(j1,"线程A");

            t1.start();
            for(int i=0;i<50;i++){
                if (i>10){
                    t1.join();//强制让线程t1先完成后,其他线程才能继续运行
                }
                System.out.println("主线程运行i="+i);
            }
            new Thread(j1,"线程B").start();

        }catch (InterruptedException e){}

    }
/**
 * join
 */
class JoinThread implements Runnable{

    @Override
    public void run() {
        for (int i=0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+"运行,i="+i);
        }
    }
}

在这里插入图片描述
(2)sleep()方法

    public static void method02(){
        SleepThread st=new SleepThread();
        st.setName("线程1");
        st.start();
    }
/**
 * sleep
 */
class SleepThread extends Thread{
    @Override
    public void run() {
        for(int i=0;i<10;i++){
            try {
                sleep(1000);// 若是实现Runnable接口用Thread.sleep()
            }catch (InterruptedException e){
                System.out.println("出现异常");
            }
            System.out.println(this.getName()+"运行,i="+i);
        }
    }
}

(3)interrupt()方法
官方源码解释:

Interrupts this thread.
Unless the current thread is interrupting itself, which is always permitted, the checkAccess method of this thread is invoked, which may cause a SecurityException to be thrown.
If this thread is blocked in an invocation of the wait(), wait(long), or wait(long, int) methods of the Object class, or of the join(), join(long), join(long, int), sleep(long), or sleep(long, int), methods of this class, then its interrupt status will be cleared and it will receive an InterruptedException.
If this thread is blocked in an I/O operation upon an InterruptibleChannel then the channel will be closed, the thread's interrupt status will be set, and the thread will receive a java.nio.channels.ClosedByInterruptException.
If this thread is blocked in a java.nio.channels.Selector then the thread's interrupt status will be set and it will return immediately from the selection operation, possibly with a non-zero value, just as if the selector's wakeup method were invoked.
If none of the previous conditions hold then this thread's interrupt status will be set.
Interrupting a thread that is not alive need not have any effect.
Throws:
SecurityException – if the current thread cannot modify this thread
中断此线程。
除非当前线程正在中断自身(这是始终允许的),否则将调用此线程的checkAccess方法,这可能会导致抛出SecurityException。
如果此线程在调用对象类的wait()、wait(long)或wait(longint)方法时被阻塞,或者在调用此类的join()、join(long)、join(longint)、sleep(long)或sleep(longint)方法时被阻塞,则其中断状态将被清除,并且它将接收到InterruptedException。
如果此线程在I/O操作中被阻塞在中断通道上,则通道将被关闭,线程的中断状态将被设置,并且线程将接收java.nio.channels.ClosedByInterruptException。
如果该线程在java.nio.channels.Selector中被阻塞,那么该线程的中断状态将被设置,并且它将立即从选择操作返回,可能返回一个非零值,就像调用了选择器的唤醒方法一样。
如果前面的条件都不成立,那么这个线程的中断状态将被设置。
中断一个非活动线程不需要有任何效果。
抛出:
SecurityException–如果当前线程无法修改此线程
public void interrupt() {
        if (this != Thread.currentThread())
            checkAccess();

        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) {
                interrupt0();           // Just to set the interrupt flag
                b.interrupt(this);
                return;
            }
        }
        interrupt0();
    }

个人例子:

  public static void method03(){
        InterruptThread it=new InterruptThread();
       Thread t1= new Thread(it,"线程A");
       t1.start();
       try {
           Thread.sleep(2000);
       }catch (InterruptedException e){
           e.printStackTrace();
       }
       t1.interrupt();//中止线程运行,让子线程产生异常
    }
/**
 * interrupt()
 */
class InterruptThread implements Runnable{

    @Override
    public void run() {
        try{
            System.out.println("进入到run运行");
            for(int i=0;i<20;i++){
//                Thread.sleep(1000);
                if(i==10){
                    Thread.sleep(10000);
                    System.out.println("线程休眠结束!");
                }
                System.out.println(Thread.currentThread().getName()+"运行,i="+i);
        }
            System.out.println("线程运行完成");
        }catch (InterruptedException e){
            System.out.println("出现异常");
        }
    }
}

在这里插入图片描述
(4)setDaemon()方法

 public static void method04(){
        SetDaemonThread sdt=new SetDaemonThread();
        Thread t1=new Thread(sdt,"线程守护1");
        t1.setDaemon(true);
        t1.start();
    }
/**
 * setDaemon
 */
class SetDaemonThread implements Runnable{

    @Override
    public void run() {
        try {
            for (int i=0;i<100;i++){
                System.out.println(Thread.currentThread().getName()+"运行,i="+i);
                Thread.sleep(1000);
            }
        }catch (InterruptedException e){e.printStackTrace();}

    }
}

在后台运行当前控制台看不到结果
在这里插入图片描述
(5)Priority

 public static void method05(){
        PriorityThread pt=new PriorityThread();
        Thread t1=new Thread(pt,"线程A");
        Thread t2=new Thread(pt,"线程B");
        Thread t3=new Thread(pt,"线程C");
        t1.setPriority(1);//优先级最低
        t3.setPriority(10);//优先级最高
        t1.start();t2.start();t3.start();
    }
/**
 * Priority
 */
class PriorityThread implements Runnable{

    @Override
    public void run() {
        try {
            for (int i=0;i<50;i++){
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName()+"运行,i="+i);
            }
        }catch (InterruptedException e){e.printStackTrace();}

    }
}

优先级高先启动
在这里插入图片描述
(6)yield()

    public static void method06(){
        YieldThread yt=new YieldThread();
        Thread t1=new Thread(yt,"线程A");
        Thread t2=new Thread(yt,"线程B");
        Thread t3=new Thread(yt,"线程C");
        t1.setPriority(10);
        t1.start();
        t2.start();
        t3.start();
    }
class YieldThread implements Runnable{

    @Override
    public void run() {
        for (int i=0;i<100;i++) {
            try {
                Thread.sleep(1000);

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            String name=Thread.currentThread().getName();
            System.out.println(Thread.currentThread().getName() + "运行,i=" + i);
            if (i % 5 == 0&&name.equals("线程A")) {
                System.out.println("我礼让我快乐-");
                Thread.currentThread().yield();

            }
        }}

}

在这里插入图片描述

控制线程的优先级

为了使有些线程可以提前得到服务,给线程设置了优先级,在单个CPU上运行多线程时采用线程队列技术,java虚拟机支持固定优先级队列。

官方源码设定:

//优先级1~10,以下为Thread定义的3个常数
//1优先级最低
 public final static int MIN_PRIORITY = 1;
 //5为默认值
  public final static int NORM_PRIORITY = 5;
  //10优先级最高
  public final static int MAX_PRIORITY = 10;

控制线程的状态

(1)线程的启动——“可执行”状态

    执行run()方法是通过调用Thread类中的start()方法实现的

(2)线程的挂起——“非可执行的状态”

    线程挂起后CPU不会分给线程资源
   * 线程休眠: 通过调用sleep()方法 
   * 线程插入:通过调用join()方法
   * 线程等待:通过调用wait()方法,可通过notify()/notifyAll()唤醒,这三个方法都是Object类的,所以会被所有类继承;应为被声明为final类,所以无法重新定义。

(3)确定线程的状态

可通过isAlive()判断是否在活动状态,会有一个boolean类型的返回值,对于一个已经开始但还没有完成任务的线程,但返回值为true时,处于活动状态的线程不一定在运行(可能在等待资源)

(4)结束线程

 * 自然消亡:执行完成run()方法
 * 强制死亡:  通过调用stop()方法(但该方法已经被放弃)

(5)后台线程、

 Daemon线程是一个后台服务线程
可以使用Thread.setDaemon(boolean on) 方法设置一个后台线程,要注意,是必须在start()启动之前调用   setDaemon()方法才能成功设置

Java的线程同步机制与应用模型

线程的同步机制

同步锁

关键字:synchronized

同步方法与同步对象的区别:
        同步对象:就是以这个对象为锁,其他也以这个对象为锁的代码段,在这个锁被
          占用时,就必须进行等待。
        同步方法:就是这个类加入synchronize的方法,在执行时,会获得一个该类的唯一同步锁,当这个锁被占用时,
           其他也加入了synchronize的方法就必须等待(若是把方法改为静态,容易出现共享,造成死锁)

(1)同步方法

格式:
synchronize void method(){
           需要同步的代码;
      }

例子:

/**
 * synchronize
 */
class SyncMethodThread implements Runnable{

    private int ticket=100;

    @Override
    public void run() {
        while (ticket>0){
            this.sale();
        }

    }
    public synchronized void sale(){
        if(ticket>0){
            try {
                Thread.sleep(1000);
            }catch (InterruptedException e){e.printStackTrace();}
            System.out.println(Thread.currentThread().getName()+",卖出票,还剩"+--ticket);

        }
    }
}

(2)同步代码块

格式:
synchronize(同步对象){
          需要同步的代码;
      }

例子:

/**
 * synchronized(obj)
 */
class SyncObjectThread implements Runnable{

    private int ticket=100;
    @Override
    public void run() {
        for (int i=0;i<100;i++){
            synchronized (this){  //对对象进行同步
                if (ticket>0){
                    try {
                        Thread.sleep(1000);
                    }catch (InterruptedException e){e.printStackTrace();}
                    System.out.println(Thread.currentThread().getName()+",卖出第"+(100-ticket+1)+"张票");
                    ticket--;
                }
            }
        }

    }
}

在这里插入图片描述

生产消费模型

package 生产消费模型;

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Share s=new Share();
		Producer p=new Producer(s);
		Consumer c=new Consumer(s);
		p.start();
		c.start();
	}
}
/**
 * 共享资源类Share
 * @author Administrator
 *
 */
class Share{
	private int u;
	int get() {
		return u;
	}
	public void put(int value) {
		u=value;
	}
}

/**
 * 生产者类Producer
 * @author Administrator
 *
 */
class Producer extends Thread{
	private Share shared;

	public Producer(Share s) {
		shared=s;
	}
	public void run() {
		for(int i=0;i<10;i++) {
			shared.put(i);
			System.out.println("生产者"+" 的生产数据为:"+i);
			try {
				sleep((int)(Math.random()*100));
			}catch(InterruptedException e) {
				System.out.println("出现中断异常!");
			}
		}
	}
}

/**
 * 消费者类Consumer
 * @author Administrator
 *
 */
class Consumer extends Thread{
	private Share shared;
	public Consumer (Share s) {
		shared=s;
	}
	public void run() {
		int value=0;
		for(int i=0;i<10;i++) {
			value=shared.get();
			System.out.println("消费者"+"拿到的生产数据为:"+value);
		}
	}
}

(1)通过synchronized()锁定的方法(优化代码后)

class Share1{
	private int u;
	private boolean available=false;
	
	synchronized int get() {  //锁定get方法
		while(available==false) {
			try {
				wait();
			}catch(InterruptedException e) {}
		}
		available=false;
		notifyAll();
		return u;
	}
	public synchronized void put(int value) {
		while(available==true) {
			try {
				wait();
			}catch(InterruptedException e) {}
		}
		u=value;
		available=true;
		notifyAll();
	}
}

运行截图
在这里插入图片描述

公共公司银行账号模型

package 银行账户模型;
/**
 * 存款线程与执行线程类
 * @author Administrator
 *
 */
public class Main extends Thread{
	private Account a1;
	private int amount;
	public Main(Account a1, int amount) {
		this.a1 = a1;
		this.amount = amount;
	}
	public void run() {
		synchronized(a1) {
			int k=a1.howmatch();
			try {
				sleep(10);
			}catch(InterruptedException e) {System.out.println("出现中断异常!");}
			a1.put(amount);
			System.out.println("当前存款额为"+k+",存入"+amount+",余额"+a1.howmatch());
		}
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Account a1=new Account();
		(new Main(a1,1000)).start();
		(new Main(a1,200)).start();
		(new Fetch(a1,500)).start();
	}

}

/**
 * 银行账户类
 * @author Administrator
 *
 */
class Account{
	private int value;
	void put(int i) {
		value=value+i;
	}
	int get(int i) {
		if(value>i) {
			value=value-i;
		}else {
			i=value;
			value=0;
		}
		return i;
	}
	int howmatch() {
		return value;
	}
}

/**
 * 取款线程类
 * @author Administrator
 *
 */
class Fetch extends Thread{
	private Account a1;
	private int amount;
	public Fetch(Account a1,int amount) {
		this.a1=a1;
		this.amount=amount;
	}
	public void run() {
		synchronized(a1) {
			int k=a1.howmatch();
			try {
				sleep(1);
			}catch(InterruptedException e) {System.out.println("出现中断异常!");}
			System.out.println("当前存款额为"+k+",取走"+a1.get(amount)+",余额"+a1.howmatch());
		}
	}
}

运行截图
在这里插入图片描述

线程通信———水塘模型

package 线程通讯水塘模型;

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Water water=new Water();
		Thread t1=new WaterInThread(water);
		Thread t2=new WaterOutThread(water);
		t1.start();
		t2.start();
	}

}

/**
 * 水池类
 * @author Administrator
 *
 */
class Water{
	public int volume;
	public synchronized void increase() {
		if(volume!=0) {
			try {
				wait();
			}catch(InterruptedException e) {System.out.println("中断异常!");}
		}
		volume++;
		System.out.print(volume+"、");
		notify();
	}
	public synchronized void decrease() {
		if(volume==0) {
			try {
				wait();
			}catch(InterruptedException e) {System.out.println("中断异常!");}
		}
		volume--;
		System.out.print(volume+"。");
		notify();
	}
}

/**
 * 进水线程类
 * @author Administrator
 *
 */
class WaterInThread extends Thread{
	private Water water;

	public WaterInThread(Water water) {
		super();
		this.water = water;
	}
	public void run() {
		for(int i=0;i<10;i++) {
			try {
				Thread.sleep(1000);
			}catch(InterruptedException e) {System.out.println("中断异常!");}
			water.increase();
		}
	}
}

/**
 * 排水线程类
 * @author Administrator
 *
 */
class WaterOutThread extends Thread{
	private Water water;

	public WaterOutThread(Water water) {
		super();
		this.water = water;
	}
	public void run() {
		for(int i=0;i<10;i++) {
			try {
				Thread.sleep(1000);
			}catch(InterruptedException e) {System.out.println("中断异常!");}
			water.decrease();
		}
	}
}

运行截图
在这里插入图片描述

使用多线程应该注意的问题

线程的死锁(DeadLock)

造成死锁的4个条件(必须同时满足)

  • 互斥条件:线程所用的资源必须至少有一个是不能共享的。
  • 请求与保持条件:至少有一个线程必须持有一个资源并且正在等待获取一个当前被其他线程持有的资源
  • 非剥夺条件:分配的资源不能从相应的线程中强制剥夺
  • 循环等待条件:第一个线程等待其他线程,后者又在等待第一个线程

注意:要破坏死锁,只需破坏4个中的其中一个就可

public class Test04 {
    public static void main(String[] args) {
        DeadLock dl1=new DeadLock();
        DeadLock dl2=new DeadLock();
        dl1.flag=true;
        dl2.flag=false;
        new Thread(dl1).start();
        new Thread(dl2).start();
    }
}

class Person1{
    public void say(){
        System.out.println("P1对P2说:你放手,我就放!");
    }
    public void result(){
        System.out.println("P2松开了手");
    }
}
class Person2{
    public void say(){
        System.out.println("P2对P1说:你放手,我就放!");
    }
    public void result(){
        System.out.println("P1松开了手");
    }
}

class DeadLock implements Runnable{

    private static Person1 p1=new Person1();
    private static Person2 p2=new Person2();
    public boolean flag=false;


    @Override
    public void run() {
        if(flag){
            synchronized (p1){
                p1.say();
                try {
                    Thread.sleep(1000);
                }catch (InterruptedException e){e.printStackTrace();}
                synchronized (p2){
                    p1.result();
                }
            }
        }else {
            synchronized (p2){
                p2.say();
                try {
                    Thread.sleep(1000);
                }catch (InterruptedException e){e.printStackTrace();}
                synchronized (p1){
                    p2.result();
                }
            }
        }
    }
}

在这里插入图片描述

使用多线程的代价

  • 线程需要占用内存
  • 线程过多,会消耗大量CPU时间来跟踪线程
  • 必须考虑多线程同时访问共享资源的问题(协调不好可能会发生死锁和资源竞争)
  • 因为同一个任务的所有线程都共享相同的地址空间,并共享任务的全局变量,所以程序必须考虑多线程同时访问全局变量的问题

多线程的新特性

线程池

(1)newCacheThreadPool——可变尺寸的线程池(缓存线程池)

  • 工作线程创建数量几乎没有限制,这样可以灵活地往线程池中添加线程。
  • 如果长时间没有往线程池中提交任务,即空闲了指定的时间(默认为1分钟),该工作线程将自动停止,终止后,如果你又重新提交了新的任务,则线程池重新创建一个工作线程。

(2)newFixedThreadPool——固定大小的线程池

  • 创建一个有指定工作线程数量的线程池,每提交一个任务就会创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。

(3)ScheduledThreadPool——调度线程池

  • 支持定时的以及周期性的任务执行

(4)SingleThreadExecutor——单例线程池

  • 只创建唯一的工作者线程啦执行任务,如果这个线程异常结束,会有另一个取代它,保证顺利执行
  • 保证顺利执行,并且在任意给定的时间内不会有多个线程时活动的。

Callable接口

(1)通过Callable泛型接口可以创建有返回值的线程,其中有一个带有返回值的call(0方法。
(2)使用ExecutorService 接口,它提供了管理终止线程的方法,一个submit()方法,用来创建Future对象(跟踪一个或多个异步任务执行状况),
(3)获取call()方法的返回值,要通过Future接口实例化对象,调用其的get()方法。

package 线程池;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Main2 {
	//内部类实现Callable接口
	class MyCallable implements Callable<String>{
		private String name;

		public MyCallable(String name) {
			// TODO Auto-generated constructor stub
			this.name=name;
		}

		@Override
		//带有返回值的call() 方法
		public String call() throws Exception {
			// TODO Auto-generated method stub
			return name+"任务返回内容";
		}
	}

	public static void main(String[] args) throws ExecutionException,InterruptedException{
		// TODO Auto-generated method stub
		Main2 m=new Main2();
		ExecutorService pool=Executors.newFixedThreadPool(2);  //建立固定的线程池
		
		//创建偶返回值的任务
		Callable<String> c1=m.new MyCallable("A");
		Callable<String> c2=m.new MyCallable("B");
		
		//执行任务并获取Future对象
		Future<String> f1=pool.submit(c1);
		Future<String> f2=pool.submit(c2);
		
		//从Fuutre对象上获取任务的返回值,并输出到控制台
		System.out.println(">>>"+f1.get().toString());
		System.out.println(">>>"+f2.get().toString());
		
		//关闭线程池
		pool.shutdown();
	}
}

运行截图
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值