# 1线程的创建和启动
package ThreadLearnDemo01;
public class FirstThread extends Thread{
private int i;
public void run(){
for(;i<100;i++)
{
System.out.println(this.getName()+" "+i);
}
}
public static void main(String[] args) {
for(int i=0;i<100;i++) {
if (i == 30) {
new FirstThread().start();
new FirstThread().start();
}
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}
package ThreadLearnDemo01;
public class FirstThread extends Thread{
private int i;
public void run(){
for(;i<100;i++)
{
System.out.println(this.getName()+" "+i);
}
}
public static void main(String[] args) {
for(int i=0;i<100;i++) {
if (i == 30) {
new FirstThread().start();
new FirstThread().start();
}
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}
在运行的时候出现了一种奇怪的现象。
从整体上看,两个线程都是用同一个target firstthread来初始构造的,共享这一个target的资源,但是在运行过程中出现了乱序,这是一种不安全的线程。
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class ThreadLearnDemo02 {
public static void main(String[] args) {
//Callable是runable接口的升级版,提供一个call()方法可以作为线程执行体,
//call可以有返回值,可以抛出异常
//用lamdba创建一个Callable的无参构造
//try catch会一直等到start的线程执行结束
FutureTask<Integer> task=new FutureTask<Integer>((Callable <Integer>)()->{
int j=0;
for(;j<100;j++)
{
System.out.println(Thread.currentThread().getName()+" "+j);
}
return j;
});
int i=0;
for(;i<100;i++)
{
System.out.println(Thread.currentThread().getName()+" "+i);
if(i==3)
{
new Thread(task, "Callable创建的线程").start();
}
}
try{
System.out.println("子线程返回值"+task.get());
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
}
new出来的线程是新建状态,该状态和其他的java对象一样,没有表现出任何线程的动态特征。
调用start()方法之后,java虚拟机会为其创建方法调用栈和程序计数器,线程何时开始运行,去决议jvm里线程调度器的调度。
package ThreadLearnDemo03;
public class ThreadLearnDemo03 extends Thread{
private int i;
public void run(){
for(;i<100;i++)
{
//this.getName()返回的是该对象的名字
//不是当前线程的名字
//使用Thread.currentThread().getName()返回的是该线程的名字
System.out.println(Thread.currentThread().getName()+" "+i+this.getName());
}
}
public static void main(String[] args) {
for(int i=0;i<100;i++)
{
//调用了run之后就不再处于新建状态了,不要再调用start
System.out.println(Thread.currentThread().getName());
if(i==50){
new ThreadLearnDemo03().run();
new ThreadLearnDemo03().run();
}
}
}
}
什么时候会进入阻塞状态:
针对上面情况。发生如下特定情况可以解除上面的阻塞。
线程结束方式:
当主线程结束时,其他线程不受影响,并不会随之结束,一旦子线程启动起来后,它就拥有和主线程相同的地位,不受主线程的影响
测试线程是否死亡的方法:isAlive() 当处与新建和死亡两种状态时,该方法返回false。
不要对一个已经死亡的线程调用start或者run,死亡就是死亡,如果调用会引发IllegalThreadState Exception异常
package ThreadLearnDemo04;
public class ThreadLearnDemo04 extends Thread{
public ThreadLearnDemo04(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 ThreadLearnDemo04("新线程").start();
for(int i=0;i<100;i++)
{
if(i==20)
{
ThreadLearnDemo04 threadLearnDemo04 = new ThreadLearnDemo04("被join的线程");
threadLearnDemo04.start();
threadLearnDemo04.join();
}
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}
主线程经过join处于等待状态,新线程和被join线程并发执行。
主线程必须等待被join线程执行完之后才能继续执行。
后台线程的任务是为其它线程提供服务,如果前台线程都死亡,后台会自动死亡。使用setDaemon(true)将指定线程设置成后台线程。
package ThreadLearnDemo05;
public class ThreadLearnDemo05 extends Thread{
public void run()
{
for(int i=0;i<1000;i++)
{
System.out.println(getName()+" "+i+" "+this.getName());
}
}
public static void main(String[] args) {
ThreadLearnDemo05 threadLearnDemo05 = new ThreadLearnDemo05();
threadLearnDemo05.setDaemon(true);
threadLearnDemo05.start();
for(int i=0;i<100;i++)
{
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}
在这个程序中,创建的子线程不会跑完
staic void sleep(long millis)
static void sleep(long millis,int nanos)
package ThreadLearnDemo06;
import java.util.Date;
public class ThreadLearnDemo06 {
public static void main(String[] args) throws InterruptedException {
for(int i=0;i<10;i++){
System.out.println("当前时间:"+new Date());
Thread.sleep(1000);
}
}
}
这个程序会每隔一秒输出一条语句
不建议使用,通过分配给线程优先级,然后yield让系统线程调度器使所有线程处于就绪态,重新调度一次,在多CUP机器效果不明显。
package com.luojias.threadlearn.demo01;
public class Demo01 extends Thread{
public Demo01(String name)
{
super(name);
}
public void run()
{
for(int i=0;i<1000;i++)
{
System.out.println(getName()+" "+getPriority()+" "+" "+i);
}
}
public static void main(String[] args) {
Thread.currentThread().setPriority(6);
for(int i=0;i<30;i++)
{
if(i==10)
{
Demo01 low = new Demo01("低级");
low.start();
System.out.println("低级创建时候的优先级"+low.getPriority());
low.setPriority(MIN_PRIORITY);
}
if(i==20)
{
Demo01 high=new Demo01("高级");
System.out.println("高级创建时候的优先级"+high.getPriority());
high.setPriority(MAX_PRIORITY);
high.start();
}
}
}
}
输出图:
开始:
最后:
得出如下结论:线程的默认优先级是主线程的优先级
优先度高的线程获得更多的执行机会。
另外需要注意:
由于操作系统不同,应该使用MAX_PRIORITY、MIN_PRIORITY、NORM_PRIORITY三个静态常量来设置优先级
银行取钱问题
package com.luojias.threadlearn.demo02;
public class Account {
private String accountNo;
private double balance;
public Account(){}
public Account (String accountNo,double balance)
{
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;
}
//用hasCode判断账户编号是否相等
public int hasCode()
{
return accountNo.hashCode();
}
//用equals判断账户是否相等
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);//类相同时判断accountNo是否相同,就是这一步,因为两个相同对象的hascode必须相同,所以需要重写hashcode
}
return false;
}
//整个重写研究了好一会
}
package com.luojias.threadlearn.demo02;
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() {
if (account.getBalance() >= drawAmount) {
System.out.println(getName() + "取钱成功吐出钞票:" + drawAmount);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.setBalance(account.getBalance() - drawAmount);
System.out.println("余额为:" + account.getBalance());
} else {
System.out.println("余额不足");
}
}
}
package com.luojias.threadlearn.demo02;
public class Demo02 {
public static void main(String[] args) {
Account acct=new Account("123456",1000);
new DrawThread("甲",acct,900).start();
new DrawThread("乙",acct,800).start();
}
}
运行main函数后会出现各种错误,有的时候余额都是负数,有的时候都是整数,有的时候一正一负。
为了解决上面出现的问题可以使用synchronized,也就是加锁操作
package com.luojias.threadlearn.demo02;
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() {
synchronized (account) {
if (account.getBalance() >= drawAmount) {
System.out.println(getName() + "取钱成功吐出钞票:" + drawAmount);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.setBalance(account.getBalance() - drawAmount);
System.out.println("余额为:" + account.getBalance());
} else {
System.out.println("余额不足");
}
}
}
}
这样有一个线程在修改的时候,account就会被锁住,别的线程,无法使用account,在修改完毕后,释放锁
同步方法能够方便地实现线程安全的类,具有以下特征
这里直接锁run是不能锁住的,因为每次run的是一个新的线程。
所以必须在共享的资源里加锁
把锁加在共享的Account解决问题
package com.luojias.threadlearn.demo02;
public class Account {
private String accountNo;
private double balance;
public Account(){}
public Account (String accountNo,double balance)
{
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;
}
//用hasCode判断账户编号是否相等
public int hasCode()
{
return accountNo.hashCode();
}
//用equals判断账户是否相等
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);//类相同时判断accountNo是否相同,就是这一步,因为两个相同对象的hascode必须相同,所以需要重写hashcode
}
return false;
}
//整个重写研究了好一会
public synchronized void draw(double drawAmount) {
if (balance >= drawAmount) {
System.out.println( accountNo+ "取钱成功吐出钞票:" + drawAmount);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
setBalance(balance - drawAmount);
System.out.println("余额为:" + balance);
} else {
System.out.println("余额不足");
}
}
}
结束:
不会释放同步监视器的情况:
用lock可以设置时间。如果锁住的时间过长,然后释放
package com.luojias.threadlearn.demo02;
import java.util.concurrent.locks.ReentrantLock;
public class Account {
private String accountNo;
private final ReentrantLock lock=new ReentrantLock();
private double balance;
public Account(){}
public Account (String accountNo,double balance)
{
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;
}
//用hasCode判断账户编号是否相等
public int hasCode()
{
return accountNo.hashCode();
}
//用equals判断账户是否相等
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);//类相同时判断accountNo是否相同,就是这一步,因为两个相同对象的hascode必须相同,所以需要重写hashcode
}
return false;
}
//整个重写研究了好一会
public synchronized void draw(double drawAmount) {
lock.lock();
try {
if (balance >= drawAmount) {
System.out.println(accountNo + "取钱成功吐出钞票:" + drawAmount);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
setBalance(balance - drawAmount);
System.out.println("余额为:" + balance);
} else {
System.out.println("余额不足");
}
}
finally {
lock.unlock();
}
}
}
这是代码块都在等待对方释放资源,然后都无法继续进行的时候,称为死锁。
两者用法基本相同,一个是调用wait notifyall notify另一个是调用await signal signalall
贴上测试代码,可能有点繁琐,其实可以把123三种方法写成同一个方法。
package com.luojias.threadlearn.demo03;
import java.util.Objects;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Account {
private String accountNo;
private double balance;
private boolean flag=false;
private final ReentrantLock lock= new ReentrantLock();
private final Condition cond= lock.newCondition();
//构造函数
public Account(){ }
public Account(String accountNo,double balance){this.accountNo=accountNo;this.balance=balance;}
//get set方法
public String getAccountNo() {
return accountNo;
}
public double getBalance() {
return balance;
}
public void setAccountNo(String accountNo) {
this.accountNo = accountNo;
}
//重写equals和hashCode函数
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Account account = (Account) o;
return accountNo.equals(account.accountNo);
}
public int hashCode() {
return Objects.hash(accountNo);
}
public void draw(double balance) {
lock.lock();
try {
if (!flag) {
cond.await();
} else {
System.out.println("取钱成功" + balance + " " + Thread.currentThread().getName());
flag = false;
balance = 0;
cond.signalAll();
}
}catch(InterruptedException er){
er.printStackTrace();
}
finally {
lock.unlock();
}
}
public void deposit(double money) {
lock.lock();
try {
if(flag){
cond.await();
}
else{
balance=money;
flag=true;
System.out.println("存钱成功1");
cond.signalAll();
}
}catch (InterruptedException ex) {
ex.printStackTrace();
}finally {
lock.unlock();
}
}
public void deposit2(double money) {
lock.lock();
try {
if(flag){
cond.await();
}
else{
balance=money;
flag=true;
System.out.println("存钱成功2");
cond.signalAll();
}
}catch (InterruptedException ex) {
ex.printStackTrace();
}finally {
lock.unlock();
}
}
public void deposit3(double money) {
lock.lock();
try {
if(flag){
cond.await();
}
else{
balance=money;
flag=true;
System.out.println("存钱成功3");
cond.signal();
}
}catch (InterruptedException ex) {
ex.printStackTrace();
}finally {
lock.unlock();
}
}
}
package com.luojias.threadlearn.demo03;
public class DepositThread extends Thread{
private Account account;
private double depositAmount;
public DepositThread(String name,Account account,double depositAmount){
super(name);
this.account=account;
this.depositAmount=depositAmount;
}
@Override
public void run() {
for(int i=0;i<100;i++)
{
account.deposit(depositAmount);
}
}
}
package com.luojias.threadlearn.demo03;
public class DepositThread2 extends Thread{
private Account account;
private double depositAmount;
public DepositThread2(String name,Account account,double depositAmount){
super(name);
this.account=account;
this.depositAmount=depositAmount;
}
@Override
public void run() {
for(int i=0;i<100;i++)
{
account.deposit(depositAmount);
}
}
}
package com.luojias.threadlearn.demo03;
public class DepositThread3 extends Thread{
private Account account;
private double depositAmount;
public DepositThread3(String name,Account account,double depositAmount){
super(name);
this.account=account;
this.depositAmount=depositAmount;
}
@Override
public void run() {
for(int i=0;i<100;i++)
{
account.deposit(depositAmount);
}
}
}
package com.luojias.threadlearn.demo03;
public class DrawThread extends Thread{
private Account account;
private double drawAmount;
public DrawThread(String name,Account account,double drawAmount){
super(name);
this.drawAmount=drawAmount;
this.account=account;
}
@Override
public void run() {
for(int i=0;i<100;i++)
{
account.draw(drawAmount);
}
}
}
package com.luojias.threadlearn.demo03;
public class Test {
public static void main(String[] args) {
Account acct=new Account("1234567",0);
new DrawThread("取钱者",acct,800).start();
new DepositThread("存期者1",acct,800).start();
new DepositThread2("存期者2",acct,800).start();
new DepositThread3("存期者3",acct,800).start();
}
}
小demo
package com.luojias.threadlearn.demo04;
import java.util.concurrent.ArrayBlockingQueue;
public class BlockingQueueTest {
public static void main(String[] args) throws InterruptedException {
ArrayBlockingQueue<String> bq=new ArrayBlockingQueue<String>(2);
bq.put("Java");//等同于bq.offer bq.add
bq.put("Java");
bq.put("Java");//在此处阻塞线程
}
}
实例:
package com.luojias.threadlearn.demo05;
import java.util.concurrent.BlockingQueue;
public class Consumer extends Thread{
private BlockingQueue<String>bq;
public Consumer(BlockingQueue<String>bq){
this.bq=bq;
}
public void run(){
while (true){
System.out.println(getName()+" 消费者准备消费集合");
try {
Thread.sleep(200);
bq.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName()+"消费完成"+bq);
}
}
}
package com.luojias.threadlearn.demo05;
import java.util.concurrent.BlockingQueue;
public class Producer extends Thread{
private BlockingQueue<String>bq;
public Producer(BlockingQueue<String>bq){
this.bq=bq;
}
public void run(){
String []strArr=new String[]{
"java","i","love"
};
for(int i=0;i<9999999;i++){
System.out.println(getName()+"生产者准备生产集合元素");
try {
Thread.sleep(200);
bq.put(strArr[i%3]);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName()+" 生产完成 "+bq);
}
}
}
package com.luojias.threadlearn.demo05;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class BlockingQueueTest2 {
public static void main(String[] args) {
BlockingQueue<String>bq=new ArrayBlockingQueue<String>(1);
new Producer(bq).start();
new Producer(bq).start();
new Producer(bq).start();
new Consumer(bq).start();
}
}
未完待续