多线程
1.创建线程方式
创建线程方式一:继承Thread类,重写run()方法,调用start开启线程
总结:线程开启不一定立即执行,有cpu调度执行
package com.zhang.linePro;
public class TestThread1 extends Thread {
@Override
public void run() {
//run方法线程体
for (int i = 0; i <200 ; i++) {
System.out.println("我在看代码");
}
}
public static void main(String[] args) {//main()线程 ,主线程
//创建一个线程对象
TestThread1 testThread1 = new TestThread1();
//调用start方法开启线程
testThread1.start();//方法二:new Thread(testThread1).start();
for (int i = 0; i <1000 ; i++) {
System.out.println("我在学习多线程"); //我在学习和我在看代码交替执行
}
}
}
创建线程方法二: 实现runnable接口,重写run()方法,执行线程需要丢入Runnable接口实现类,调用start方法
推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用。
创建线程方法三:实现callable接口 callable好处:可以定义返回值,可以抛出异常。
2.Lamda表达式
package com.zhang.linePro;
public class Demo02 {
public static void main(String[] args) {
Ilova ilova = ()->{
System.out.println("aini");
};
ilova.love();
}
interface Ilova{
void love();
}
}
3.线程停止
推荐线程自己停下来,建议使用一个标志位进行终止变量,当flag=false,终止线程运行
package com.zhang.linePro;
public class TestStop implements Runnable {
private boolean flag=true;
@Override
public void run() {
for (int i = 0; i <1000 ; i++) {
while (flag){
System.out.println("xiancheng"+i++);
}
}
}
//停止线程方法
public void Stop(){
flag=false;
}
public static void main(String[] args) {
TestStop testStop = new TestStop();
new Thread(testStop).start();
for (int i = 0; i <1000 ; i++) {
System.out.println("main"+i);
if (i==900) {testStop.Stop();//当主线程中的i到900的时候,次线程就停止,后面不在输出xianchengi了
System.out.println("线程该停止了");}
}
}
}
4.线程礼让(yield)
将线程从运行状态转为阻塞状态。让cpu重新调度,礼让不一定成功
package com.zhang.linePro;
public class TestYield {
public static void main(String[] args) {
MyYield myYield = new MyYield();
new Thread(myYield,"a").start();
new Thread(myYield,"b").start();
}
static class MyYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始");
Thread.yield();//线程礼让
System.out.println(Thread.currentThread().getName()+"线程开始");
}
}
}
5.线程强制执行(join)
package com.zhang.linePro;
public class TestYield {
public static void main(String[] args) throws InterruptedException {
MyYield myYield = new MyYield();
Thread thread = new Thread(myYield);
thread.start();
for (int i = 0; i < 500; i++) {
if (i==200) thread.join(); //线程执行到1000,主线程才能执行剩下的
System.out.println("zhuxian"+i);
}
}
static class MyYield implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("vip"+i);
}
}
}
}
线程五大状态
- new 尚未启动
- RUNNABLE 运行
- BLOCK 阻塞
- TIMED_WAITTING timed_waitting 等待
- TERMINATED terminated 终止
线程优先级
先设置优先级,再启动。
Thread thread = new Thread(myYield);
thread.setPriority(2)//线程优先级1-10
thread.start();
守护线程
线程分为用户线程和守护线程
虚拟机不用等待守护线程执行完毕,但是必须确保用户线程执行完毕
守护线程:后台记录操作日志,监控内存,垃圾回收等。
Thread thread = new Thread(myYield);
thread.setDaemon(true)//默认是false表示用户线程
thread.start();
6.线程同步机制
7.死锁
产生死锁的四个必要条件:
- 互斥条件:一个资源每次只能被一个进程使用
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
- 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
package com.zhang.linePro;
//死锁:多个线程互相拿着对方所需的资源,形成死锁
public class DeadBlock {
public static void main(String[] args) {
Makeup g1 = new Makeup(0,"灰姑娘");
Makeup g2 = new Makeup(1,"小公猫");
new Thread(g1).start();
new Thread(g2).start();
}
}
//资源
class Lipstick{
//口红
}
class Mirror{
//镜子
}
class Makeup implements Runnable{ //化妆
//需要的资源只有一份,用static来确保资源只有一份
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
int choice;
String girlname;
Makeup(int choice,String girlname){
this.choice = choice;
this.girlname = girlname;
}
//化妆,互相持有对方的锁,持有对方所需的资源
private void makeup() throws InterruptedException {
if (choice == 0) {
synchronized (lipstick) {//口红锁,死锁锁的是对象
System.out.println(this.girlname + "获得口红");
Thread.sleep(1000);//一秒钟后想获得镜子
synchronized (mirror) {
System.out.println(this.girlname + "获得镜子");
}
}
} else {
synchronized (mirror) {//镜子锁
System.out.println(this.girlname+ "获得镜子");
Thread.sleep(2000);//两秒钟后想获得口红
synchronized (lipstick) {
System.out.println(this.girlname + "获得口红");
}
}
}
}
@Override
public void run() {
try { //化妆
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
想破开这个死锁:把资源变成多份或者是把使用时间调成一致,再或者是把下面的死锁从上个死锁里拿出来(口红用完了再等一秒,等别人把镜子用完了,你再用)
Lock锁
private final ReentrantLock lock = new ReentrantLock();定义lock锁
Lock锁和synchronized的对比
-
Lock锁是显式锁(手动开启锁,别忘记手动关闭锁)。synchronized是隐式锁,出了作用域自动释放。
-
Lock只有代码块锁,synchronized有代码块锁和方法锁
-
使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)
-
优先使用顺序:Lock>同步代码块(已经进了方法体,分配了相应资源)>同步方法
package com.zhang.linePro;
import java.util.concurrent.locks.ReentrantLock;
public class TestLock {
public static void main(String[] args) {
TestLock1 testLock = new TestLock1();
new Thread(testLock).start();
new Thread(testLock).start();
new Thread(testLock).start();
}
}
class TestLock1 implements Runnable{
int ticketnums=10;
//定义lock锁
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while(true) {
try{lock.lock(); //加锁
if (ticketnums > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(ticketnums--);
}else break;
}finally {
lock.lock(); // 解锁
}
}
}
}