初识java多线程

1. 线程的3种创建方式

(1) 继承Thread类,重写run()方法

public class FirstThread extends Thread {
    private int i ;
    public void run(){
        for(i = 0; i < 100; i++){
            System.out.println(getName()+" "+i);

        }
    }
    public static void main(String[] args){
        for(int i = 0; i < 100; i++){
            System.out.println(Thread.currentThread().getName() + " " + i);
            if(i == 20){
                new FirstThread().start();
                new FirstThread().start();
            }
        }
    }
}

(2)实现Runnable接口创建线程类,重写run()方法,jdk1.0, 无返回值

public class SecondThread implements Runnable {
    private int i;
    @Override
    public void run() {

        for (; i < 20; i++) {
            System.out.println(Thread.currentThread().getName() + "  " + i);
        }
    }

    public static void main(String[] args) {
        for ( int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "  " + i);
            if ( i == 20) {
                SecondThread st = new SecondThread();
                new Thread(st, "线程1").start(); //Runable对象仅仅作为Thread对象的target,实际的线程对象仍然是Thread示例,
                new Thread(st, "线程2").start(); //该Thread线程负责执行其target的run()方法。
            }
        }
    }
}

(3)使用Callable和Future创建线程,重写call方法,jdk1.5, 有返回值

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

