1.线程介绍
单线程:同一个时刻,只允许执行一个线程
多线程:同一个时刻,允许执行多个线程
并发:同一时刻,多个任务交替执行。
并行:同一时刻,多个任务同时执行。
2.线程使用
方式一:继承Thread类,重写run方法
public class thread01 {
public static void main(String[] args) {
//创建Cat对象,可以当作线程使用
Cat cat = new Cat();
cat.start();//启动线程-->最终会执行cat的run方法
//主线程main和子线程cat会交替执行,主线程不会阻塞
//如果这里还有代码,也会执行,不是等到子线程执行完,才执行这里的代码
}
}
class Cat extends Thread{
//1.当一个类继承了thread类,该类就是线程类
//Thread的run方法是实现Runnable接口的run方法
int times=0;
@Override
public void run() {//重写run方法
while (true){
System.out.println("喵喵"+(++times)+"线程名称="+Thread.currentThread().getName());
try {
Thread.sleep(1000);//毫秒=1秒
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if(times == 80){
break;
}
}
}
}
方式二:实现Runable接口,重写run方法(静态代理)
例1:Thread类代理
public class runnable02 {
public static void main(String[] args) {
T2 t2 = new T2();
//这里不能直接调用star
//创建一个Thread对象,把t2对象传入
Thread thread=new Thread(t2);
//底层是静态代理模式
thread.start();
}
}
class T2 implements Runnable{
int count=0;
@Override
public void run() {
while (true){
System.out.println("修狗"+(count++));
//休眠一秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
例2:Cat1类模拟Thread类的静态代理
public class runable {
public static void main(String[] args) {
Cat1 cat1=new Cat1();
new Proxy(cat1).start();
}
}
class Animal{}
class Cat1 extends Animal implements Runnable{
@Override
public void run() {
System.out.println("猫咪");
}
}
//模拟的Thread
class Proxy implements Runnable{
private Runnable target=null;
@Override
public void run() {
if (target!=null){
target.run();
}
}
public Proxy(Runnable target) {
this.target = target;
}
public void start(){
start0();
}
public void start0(){
run();
}
}
图竖着看
Thread和Runnable 本质上没有区别,都是start()-->start0()-->run()。
Runnable接口更适合多个线程共享一个资源,避免了单继承的情况。
T3 t3 = new T3();
Thread thread1 = new Thread(t3);
Thread thread2 = new Thread(t3);
thread1.start();
thread2.start();
如何通知线程停止?
public class Thread03 {
public static void main(String[] args) throws InterruptedException {
T3 t3 = new T3();
Thread thread1 = new Thread(t3);
thread1.start();
Thread.sleep(10 * 1000);//主线程休眠10秒,子线程还在一直跑
t3.setLoop(false);//10秒之后,主线程恢复,通知子线程停止
}
}
class T3 implements Runnable{
private boolean loop=true;
int n=10;
@Override
public void run() {
while (loop){
System.out.println("hello world"+(n++));
try {
Thread.sleep(1000);//子线程每隔1秒输出一次
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
public void setLoop(boolean loop) {
this.loop = loop;
}
}
3.线程方法
常用方法第一组:
1.setName ()//设置线程名称,使之与参数 name 相同
2. getName ()//返回该线程的名称
·3.start() //使该线程开始执行; Java 虚拟机底层调用该线程的
4.run ()//调用线程对象 run 方法
5.setPriority() //更改线程的优先级
6.getPriority() //获取线程的优先级
7. sleep()//在指定的毫秒数内让当前正在执行的线程休眠
8. interrupt()//中断线程
public class ThreadMothd01 {
public static void main(String[] args) throws InterruptedException {
T t = new T();
t.setName("qwq");
t.setPriority(Thread.MAX_PRIORITY);
t.start();
//主线程打印5 hi,然后我就中断 子线程休眠
for (int i = 0; i < 5; i++) {
Thread.sleep(1000);
System.out.println("hi"+i);
}
t.interrupt();
}
}
class T extends Thread{
@Override
public void run() {
while (true){
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+"吃饱了"+i);
}
System.out.println(Thread.currentThread().getName()+"休眠---");
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+"唤醒了---");
}
}
}
常用方法第二组:
1. yield():线程的礼让。让出cpu,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功。
2.join(): 线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务
案例: 创建一个子线程 ,每隔1s 输出 hello,输出 20次,主线程每隔1秒,输出 hi,输出 20次.要求: 两个线程同时执行,当主线程输出 5次后,就让子线程运行完毕,主线程再继续。
public class TreadMothed01 {
public static void main(String[] args) throws InterruptedException {
Hi hi = new Hi();
hi.start();
for (int i = 0; i < 20; i++) {
System.out.println("hi");
Thread.sleep(500);
if(i==4){
System.out.println("第五个");
hi.join();
}
}
}
}
class Hi extends Thread{
private int n=0;
@Override
public void run() {
while (true){
System.out.println("hello");
n=n+1;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if(n==20){
break;
}
}
}
}
用户线程和守护线程
1.用户线程:也叫工作线程,当线程的任务执行完结束或通知方式结束。
2.守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束。常见的守护线程: 垃圾回收机制。
如何设计一个守护线程?
答://先设置成守护线程,再启动,否则报错
hi.setDaemon(true);
public class TreadMothed01 {
public static void main(String[] args) throws InterruptedException {
Hi hi = new Hi();
//如果我们希望主线程结束,子线程自动结束
//把子线程设计成守护线程
//先设置成守护线程,再启动,否则报错
hi.setDaemon(true);
hi.start();
for (int i = 1; i <= 10; i++) {
System.out.println("海贼王qwq");
Thread.sleep(1000);
}
}
}
class Hi extends Thread{
private int n=0;
@Override
public void run() {
while (true){
System.out.println("hello");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
4.线程生命周期
5.Synchronized
使用同步访问技术,保证数据在任何同一时刻,最多有一个线程访问,以保证数据的完整性。
具体实现方法-synchronized:
1.同步代码块
synchronized(对象){ // 得到对象的锁,才能操作同步代码
// 需要被同步代码;}
2.同步方法
public synchronized void m (String name)!
//需要被同步的代码
注意事项和细节:
1.同步方法如果没有使用static修饰: 默认锁对象为this
2.如果方法使用static修饰,默认锁对象:当前类.class
3.实现的落地步骤
(1).需要先分析上锁的代码
(2).选择同步代码块或同步方法
(3).要求多个线程的锁对象为同一个即可!
public class spiao {
public static void main(String[] args) {
per per = new per();
new Thread(per).start();
new Thread(per).start();
new Thread(per).start();
}
}
//实现接口方式,使用synchronized实现线程同步
class per implements Runnable {//创建Runnable接口的实现类
private int ticket = 50;
private static boolean lop=true;//控制run方法变量
Object o = new Object();
//同步方法:静态
//1.public synchronized static void m() {}
//2.锁在per.class
//3.也可以在代码块上写 synchronized ,同步代码块,锁是在类上
public synchronized static void m1() {
}
public static void m2() {
synchronized (per.class){
System.out.println("aa");
}
}
//同步方法:非静态
//1.public synchronized void m() {}是一个同步方法
//2.这时锁在 this 对象上
//3.也可以在代码块上写 synchronized ,同步代码块,锁还是在this对象上
public /*synchronized*/ void m() {//同一时刻,只有一个线程执行m方法
synchronized (/*this*/ o) {
if (ticket <= 0) {
System.out.println("售票结束");
lop = false;
return;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("窗口还有" + (ticket--) + "票");
}
}
@Override
public void run() {
while (lop) {
m();
}
}
}
6.互斥锁
1.每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。
2.关键字synchronized 来与对象的互斥锁联系。当某个对象用synchronized修饰时表明该对象在任一时刻只能由一个线程访问.
3.同步的局限性:导致程序的执行效率要降低
4.同步方法(非静态的)的锁可以是this,也可以是其他对象(要求是同一个对象)
5.同步方法(静态的)的锁为当前类本身
7.死锁
拿到a锁,才能拿b锁,然后执行代码
拿到b锁,才能拿a锁,然后执行代码
执行2个线程,一个拿到a锁,一个拿到b锁,都拿不到剩下的锁,就形成了死锁。
释放锁
1.当前线程的同步方法、同步代码块执行结束
2.当前线程在同步代码块,同步方法中遇到break、return.
3.当前线程在同步代码块,同步方法中出现了未处理的Error或Exception,导致异常结束
4.当前线程在同步代码块、同步方法中执行了线程对象的wait0方法,当前线程暂停,并释
放锁。
8.练习
1.多线程执行
请编写一个程序,创建两个线程,一个线程每隔1秒输出“hello,world”,输出10
次,退出,一个线程每隔1秒输出“hi”,输出 5次退出。
public class Thread03 {
public static void main(String[] args) {
T3 t3 = new T3();
T4 t4 = new T4();
new Thread(t3).start();
new Thread(t4).start();
for (int i = 0; i < 5; i++) {
System.out.println("主线程。。不会阻塞");
}
}
}
class T3 implements Runnable{
int n=10;
@Override
public void run() {
while (true){
System.out.println("hello world"+(n++));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (n==10){
break;
}
}
}
}
class T4 implements Runnable{
int m=5;
@Override
public void run() {
while (true){
System.out.println("hi"+(m++));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (m==5){
break;
}
}
}
}
2.
(1)在main方法中启动两个线程
(2)第1个线程循环随机打印100以内的整数
(3) 直到第2个线程从键盘读取了“Q”命令
public class e1 {
public static void main(String[] args) {
A a = new A();
B b = new B(a);//传Q入a才能控制a
a.start();
//输完Q之后按回车,才会把Q传给k
b.start();
}
}
class A extends Thread{
private boolean loop=true;
public void setLoop(boolean loop) {
this.loop = loop;
}
@Override
public void run() {
while (loop){
System.out.println( (int) (Math.random()*100) + 1 );
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
class B extends Thread{
private A a;
private Scanner scanner = new Scanner(System.in);
public B(A a){
this.a=a;
}
@Override
public void run() {
while (true){
System.out.println("请输入字符。。。");
char k = scanner.next().toUpperCase().charAt(0);
if(k == 'Q'){
a.setLoop(false);//a线程退出
break;//b线程也退出
}
}
}
}
3.
2个人同时取10000块钱,每次取1000,不能超取
public class e2 {
public static void main(String[] args) {
User user = new User();
new Thread(user).start();
new Thread(user).start();
}
}
class User implements Runnable{
private int num=10000;
@Override
public void run() {
while (true){
synchronized (this){//那个线程获取到this锁,就执行代码块
//得不到的就阻塞
if(num>=1000){
num=num-1000;
System.out.println(Thread.currentThread().getName()+"取1000"+"当前余额"+num);
}else {
System.out.println("没钱了");
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
}