// 经过实现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控制线程通讯