如何创建一个线程
1.继承Thread类
package test20141214;
public class FirstThreadTest extends Thread{
public static void main(String[] args) {
for(int i=0;i<100;i++){
System.out.println("当前线程为"+Thread.currentThread().getName()+"i="+i);
if(i==20){
Thread t1=new FirstThreadTest();
Thread t2=new FirstThreadTest();
t1.start();
t2.start();
}
}
}
public void run() {
for(int i=0;i<50;i++){
System.out.println("当前线程名:"+this.getName()+"i="+i);
}
}
}
2.实现Runnable接口
package test20141214;
public class SecondThreadTest implements Runnable{
int i;
public static void main(String[] args) {
for(int i=0;i<50;i++){
System.out.println("当前线程名为"+Thread.currentThread().getName()+"i="+i);
if(i==20){
SecondThreadTest s1=new SecondThreadTest();
SecondThreadTest s2=new SecondThreadTest();
new Thread(s1,"线程1").start();
new Thread(s1,"线程2").start();
}
}
}
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println("当前线程名为"+Thread.currentThread().getName()+"i="+i);
}
}
}
关于join方法(当子线程使用join方法时,调用此方法的线程会立即阻塞执行子线程方法,直到子线程死亡)
package test20141215;
public class JoinThreadTest implements Runnable {
@Override
public void run() {
// TODO 自动生成的方法存根
for(int i=0;i<5;i++){
System.out.println("当前的线程为"+Thread.currentThread().getName()+"i="+i);
}
}
public static void main(String[] args) {
for(int i=0;i<5;i++){
System.out.println("当前的线程为"+Thread.currentThread().getName()+"i="+i);
if(i==2){
JoinThreadTest jt=new JoinThreadTest();
Thread t=new Thread(jt,"子线程1");
t.start();
try {
t.join();
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
}
}
运行结果
当前的线程为maini=0
当前的线程为maini=1
当前的线程为maini=2
当前的线程为子线程1i=0
当前的线程为子线程1i=1
当前的线程为子线程1i=2
当前的线程为子线程1i=3
当前的线程为子线程1i=4
当前的线程为maini=3
当前的线程为maini=4
后台线程(前台线程全部死亡后,后台线程也会死亡,无论后台线程是否执行结束)
package test20141215;
public class DemoThreadTest implements Runnable {
public static void main(String[] args) {
DemoThreadTest dt=new DemoThreadTest();
Thread t=new Thread(dt,"后台线程");
//设置后台线程
t.setDaemon(true);
t.start();
for(int i=0;i<10;i++){
System.out.println("当前线程为"+Thread.currentThread().getName()+"i="+i);
}
}
public void run(){
for(int i=0;i<999;i++){
System.out.println("当前线程为"+Thread.currentThread().getName()+"i="+i);
}
}
}
sleep方法(让当前执行的线程暂停一段时间,并进入阻塞状态,即使当前没有线程运行,在sleep的线程也不会运行)
package test20141215;
public class SleepTest {
public static void main(String[] args) {
for(int i=0;i<20;i++){
System.out.println("当前线程为"+Thread.currentThread().getName()+"i="+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
}
yield方法(让当前线程暂停,以此让其他线程有运行的机会,事实上只有优先级和调用此线程的优先级相同或者更高的时候才有机会得到运行)
package test20141215;
import test20141214.FirstThreadTest;
public class YieldDemo extends Thread {
public static void main(String[] args) {
Thread t1=new FirstThreadTest();
t1.setPriority(Thread.MIN_PRIORITY);
Thread t2=new FirstThreadTest();
t2.setPriority(Thread.MAX_PRIORITY);
t1.start();
t2.start();
}
public void run() {
for(int i=0;i<20;i++){
System.out.println("当前线程名:"+this.getName()+"i="+i);
if(i%5==0){
this.yield();
}
}
}
}
运行得到的结果是,当t1线程满足i%5==0条件是,会暂停线程,此时因为t2的优先级比t1高会得到运行机会。但是t2满足i%5==0时,暂停线程后并没有找到比t2优先级高或者相等的线程,所以t2暂停后又会得到运行机会。
线程优先级(我们可以通过线程的setPriority方法设置优先级)
|
|
|
同步代码块(经典的取钱问题,当一个人取钱的一刹那,另外一个人用同一张卡取钱。)
语法
synchronized(obj){
}
括号里的obj就是同步监视器,目的就是为了阻止两个线程对并发资源的访问
package test20141215;
public class Account {
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;
}
private String accountNo;
private double balance;
@Override
public int hashCode() {
return accountNo.hashCode();
}
@Override
public boolean equals(Object obj) {
if(obj!=null&&obj.getClass()==Account.class){
Account target=(Account)obj;
return target.getAccountNo().equals(accountNo);
}
return false;
}
}
package test20141215;
public class DrawThread extends Thread{
private Account acc;
private double drawAcc;
public DrawThread(String name,Account acc,Double drawAcc){
super(name);
this.acc=acc;
this.drawAcc=drawAcc;
}
public void run(){
//synchronized(acc){
if(acc.getBalance()>=drawAcc){
System.out.println("当前线程为"+Thread.currentThread().getName());
try {
this.sleep(100);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
System.out.println("取钱成功,吐钞票");
acc.setBalance(acc.getBalance()-this.drawAcc);
System.out.println(getName()+"余额为"+acc.getBalance());
}else{
System.out.println(getName()+"余额不足");
}
}
//}
}
package test20141215;
public class TestDraw {
public static void main(String[] args) {
Account a=new Account("123",1000.0);
DrawThread d1=new DrawThread("czk1",a,500.0);
DrawThread d2=new DrawThread("czk2",a,800.0);
d1.start();
d2.start();
}
}
不难发现,第一个人取钱的时候(用了sleep模拟另外一个人一刹那取钱),另外一个人也取同一张卡的钱。第一个人取出500时候卡里还剩500,但是另外一个取钱端一刹那前读取的数据是还剩1000,其实只有500了。导致数据不同步所以要加锁(去掉注释)
运行结果
当前线程为czk1
当前线程为czk2
取钱成功,吐钞票
取钱成功,吐钞票
czk1余额为500.0
czk2余额为-300.0
加上锁后,第一个人取钱,就会给对象加锁,另一个人要取钱就要等待第一个人取完后,才能对卡里的数据进行操作。
第一个人取钱操作没有完成,第二个人就要一直等下去。
运行结果
当前线程为czk1
取钱成功,吐钞票
czk1余额为500.0
czk2余额不足
同步方法(同步方法的同步监视器就是this,类本身),下面把之前的同步代码块改成同步方法。
第一步把run方法中的逻辑放在Account类中的同步方法draw中
package test20141215;
public class Account {
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;
}
private String accountNo;
private double balance;
@Override
public int hashCode() {
return accountNo.hashCode();
}
@Override
public boolean equals(Object obj) {
if(obj!=null&&obj.getClass()==Account.class){
Account target=(Account)obj;
return target.getAccountNo().equals(accountNo);
}
return false;
}
public synchronized void draw(double drawAmount){
if(balance>=drawAmount){
System.out.println("当前余额为"+balance);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
balance=balance-drawAmount;
System.out.println("取钱成功,当前余额为"+balance);
}else{
System.out.println("余额不足,取钱失败");
}
}
}
第二步把线程中的run方法逻辑,改成调用Account类中的方法
package test20141215;
public class DrawThread extends Thread{
private Account acc;
private double drawAcc;
public DrawThread(String name,Account acc,Double drawAcc){
super(name);
this.acc=acc;
this.drawAcc=drawAcc;
}
public void run(){
// synchronized(acc){
// if(acc.getBalance()>=drawAcc){
// System.out.println("当前线程为"+Thread.currentThread().getName());
// try {
// this.sleep(100);
// } catch (InterruptedException e) {
// // TODO 自动生成的 catch 块
// e.printStackTrace();
// }
// System.out.println("取钱成功,吐钞票");
// acc.setBalance(acc.getBalance()-this.drawAcc);
// System.out.println(getName()+"余额为"+acc.getBalance());
// }else{
// System.out.println(getName()+"余额不足");
// }
// }
acc.draw(drawAcc);
}
}
测试运行
package test20141215;
public class TestDraw {
public static void main(String[] args) {
Account a=new Account("123",1000.0);
DrawThread d1=new DrawThread("czk1",a,500.0);
DrawThread d2=new DrawThread("czk2",a,800.0);
d1.start();
d2.start();
}
}
结果
当前余额为1000.0
取钱成功,当前余额为200.0
余额不足,取钱失败
死锁(两个线程共同访问一个资源,之前的线程得到资源的锁后,在调用另一个线程,但另一个线程也使用相同的资源造成死锁)
package test20141215;
public class deadSyn extends Thread{
public static void main(String[] args) {
final StringBuffer mutex=new StringBuffer("ABCD");
System.out.println("主线程开始");
Thread t=new Thread(){
public void run(){
System.out.println("子线程开始");
synchronized(mutex){
mutex.reverse();
}
}};
t.start();
synchronized (mutex) {
try {
t.join();
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
}