一、线程和进程
1、线程
注意:多线程。从宏观角度同时执行了多个线程。
从微观角度同一时间只能执行一个线程
多个线程是竞争关系,抢占cpu资源,否则只能等待。
2、进程和线程的区别:
进程是应用程序,线程是一条执行路径
进程有独立的内存空间,崩溃不会影响其他程序,
线程没有独立的空间,多个线程在同一个进程的空间,可能会影响其他线程
一个进程中,至少有一个线程
3、主线程子线程
主线程:main方法产生的线程,也叫作UI线程。
子线程:除了主线程以外的,也叫工作线程。
4、创建线程的两种方式
1、创建一个类继承Thread
2、重写run方法
3、创建线程对象
4、启动线程
5、Thread.currentThread().getName(),哪个线程调用,名字就是哪个现成的名字
getName();super调用父类的getName(),被赋值谁的名字,就打印谁的名字
main方法:
Test2 test=new Test2("一号窗口");test.start();
Test2 test2=new Test2("二号窗口");test2.start();
class Test2 extends Thread{
String name;
int ticket=10;
public Test2(String name) {
super(name);
this.name = name;
}
public void run() {
while (true) {
if (ticket>0) {
ticket--;
System.out.println(Thread.currentThread().getName()+"还剩下"+ticket);
}else {
break;}
}
}
共享资源操作相同
1、共享资源类实现Runable接口
2、重写run方法
3、创建共享资源对象
4、创建线程对象,将共享资源对象添加到线程中
5、启动线程
main方法:
Test3 test3=new Test3();
Thread thread=new Thread(test3);
Thread thread2=new Thread(test3);
thread.start();
thread2.start();
class Test3 extends Thread{
String name;
int ticket=10;
public Test2(String name) {
super(name);
this.name = name;
}
public void run() {
while (true) {
if (ticket>0) {
ticket--;
System.out.println(Thread.currentThread().getName()+"还剩下"+ticket);
}else {
break;}
}
}
贡献资源操作不相同
1、贡献资源作为一个单独的类
2、由多个操作去实现Runable接口
3、把共享资源作为多个操作类的属性
4、创建线程对象,将共享资源对象添加到线程中
5、启动线程
main方法:
Card card=new Card();
Boyfriend boyfriend=new Boyfriend(card);
Girlfriend girlfriend=new Girlfriend(card);
Thread thread=new Thread(boyfriend);
Thread thread2=new Thread(girlfriend);
thread.start();
thread2.start();
class Card{
double money;
}
class Boyfriend implements Runnable{
Card card;
public Boyfriend(Card card){
this.card=card;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 5; i++) {
card.money+=500;
System.out.println(Thread.currentThread().getName()+"存500-剩余金额"+card.money);
}
}
}
class Girlfriend implements Runnable{
Card card;
public Girlfriend(Card card){
this.card=card;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 5; i++) {
card.money-=500;
System.out.println(Thread.currentThread().getName()+"取500,剩余金额"+card.money);
}
}
}
5、run和start的区别
run没有开辟新的栈空间,没有新线程,都是主线程在执行
start开辟了新的栈空间,在新的栈空间启动run()方法
6、线程的调度
setPriority();分配优先级,默认5,最低1,最高10
.join();插队,阻塞指定的线程等到另一个线程完成以后再继续执行
.sleep();需要设置睡眠时间
.yield();礼让,当执行到这个方法时,会让出cpu时间,立马变成可执行状态
sleep和pield的区别:
sleep yeild
线程进入被阻塞的状态 线程转入暂停执行的状态
(没有其他线程运行)等待指定的时间再运行 马上恢复执行的状态
其他线程的执行机会是均等的 将优先级或更高的线程运行
7、打断线程的终止方式
1、用标记,当终止线程时,会执行完run方法
2、stop()方法,不建议使用,会执行不到特定的代码
3、interrupt(),只能中断正在休眠的线程,通过抛异常的方法中断线程的终止。
InputStream inputStream=System.in;
int m=inputStream.read();
myThread2.interrupt();//通过外界输入打断
8、线程是五种状态
新建 就绪 执行 死亡 阻塞
二、同步
发生在两个以两个以上的线程中
解决代码的重复问题
优点:提高了线程中数据的安全性
缺点:降低了执行效率
1、同步代码块
synchronized(锁){同步代码块}
注意:锁分任意锁和互斥锁,锁是对象,琐是唯一的。
2、同步方法
public synchroinzed 返回值类型 方法名(){同步代码}
3、在共享资源中:
线程操作相同,琐是this
synchronized (this) {// 同步代码块,包含同步代码块。任意锁,互斥锁。
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "---" + ticket--);
} else {
break;
}
}
线程操作不相同,琐是共享资源对象
synchronized (card) {
card.setMoney(card.getMoney() + 1000);
System.out.println("Boy+1000---" + card.getMoney());
}
4、在同步方法中:
共享资源,线程操作相同,资源类中的锁是this
共享资源,线程操作不相同,资源类中的锁也是this
public synchronized void input(){
money+=100;
System.out.println("input+100----"+money);
}
5、在静态方法中同步:懒汉式
同步代码块,琐是类.class
同步方法,锁也是类.class
public static LazyInstance getInstance(){
if (instance==null) {
synchronized (LazyInstance.class) {
if (instance==null) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
instance=new LazyInstance();
}
System.err.println(instance.hashCode());
}
}
return instance;
}
三、经典例子:生产者消费者
面包类:class Bread{属性和构造方法}
超市类:class Market{
Bread[] breads=new Bread[];//超市里有面包数组
int index=-1;//一开始没有面包,下标为-1;
public synchronized void sale(){
if(index<=-1){如果没有没有面包,就等待添加
this.wait();
}
如果有面包,就打印面包信息
System.out.println("消费面包"+breads[index].id+breads[index].name+breads[index].price);
index--;//面包减少一个
this.notify();唤醒添加线程
}
public synchronized vide add(Bread bread){
if(index>=4){
this.wait();
}
indenx++;//面包下标+1,存入下一面包位置中
breads[index]=bread;//给数组中的面包赋值
System.out.println("添加面包"+breads[index].id+breads[index].name+breads[index].price);
this.notify();//唤醒销售线程
}
工厂类:实现Runnable接口:
将超市类作为属性
添加构造方法
重写run方法,调用超市类add方法
顾客类:实现Runnable接口:
将超市类作为属性
添加构造方法
重写run方法,调用超市类sale方法