public class ThirdThread {
    public static void main(String[] args) {
        ThirdThread rt = new ThirdThread();
        //使用Lambda表达式创建Callable对象
        //使用FuturTask包装Callable对象
        FutureTask<Integer> task = new FutureTask<>((Callable<Integer>) ()->{
            int i = 0;
            for (; i < 100; i++) {
                System.out.println(Thread.currentThread().getName() + " 的循环变量i的值 " + i);
            }
            return i;
        });
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " 的循环变量i的值 " + i);
            if (i == 20) {
                new Thread(task, "有返回值的线程:").start();
            }
        }
        try {
            System.out.println("子线程返回值:" + task.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

2. 线程的生命周期

线程的5种状态:新建(new)-> 就绪(Runnable)-> 运行(Running)-> 阻塞(Blocked)-> 死亡(Dead)

线程启动使用start()方法,而不是run方法。永远不要调用线程对象的run方法!!!否则,系统会将线程对象当作普通对象而不是线程对象。线程对象调用start方法后,只是表示该线程处于可以运行的状态,至于何时运行,取决于JVM的调度。start方法只能在新建状态后调用,否则会引发异常。start()方法的实现依赖于本地方法start0()方法,JNI技术(Java Native Interface)。

 public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

    private native void start0();  //只声明了native方法,没有实现,交由本地jvm实现

3. 控制线程的方法

(1)join()

让一个线程等待另一个线程完成的方法。当在程序A中调用线程B的join方法时,A的线程被阻塞,直到B执行完才会执行A。

public class JoinThread extends Thread {
    public JoinThread(String name) {
        super(name);
    }
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(getName() + " " + i);
        }
    }
    public static void main(String[] args) throws InterruptedException {
        new JoinThread("new Thread").start();
        for (int i = 0; i < 100; i++) {
            if ( i == 20 ) {
                JoinThread jt = new JoinThread("join Thread");
                jt.start();
                jt.join();  //必须等jt执行结束才会向下执行,此时main线程被阻塞,join Thread和new Thread交替执行
            }
            //join Thread执行完毕,继续执行main线程
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
    }
}

(2)后台线程

在后台运行,为其他线程提供服务。如果所有的前台线程都死亡,后台线程会自动死亡。

调用Thread对象的setDaemon(true)方法可将制定线程设置成后台线程。

(3)sleep(long millis):让当前正在执行的线程阻塞millis 毫秒。

(4)yeild():Thread类提供的静态方法,暂停当前线程,但不是进入阻塞状态,而是进入就绪状态,等待系统的线程调度器重新调度。sleep方法比yield方法有更好的可移植性,通常不建议使用yield方法来控制并发线程的执行。

(5)改变线程优先级

优先级高的线程获得较多的执行机会。每个线程默认的优先级与创建它的父线程优先级相同。默认情况下,main线程具有普通优先级,其创建的子线程也具有普通优先级。

设置优先级:Thread.currentThread().setPriority(int num); //num为优先级,范围1~10

获取优先级:Thread.currentThread().getPriority();

Thread类的三个静态常量:

MAX_PRIORITY: 10

MIN_PRIORITY: 1

NORM_PRIORITY: 5

4. 线程同步

1. 同步代码块


public class DrawThread extends Thread
{
	// 模拟用户账户
	private Account account;
	// 当前取钱线程所希望取的钱数
	private double drawAmount;
	public DrawThread(String name , Account account
		, double drawAmount)
	{
		super(name);
		this.account = account;
		this.drawAmount = drawAmount;
	}
	// 当多条线程修改同一个共享数据时,将涉及数据安全问题。
	public void run()
	{
		// 使用account作为同步监视器,任何线程进入下面同步代码块之前,
		// 必须先获得对account账户的锁定——其他线程无法获得锁,也就无法修改它
		// 这种做法符合:“加锁 → 修改 → 释放锁”的逻辑
		synchronized (account)
		{
			// 账户余额大于取钱数目
			if (account.getBalance() >= drawAmount)
			{
				// 吐出钞票
				System.out.println(getName()
					+ "取钱成功!吐出钞票:" + drawAmount);
				try
				{
					Thread.sleep(1);
				}
				catch (InterruptedException ex)
				{
					ex.printStackTrace();
				}
				// 修改余额
				account.setBalance(account.getBalance() - drawAmount);
				System.out.println("\t余额为: " + account.getBalance());
			}
			else
			{
				System.out.println(getName() + "取钱失败!余额不足!");
			}
		}
		// 同步代码块结束,该线程释放同步锁
	}
}

2. 同步方法

public class Account
{
	// 封装账户编号、账户余额两个成员变量
	private String accountNo;
	private double balance;
	public Account(){}
	// 构造器
	public Account(String accountNo , double balance)
	{
		this.accountNo = accountNo;
		this.balance = balance;
	}

	// accountNo的setter和getter方法
	public void setAccountNo(String accountNo)
	{
		this.accountNo = accountNo;
	}
	public String getAccountNo()
	{
		return this.accountNo;
	}
	// 因此账户余额不允许随便修改,所以只为balance提供getter方法,
	public double getBalance()
	{
		return this.balance;
	}

	// 提供一个线程安全draw()方法来完成取钱操作
	public synchronized void draw(double drawAmount)
	{
		// 账户余额大于取钱数目
		if (balance >= drawAmount)
		{
			// 吐出钞票
			System.out.println(Thread.currentThread().getName()
				+ "取钱成功!吐出钞票:" + drawAmount);
			try
			{
				Thread.sleep(1);
			}
			catch (InterruptedException ex)
			{
				ex.printStackTrace();
			}
			// 修改余额
			balance -= drawAmount;
			System.out.println("\t余额为: " + balance);
		}
		else
		{
			System.out.println(Thread.currentThread().getName()
				+ "取钱失败!余额不足!");
		}
	}

	// 下面两个方法根据accountNo来重写hashCode()和equals()方法
	public int hashCode()
	{
		return accountNo.hashCode();
	}
	public boolean equals(Object obj)
	{
		if(this == obj)
			return true;
		if (obj !=null
			&& obj.getClass() == Account.class)
		{
			Account target = (Account)obj;
			return target.getAccountNo().equals(accountNo);
		}
		return false;
	}
}

3. 同步锁

public class Account
{
	// 定义锁对象
	private final ReentrantLock lock = new ReentrantLock();
	// 封装账户编号、账户余额的两个成员变量
	private String accountNo;
	private double balance;
	public Account(){}
	// 构造器
	public Account(String accountNo , double balance)
	{
		this.accountNo = accountNo;
		this.balance = balance;
	}

	// accountNo的setter和getter方法
	public void setAccountNo(String accountNo)
	{
		this.accountNo = accountNo;
	}
	public String getAccountNo()
	{
		return this.accountNo;
	}
	// 因此账户余额不允许随便修改,所以只为balance提供getter方法,
	public double getBalance()
	{
		return this.balance;
	}

	// 提供一个线程安全draw()方法来完成取钱操作
	public void draw(double drawAmount)
	{
		// 加锁
		lock.lock();
		try
		{
			// 账户余额大于取钱数目
			if (balance >= drawAmount)
			{
				// 吐出钞票
				System.out.println(Thread.currentThread().getName()
					+ "取钱成功!吐出钞票:" + drawAmount);
				try
				{
					Thread.sleep(1);
				}
				catch (InterruptedException ex)
				{
					ex.printStackTrace();
				}
				// 修改余额
				balance -= drawAmount;
				System.out.println("\t余额为: " + balance);
			}
			else
			{
				System.out.println(Thread.currentThread().getName()
					+ "取钱失败!余额不足!");
			}
		}
		finally
		{
			// 修改完成,释放锁
			lock.unlock();
		}
	}

	// 下面两个方法根据accountNo来重写hashCode()和equals()方法
	public int hashCode()
	{
		return accountNo.hashCode();
	}
	public boolean equals(Object obj)
	{
		if(this == obj)
			return true;
		if (obj !=null
			&& obj.getClass() == Account.class)
		{
			Account target = (Account)obj;
			return target.getAccountNo().equals(accountNo);
		}
		return false;
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值