多线程-线程控制

 

// 经过实现Runnable接口来建立线程类
public class SecondThread implements Runnable
{
	private int i ;
	// run方法一样是线程执行体
	public void run()
	{
		for ( ; i < 100 ; i++ )
		{
			// 当线程类实现Runnable接口时,
			// 若是想获取当前线程,只能用Thread.currentThread()方法。
			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(target , name)方法建立新线程
				Thread thread = new Thread(st , "新线程1");
				thread.join();
			}
		}
	}
}

join()方法,当某个程序执行路中的调用其余线程的thread1.join()方法,该程序须要等待thread1执行结束再继续运行。java

 

线程让步:yield,仅是将线程从执行状态转为就绪状态,不阻塞线程。在多cpu状况下,这个效果根本没用。因此yield也不是绝对能控制线程的方法apache

 

线程同步安全问题:安全

多个线程访问同一资源,会形成线程错误: 由此衍生出几种解决方案:session

1,synchronized:加锁-修改-释放锁;多线程

2,lock ReentrantLock并发

 ReentrantLock lock = new ReentrantLock();

lock.lock();

lock.unlock();

eg 银行取款问题:ide

1,用户输入取款金额;函数

2,系统判断帐号余额是否大于取款金额;ui

3, 若是余额大于取款金额,取款成功,若是余额小于取款金额,取款失败;this

银行帐户类 account.java

public class Account {
	
	private String accountNo;
	private double balance;
	public Account(String accountNo, double balance) {
		super();
		this.accountNo = accountNo;
		this.balance = balance;
	}
	public String getAccountNo() {
		return accountNo;
	}
	public void setAccountNo(String accountNo) {
		this.accountNo = accountNo;
	}
	public double getBalance() {
		return balance;
	}
	public void setBalance(double balance) {
		this.balance = balance;
	}
	
	
}

 

取款线程类:

public class DrawThread extends Thread{

	private Account account;
	private double drawAmount;
	
	
	public Account getAccount() {
		return account;
	}

	public void setAccount(Account account) {
		this.account = account;
	}

	public double getDrawAmount() {
		return drawAmount;
	}

	public void setDrawAmount(double drawAmount) {
		this.drawAmount = drawAmount;
	}

	public DrawThread(Account account, double drawAmount) {
		super();
		this.account = account;
		this.drawAmount = drawAmount;
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		super.run();
		
		if(account.getBalance()>=drawAmount)
		{
			System.out.println("取钱成功!吐出钞票"+drawAmount);
			
			// 修改余额
			account.setBalance(account.getBalance()-drawAmount);
			System.out.println("余额:"+account.getBalance());
		}
		else {
			System.out.println("余额不足!");
		}
		
		
		
		
		
	}






	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		
	}

}

取款main:

import java.awt.List;
import java.io.Console;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Stack;

/**
 * 
 */

/**
 * @author lrh
 *
 */
public class T {

	/**
	 * @param args
	 * @throws InterruptedException 
	 */
	public static void main(String[] args) throws InterruptedException {
		// TODO Auto-generated method stub
		
		Account account = new Account("123", 1000);
		new DrawThread(account, 800).start();
		new DrawThread(account, 800).start();
		
	}

}

 

多线程并发访问,会形成取款错误:

取钱成功!吐出钞票800.0
余额:200.0
取钱成功!吐出钞票800.0
余额:-600.0

所以,引入同步代码块这个概念:

public class DrawThread extends Thread{

	private Account account;
	private double drawAmount;
	
	
	public Account getAccount() {
		return account;
	}

	public void setAccount(Account account) {
		this.account = account;
	}

	public double getDrawAmount() {
		return drawAmount;
	}

	public void setDrawAmount(double drawAmount) {
		this.drawAmount = drawAmount;
	}

	public DrawThread(Account account, double drawAmount) {
		super();
		this.account = account;
		this.drawAmount = drawAmount;
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		super.run();
		synchronized (account) 
		{
			if(account.getBalance()>=drawAmount)
			{
				System.out.println("取钱成功!吐出钞票"+drawAmount);
				
				// 修改余额
				account.setBalance(account.getBalance()-drawAmount);
				System.out.println("余额:"+account.getBalance());
			}
			else {
				System.out.println("余额不足!");
			}
		}
		
		
		
		
		
	}






	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		
	}

}

synchronized 保证只有一个线程多共享资源account进行访问

运行结果:

取钱成功!吐出钞票800.0
余额:200.0
余额不足!

 

ThreadLocal:线程局部变量 ,为每个使用该变量的线程都提供一个变量值得副本;

public class Account {

	private ThreadLocal<String> name = new ThreadLocal<>();

	public Account(String Str) 
	{
		super();
		this.name.set(Str);
		System.out.println("----"+this.name.get());
	}

	public String getName() {
		return name.get();
	}

