创建线程并启动:继承Thread类
step1:创建子类,继承Thread类。
step2:重写run(),线程体。并发执行的内容,写在这个方法中。
step3:启动线程:start()
class Cat
class Person
class MyException extends Exception{//异常类
}
class MyThread extends Thread{//线程类
}
对比两种创建并启动线程的方式:
线程的常用方法:
* Thread类中有一个静态的方法:currentThread(),用于获取当前正在执行的线程
* getName(), Thread.currentThread().getName()获取当前线程的名字
A:线程的Id,线程的唯一标识:long的整数,终身不变。
由系统自动编号,程序员只能获取getId(),不能更改
B:线程的Name:线程的名称
设置和获取
setName()
getName()
构造方法:提供名称
Thread类构造方法:Thread(String name);
C:获取当前的线程对象:静态的方法
Thread.currentThread()--->Thread对象
谁正在被执行,就获取哪个线程对象——>获取当前的对象
理解为:该方法在哪个线程中调用,就是获取哪个线程对象。
D:线程的Priority,优先级
int整数:
MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5
t1,t2,t3..main...-->默认的优先级:5
setPriority(),getPriority()
优先级高,执行的机会多。优先级低,执行的机会少。
不绝对。
E:线程的睡眠:sleep,静态的方法
Thread.sleep(time)
当前正在执行的线程,进入了睡眠,放弃了CPU的执行。和哪个线程对象来调用无关。
理解为:该方法写在哪个线程中调用,就是哪个线程进入睡眠。
F:线程的合并:join
t1,t2,main线程
main线程中:执行了t1.join()
等待线程死亡——>在main线程中,要等待t1线程结束后,main线程才能执行。
G:守护线程:daemon
setDaemon(true)-->设置为守护线程,为前台线程提供服务,如果所有的前台线程都结束了,那么守护线程就结束了。
isDaemon()-->boolean
H:其他的方法
阻塞:
暂时不能执行:
等待:
有期限:sleep(time)
无期限:join()
同步代码块
synchronized(锁对象){//开始给对象上锁,t1
}//打开对象的锁
特征:
A:synchronized小括号里的对象,是锁对象,并要求多线程的情况下,锁对象必须是同一个对象。
B:synchronized大括号里的代码,就是同步执行的代码。或者说是加了锁的代码。意味着每次只能被一个线程执行。
C:同步的代码越少越好,在保证安全的情况下,提高性能。
D:锁对象:理论上谁都可以,只要是多个线程访问的共同对象即可。
this关键字,外面创建对象传入,类名.class,"abc",
//方法同步
public synchronized void test(){
}
synchronized(锁对象){
}
注意点:
同步代码块:自己指定锁对象。
同步方法:锁对象是固定的。
普通的同步方法:锁对象:this
静态的同步方法:锁对象:类名.class
线程之间的通信
线程之间的通信:wait(),notify(),notifyAll()
wait()——>让线程进入阻塞状态,暂停执行。一直阻塞
notify()——>唤醒线程,wait()住的线程,被唤醒。如果多个线程wait()了,唤醒其中的一个。
notifyAll()——>唤醒所有。
生产者与消费者
public class homework3 {
public static void main(String[] args) {
Bank ban=new Bank(3000);
ATM at=new ATM(ban,2000);
new Thread(at,"丈夫").start();
new Thread(at,"媳妇").start();
}
}
//ATM
class ATM implements Runnable{
private Bank bank;
private double money;
public ATM(Bank bank, double money) {
super();
this.bank = bank;
this.money = money;
}
public ATM() {
super();
}
@Override
public void run() {
bank.getMoney(money);
}
}
//银行
class Bank{
double balance;
public Bank(){
}
public Bank(double money){
balance = money;
}
public synchronized void getMoney(double money){
System.out.println(Thread.currentThread().getName()+"查询当前余额为"+balance+"元");
if(balance>=money){
balance-=money;
System.out.println(Thread.currentThread().getName()+"取款"+money+"元,当前余额为"+balance+"元");
}else {
System.out.println("余额不足");
}
}
}
线程的生命周期
线程池
//1.创建一个固定数量的线程池:
ExecutorService es = Executors.newFixedThreadPool(3);
//第2种:创建一个线程池对象:里面有对应的线程对象
ExecutorService es = Executors.newCachedThreadPool();
示例代码:
//step1:创建一个线程池对象:里面存放一些线程
//第1种:这种线程池中的线程的数量是固定的:由参数来指定
//t1,t2,t3
// ExecutorService es = Executors.newFixedThreadPool(3);
//第2种:创建一个线程池对象:里面有对应的线程对象
ExecutorService es = Executors.newCachedThreadPool();
//step2:创建任务,Runnable接口的实现
Task t1 = new Task();
Task t2 = new Task();
Task t3 = new Task();
Task t4 = new Task();
Task t5 = new Task();
//step3:提交任务:将任务提交给线程池,分配线程对象执行任务对应的run()
es.submit(t1);//将t1这个任务提交给es线程池
es.submit(t2);//将t1这个任务提交给es线程池
es.submit(t3);//将t1这个任务提交给es线程池
es.submit(t4);//将t1这个任务提交给es线程池
es.submit(t5);//将t1这个任务提交给es线程池
将一个String类型的日期数据,解析为一个Date对象。并处理异常。
public class HomeWork3 {
public static void main(String[] args) {
/*
* Date和String之间可以转换:SimpleDateFormat类
* 日期格式化:
* A:Date-->String
* format(),格式化
* B:String-->Date
* parse(),解析
*
* step1:提供模板(省略,使用默认的模板)
*
* step2:创建SimpleDateFormat类的对象
*
* step3:格式化,或者解析。。
*/
// yyyy年MM月dd日
String s1 = "2000-4-26";//String
//Date类型对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
try {
Date date = sdf.parse(s1);
System.out.println(date);
} catch (ParseException e) {
e.printStackTrace();
System.out.println("解析失败。。。");
}
}
}