多线程
1.进程和线程
进程:一个程序一次执行的过程
线程:一个进程执行会产生多个线程,Java中,主线程和子线程为并发执行
2.并发和并行
并行:多核cpu同时执行多个任务,真正意义上的同时
并发:一个cpu交替执行多个任务,宏观同时,微观交替执行
3.继承Thread类执行start方法
创建一个类继承Thread类,重写run方法,创建类对象,调用start方法启动线程
package com.hspedu.threaduse;
public class Thread01 {
public static void main(String[] args) {
Cat cat = new Cat();
cat.start();
}
}
class Cat extends Thread{
private int time = 0;
@Override
public void run() {
while (true) {
System.out.println("喵喵,我是小猫咪"+(++time));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(time == 8){
break;
}
}
}
}
4.线程执行的机制
主线程调用方法启动子线程时不会造成主线程的阻塞,主线程和子线程并发执行,当主线程消亡时子线程并不会随之消亡,而是等到该线程执行完毕后再消亡,两者在消亡上并没联系。
5.Runable创建线程
定义类实现Runnable接口,重写run方法,创建实现类对象,创建Thread对象,将实现类对象作为Thread类构造方法参数,Thread对象调用start方法祁启动线程。
使用Runnable接口可以更好利用共享资源,并且解决了java单继承的弊端
package com.hspedu.threaduse;
public class Thread02 {
public static void main(String[] args) {
Dog dog = new Dog();
Thread thread = new Thread(dog);
thread.start();
}
}
class Dog implements Runnable{
int count = 0;
@Override
public void run(){
while(true){
System.out.println("小狗汪汪叫"+(++count)+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(count==10){
break;
}
}
}
}
6.通知线程退出
在线程中定义一个状态值,并定义一个set方法,由其他线程调用set方法改变线程的状态值,通知线程退出
package com.hspedu.exit;
public class ThreadExit {
public static void main(String[] args) throws InterruptedException {
T1 t1 = new T1();
new Thread(t1).start();
Thread.sleep(1000);
t1.setFlag(false);
}
}
class T1 implements Runnable {
private int count = 0;
Boolean flag = true;
public void setFlag(Boolean flag) {
this.flag = flag;
}
@Override
public void run() {
while (flag) {
System.out.println("T运行中"+(count++));
}
}
}
7.线程第一组常用方法
常用方法
注意事项
package com.hspedu.method;
public class ThreadMehod {
public static void main(String[] args) throws InterruptedException {
T t = new T();
//给线程设置名称
t.setName("老韩");
//给线程设置优先级,最高为10,最低为1,默认为5
t.setPriority(t.MIN_PRIORITY);
t.start();
//主线程打印5个hi,就中断子线程
for (int i = 0; i < 5; i++) {
Thread.sleep(1000);
System.out.println("hi"+i);
}
//得到子线程的优先级
System.out.println(t.getName()+"的优先级是"+t.getPriority());
//执行到这里时就会中断子线程的休眠,即唤醒子线程
t.interrupt();
}
}
class T extends Thread{
@Override
public void run(){
while(true){
for(int i = 0;i<100;i++){
//Thread.currentThread().getName():获取当前线程名称
System.out.println(Thread.currentThread().getName()+"吃包子~");
}
try {
//使线程休眠20000毫秒
System.out.println("休眠中~");
Thread.sleep(10000);
} catch (InterruptedException e) {
//当线程执行到interrupt方法时就会catch一个异常
System.out.println(Thread.currentThread().getName()+"被打断");
}
}
}
}
8.线程第二组常用方法
礼让只是让线程进入就绪态,然后cpu重新对线程进行调度,所以不一定会成功。
package com.hspedu.method;
public class ThreadMethod01 {
public static void main(String[] args) throws InterruptedException {
T2 t2 = new T2();
t2.start();
for (int i = 1; i <= 20; i++) {
Thread.sleep(1000);
System.out.println("主线程吃了" + i + "个包子");
if(i==5){
System.out.println("主线程让子线程先吃");
//t2.join();//这里相当于让t2线程先执行完
Thread.yield();//礼让,不一定成功
System.out.println("子线程吃完了");
}
}
}
}
class T2 extends Thread {
@Override
public void run() {
for (int i = 1; i <= 20; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("子线程吃了" + i + "个包子");
}
}
}
9.守护线程
package com.hspedu.method;
public class ThreadMethod02 {
public static void main(String[] args) throws InterruptedException {
DaemonThread d = new DaemonThread();
d.setDaemon(true);//将子线程设置为守护线程,当主线程结束后,守护线程就结束
d.start();
for(int i = 0;i<=20;i++){
Thread.sleep(1000);
System.out.println("王宝强在辛苦的工作");
}
}
}
class DaemonThread extends Thread{
@Override
public void run(){
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("马蓉和宋洁在快乐的聊天");
}
}
}
10.线程七大状态
NEW状态:当线程被创建之后,在启动之前线程处于New状态。
Runnable状态:当线程启动后进入Runnable状态,该状态分为Ready状态和Running状态,处于Ready状态的线程等待cpu调度进入Running状态,Running状态也可转换成Ready,详细见上图。
BLOCKED状态:线程等待锁的释放。
TIMED_WAITING状态:线程等待特定的时间。调用了sleep,或者带超时的Object.wait,带超时的join
WAITING状态:线程等待状态。可能因为调用Object.wait没有超时,Thread.join没有超时,LockSupport.park。 线程等待状态是正在等待另一个线程执行特定操作。
TERMINATED状态:线程的结束。
11.线程同步机制
简单理解就是在同一个时刻一个数据只能有一个线程对其进行操作
同步机制解决售票问题
package com.hspedu.ticket;
public class SellTicket02 {
public static void main(String[] args) {
Selltocket3 selltocket3 = new Selltocket3();
new Thread(selltocket3).start();
new Thread(selltocket3).start();
new Thread(selltocket3).start();
}
}
class Selltocket3 implements Runnable{
private int ticketNum = 100;
Boolean loop = true;
@Override
public void run(){
while(loop){
sell();
}
}
private synchronized void sell(){//同步方法,在同一时刻,只能有一个线程执行该方法
if(ticketNum<=0){
System.out.println("票卖完啦");
loop = false;
return;
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"售出一张票,还剩"+(--ticketNum)+"张票");
}
}
12.互斥锁
分析同步原理
当三线程t1,t2,t3执行同一段同步代码块时,t1,t2,t3抢夺同步代码块对象的锁,这里假设t1抢到锁,即t1执行同步代码块,执行完后将所放回锁对象,然后t1,t2,t3再次对锁进行抢夺。(锁是锁在对象上不是在代码块上)
可以是同步代码块或同步方法的方式,多个线程必须抢夺同一把锁
静态方法锁为类本身,非静态方法默认锁为类的对象this
注意:用继承Thread类创建线程时,多个线程当中的this不是同一个对象,即当锁对象为this时,多个线程之间抢夺的不是同一把锁。
13.死锁
死锁例子:
线程a先得到了o1,必须的到o2才能释放o1;与此同时线程b先得到了o2,必须的到o1才能释放o2,两者僵持造成死锁。
package com.hspedu.syn;
public class DeadLock {
public static void main(String[] args) {
DeadLockDemo a = new DeadLockDemo(true);
DeadLockDemo b = new DeadLockDemo(false);
a.start();
b.start();
}
}
class DeadLockDemo extends Thread{
static Object o1 = new Object();
static Object o2 = new Object();
Boolean flag;
public DeadLockDemo(Boolean flag){
this.flag = flag;
}
//如果flag为true,则线程A获得o1,然后尝试获得o2
//如果线程A得不到o2,就会BLOCKED
// 如果flag为false,则线程B获得o2,然后尝试获得o1
//如果线程B得不到o1,就会BLOCKED
@Override
public void run(){
if(flag){
synchronized (o1){
System.out.println("进入1");
synchronized (o2){
System.out.println("进入2");
}
}
}else{
synchronized (o2){
System.out.println("进入3");
synchronized (o1){
System.out.println("进入4");
}
}
}
}
}
13.死锁
下面的操作会释放锁
下面的操作不会释放锁