	public void setName(String str) {
		this.name.set(str);
	}
	
	
}
public class MyTest extends Thread {

	private Account account;
	public MyTest(Account account,String name) 
	{
		super(name);
		this.account=account;
	}
	
	public void run()
	{
		for(int i =0;i<10;i++)
		{
			if(i==6)
			{
				account.setName(getName());
			}
			System.out.println(account.getName()+"帐号的i值:"+i);
			
		}
	}
	
	
	
}
public class ThreadLocalTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Account at = new Account("初始名");
		new MyTest(at, "线程甲").start();
		new MyTest(at, "线程乙").start();
	}

}

两个线程各有一个线程局部变量.

result:

----初始名
null帐号的i值:0
null帐号的i值:1
null帐号的i值:0
null帐号的i值:1
null帐号的i值:2
null帐号的i值:2
null帐号的i值:3
null帐号的i值:4
null帐号的i值:5
null帐号的i值:3
null帐号的i值:4
null帐号的i值:5
线程甲帐号的i值:6
线程甲帐号的i值:7
线程甲帐号的i值:8
线程甲帐号的i值:9
线程乙帐号的i值:6
线程乙帐号的i值:7
线程乙帐号的i值:8
线程乙帐号的i值:9
 

线程同步锁lock.

lock比synchroniezed方法和synchronized代码块更灵活,因此我的仍是喜欢用这个。

下面是一个本地例子:

import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;


public class Test implements Runnable{

	private final ReentrantLock lock = new ReentrantLock();
	/*
	 * 加锁前打印
	 * */
	public void run()
	{
		//加锁前打印
		System.out.println(new Date()+Thread.currentThread().getName() +" 准备进入线程");
		lock.lock();
		//锁内延时
		try {
			Thread.sleep(2222);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//锁内打印
		System.out.println(new Date()+Thread.currentThread().getName()+" 进入线程成功");
		lock.unlock();
		//解锁后打印
		System.out.println(new Date()+Thread.currentThread().getName()+" 离开线程成功");
		
	}
	/**
	 * @param args
	 * @throws InterruptedException 
	 * @throws IOException 
	 * 建立两个线程,并行执行
	 */
	public static void main(String[] args) throws InterruptedException{
		// TODO Auto-generated method stub
		Test t1=  new Test();
		new Thread(t1).start();
		new Thread(t1).start();
		//Thread.sleep(9999);
		
	}

}

运行结果:

Sat May 06 17:14:31 CST 2017Thread-0 准备进入线程
Sat May 06 17:14:31 CST 2017Thread-1 准备进入线程
Sat May 06 17:14:33 CST 2017Thread-0 进入线程成功
Sat May 06 17:14:33 CST 2017Thread-0 离开线程成功
Sat May 06 17:14:35 CST 2017Thread-1 进入线程成功
Sat May 06 17:14:35 CST 2017Thread-1 离开线程成功

重点在时间,且两者共用一个线程类

加锁后1号线程没法进入,被锁在外头了。这个控制的很好。

稍做修改:

import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;


public class Test implements Runnable{

	private final ReentrantLock lock = new ReentrantLock();
	/*
	 * 加锁前打印
	 * */
	public void run()
	{
		//加锁前打印
		System.out.println(new Date()+Thread.currentThread().getName() +" 准备进入线程");
		lock.lock();
		
		//锁内打印
		System.out.println(new Date()+Thread.currentThread().getName()+" 进入线程成功");
		//锁内延时
		try {
			Thread.sleep(2222);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		lock.unlock();
		//解锁后打印
		System.out.println(new Date()+Thread.currentThread().getName()+" 离开线程成功");
		
	}
	/**
	 * @param args
	 * @throws InterruptedException 
	 * @throws IOException 
	 * 建立两个线程,并行执行
	 */
	public static void main(String[] args) throws InterruptedException{
		// TODO Auto-generated method stub
		Test t1=  new Test();
		Test t2=  new Test();
		new Thread(t1).start();
		new Thread(t2).start();
		//Thread.sleep(9999);
		
	}

}

 

重点在main函数里面的:

Test t1=  new Test();
        Test t2=  new Test();
        new Thread(t1).start();
        new Thread(t2).start();

运行结果其实就是两个对象的两把锁了,这点尤为要注意。

因此运行结果以下:

Sat May 06 17:17:39 CST 2017Thread-0 准备进入线程
Sat May 06 17:17:39 CST 2017Thread-0 进入线程成功
Sat May 06 17:17:39 CST 2017Thread-1 准备进入线程 --二者都进入锁,由于实际上是两把锁
Sat May 06 17:17:39 CST 2017Thread-1 进入线程成功
Sat May 06 17:17:41 CST 2017Thread-0 离开线程成功
Sat May 06 17:17:41 CST 2017Thread-1 离开线程成功

 

线程通讯:使用BlockingQueue控制线程通讯

相关文章
相关标签/搜索