线程笔记整理
-
线程是在进程里面的,一个进程里面有多个线程 包括主线程(main)、gc线程(垃圾回收)是由jvm自带的线程,以及其他线程。
线程创建
-
方式一
-
继承Thread类,重写run()方法,调用start开启线程
package demo01;
public class Therad1 extends Thread{
-
-
总结:
-
没有主次关系,线程开启不一定立即执行该线程,是由cpu调度的
-
package demo02;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
public class TestThread2 extends Thread {
private String url;
private String name;
//编写构造器
public TestThread2(String url,String name) {
this.url = url;
this.name = name;
}
//重写run()方法
-
方式二
-
实现runnable接口,重写run()方法,创建new Thread()对象,调用start,把实现接口的对象传进去。
package demo03;
//实现接口Runnable
public class TestThread3 implements Runnable{
package demo02;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
public class TestThread2 implements Runnable {
private String url;
private String name;
public TestThread2(String url,String name){
this.url=url;
this.name=name;
}
-
-
总结:
-
第一种方式继承Thread类
-
子类继承Thread类具备多线程能力
-
启动线程:子类对象.start()
-
但是不建议使用:避免OOP单继承局限性
-
第二种方式实现Runnable接口
-
实现接口Runnable具有多线程能力
-
启动线程:传入目标对象+Thread对象.start()
-
推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用
-
并发问题
package demo03;
public class TestThread4 implements Runnable{
//多个线程操作同一个数据时,会出现数据紊乱,线程不安全
private int trickNums=10;
Lamda表达式
-
函数式接口的定义:
-
任何接口,如果里面只有一个抽象方法,那么他就是一个函数式接口
public interface Runnable {
public abstract void run();
}
-
-
package demo03;
public class TestLamda {
//2.static内部类
/*static class Student implements Teacher{
@Override
public void love(int a) {
System.out.println("Hello"+a);
}
}*/
public static void main(String[] args) {
Teacher teacher=null;
//3.局部内部类
/* class Student implements Teacher{
@Override
public void love(int a) {
System.out.println("Hello"+a);
}
}*/
//匿名内部类,没有类的名称,必须借助接口和父类
/* new Teacher() {
@Override
public void love(int a) {
System.out.println("Hello"+a);
}
}.love(3);*/
//lamada简化
teacher=(a,b)-> {System.out.println("Hello"+(a+b)); };
teacher.love(4,9.8);
}
}
//创建接口:创建一个函数式接口,接口里面只能有一个抽象方法
interface Teacher {
void love(int a,double b);
}
//1.创建实现类接口
/*
class Student implements Teacher{
@Override
public void love(int a) {
System.out.println("Hello"+a);
}
}*/
停止线程
-
new --->就绪状态--->运行状态---->阻塞状态或者dead状态
-
测试stop:
-
建议线程正常停止--->利用次数,不建议死循环
-
建议使用标志位--->设置一个标志位
-
不要使用stop或者destroy等过时或者jdk不建议使用的方法
-
我们一般建议使用一个标志位终止变量当flag为false时,停止该线程运行
package demo03;
public class TestStop implements Runnable {
//定义一个线程标识
private boolean flag=true;
//使用该线程标识
-
线程休眠
-
sleep指定当前线程阻塞的毫秒数
-
sleep存在异常
-
sleep时间到达后线程进入就绪状态
-
sleep可以模拟网络延时,倒计时等
-
每一个对象都有一个锁,sleep不会释放锁
public static void main(String[] args) {
//模拟网络延时
Date date = new Date(System.currentTimeMillis());
while (true){
try {
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));
date = new Date(System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package demo03;
//模拟倒计时
public class TestSleep {
public static void main(String[] args) {
try {
tenDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void tenDown() throws InterruptedException {
int i = 10;
while (true) {
Thread.sleep(1000);
System.out.println(i--);
if (i <= 0) {
break;
}
}
}
}
线程礼让
-
让正在执行的线程暂停,但不阻塞
-
将线程从运行状态转为就绪状态
-
让cpu重新调度,礼让不一定成功,看cpu心情
package demo03; public class TestYield { public static void main(String[] args) { MyYield myYield = new MyYield(); new Thread(myYield,"a").start(); new Thread(myYield,"b").start(); } } class MyYield implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"开始执行"); Thread.yield(); System.out.println(Thread.currentThread().getName()+"结束执行"); } }
线程强制执行
-
Join合并线程,待此线程完成后,在执行其他线程,其他线程阻塞
-
可以想成插队
package demo03; public class TestJoin implements Runnable { @Override public void run() { for (int i = 1; i <= 200; i++) { System.out.println("vip"+i); } } public static void main(String[] args) { //线程启动 TestJoin testJoin = new TestJoin(); Thread thread = new Thread(testJoin); thread.start(); //主线程 for (int i = 1; i <= 500; i++) { if (i == 100) { try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("main"+i); } } } -
线程对象一旦创建,就进入到了新生状态
-
当调用start()方法,线程立即进入就绪状态,但并不意味着立即调度执行
-
进入运行状态,线程才真正执行线程体的代码块
-
当调用sleep、wait或同步锁定时,线程进入阻塞状态,就是代码不往下执行,阻塞事件解除后,重新激怒人就绪状态,等待cpu调度执行
-
线程中断或者结束,一旦进入死亡状态,就不能再次启动
线程的优先级
-
范围1~10
-
min=1;最小
-
max=10 最大
-
main方法默认5
-
使用以下方式改变或获取优先级
-
getPrioity.setPrioity(int xxx);
-
-
优先级高的不一定一定会先执行,只是先执行的概率更大了 ,都看cpu的调度
-
先设置优先级,在启动线程
package demo03; public class TestPrioity { public static void main(String[] args) { System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority()); MyPrioity myPrioity = new MyPrioity(); Thread t1 = new Thread(myPrioity,"线程一"); Thread t2 = new Thread(myPrioity,"线程二"); Thread t3 = new Thread(myPrioity,"线程三"); Thread t4 = new Thread(myPrioity,"线程四"); Thread t5 = new Thread(myPrioity,"线程五"); Thread t6 = new Thread(myPrioity,"线程六"); t6.setPriority(8); t6.start(); t1.setPriority(1); t1.start(); t2.setPriority(Thread.MAX_PRIORITY); t2.start(); /* //优先级越界 t3.setPriority(11); t3.start(); //优先级越界 t4.setPriority(-1); t4.start(); */ //最大优先级 t5.setPriority(Thread.MIN_PRIORITY); t5.start(); } } class MyPrioity implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority()); } }
守护进程
-
线程分为用户线程和守护进程
-
虚拟机必须确保用户线程执行完毕
-
虚拟机不用等待守护进程执行完毕
-
如:后台记录操作日志,监控内存,垃圾回收等待
package demo03; public class TestDaemon { public static void main(String[] args) { God god = new God(); You you = new You(); Thread thread = new Thread(god); //把上帝变成守护进程 thread.setDaemon(true); thread.start(); new Thread(you).start(); } } class You implements Runnable{ @Override public void run() { for (int i = 0; i < 300; i++) { System.out.println("人生不过36500天"+i); } } } class God implements Runnable{ @Override public void run() { while (true){ System.out.println("上帝一直守护着你"); } } }
线程同步
-
并发:同一个对象被多个线程同时操作
-
由于同一个线程同享一快存储空间,在带来方便的同时,,也会带访问冲突问题,为了保证数据在方法中被访问时的正确性,,在访问时加入锁机制synchronized,当一个线程 获得对象的排他锁,独占资源,其他线程必须等待,使用后释放锁 即可,会引起性能问题,如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题
Lock
-
lock是显示锁(手动开启和手动关闭,别忘记关闭锁),synchronized是隐式锁,出了作用域自动释放,
-
使用lock锁性能更好
package demo03;
public class TestLock {
public static void main(String[] args) {
MyLock myLock = new MyLock();
new Thread(myLock,"小黄").start();
new Thread(myLock,"小白").start();
new Thread(myLock,"小黑").start();
}
}
class MyLock implements Runnable{
int trickNums=10;
@Override
//并发不安全我们可以用synchronized给方法加锁
public synchronized void run() {
while (true){
if (trickNums>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到了第"+trickNums--);
}else {
break;
}
}
}
}
package demo03;
import java.util.concurrent.locks.ReentrantLock;
public class TestLock {
public static void main(String[] args) {
MyLock myLock = new MyLock();
new Thread(myLock,"小白").start();
new Thread(myLock,"小黑").start();
}
}
class MyLock implements Runnable{
//定义lock锁
ReentrantLock lock=new ReentrantLock();
int trickNums=10;
@Override
public void run() {
while (true){
try {
//上锁
lock.lock();
if (trickNums>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到了第"+trickNums--);
}else {
break;
}
} finally {
//解开锁
lock.unlock();
}
}
}
}

浙公网安备 33010602011771号