day17 多线程

多线程

1 概念

程序:逻辑和数据集合 静态的方式存储在磁盘中
进程:运行中的程序,动态的方式在内存中运行
线程:是进程的一个执行流程,可以独立完成一个任务,进程可以独立完成多个任务,既进程重有多个线程

1.1进程和线程的区别

1)进程是有自己独立分配的资源区域的,多个线程在该区域空间中共享这些资源。

1.2多线程的目的

是为了提高效率
1)多线程是多个任务同时执行来提高效率的,但是并不是真正的同一时间执行,而是并发执行
2)并发:一个线程会抢夺该cpu执行权,当它抢到了cpu执行权,会执行一段时间,在这个时间之后,
会被其他线程抢到cpu执行权,其他线程执行一会,这样实现了线程的交替执行,整体效率提高
3)多线程是单个任务的效率降低,但是整体的效率是提高的
在这里插入图片描述

1.3java中的多线程

1)每个java项目多会有一个主线程和垃圾回收线程
2)主线程是在java项目运行时就有了该线程,java是支持多线程的,java的多线程和主线程并发执行
3)java的其他线程是主线程开启的,主线程的开启即main方法执行,在main方法中可以开启其他线程
在这里插入图片描述

2 java线程的实现

线程的基类 : Thread

2.1使用Thread的子类

实现步骤
1)自定义一个类,继承Thread
2)重写run()方法
3)在main方法中创建线程对象,并且启动线程

package com.test;
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(i + "---------------");
}
}
}
package com.test;
public class Test4 {
public static void main(String[] args) {
// 创建线程对象
Thread thread = new MyThread();
thread.start();
for (int i = 0; i < 100; i++) {
System.out.println(i + "^^^^^^^^^^^^^^^^^");
}
}
}

注意:启动线程的方法是start()方法,不是run()方法
线程启动后,运行的是run()方法中的内容,即start方法执行以后,会自己自行调用run()方法

2.2 使用Runnable的实现类

步骤:
1)自定义一个类,实现Runnable
2)重写run()方法
3)创建Runnable实现类对象,创建Thread对象,并且将Runnable实现类对象作为构造方法的参数传
给Thread对象
4)启动线程

package com.test;
public class MyRunnable implements Runnable {
int i = 100;
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println("**********" + this.i--);
}
}
}
public class Test5 {
public static void main(String[] args) {
Runnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();
Thread thread2 = new Thread(runnable);
thread2.start();
for (int i = 0; i < 50; i++) {
System.out.println(i);
}
}
}

3 Thread中的方法

3.1 获取线程名称

getName()
1)线程如果没有设置线程名称,会提供一个默认的线程名称,默认为Thread-x,x是从0开始的整数
2)getName()方法可以通过Thread子类直接调用,在Runnable的实现类中没法调用

3.2 设置线程名

1)setName(String name)

// 线程对象
Thread thread = new MyThread();
thread.start();
thread.setName("线程1");
Thread thread1 = new MyThread();
thread1.start();
Runnable runnable = new MyRunnable();
Thread thread2 = new Thread(runnable);
thread2.start();
thread2.setName("任务线程");

2)构造方法
–》Thread(String name)需要给子类添加带参的构造方法

public class MyThread extends Thread {
public MyThread(String name) {
super(name);
}
public MyThread() {
}
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println(i + "---------------" + getName());
}
}
}
public static void main(String[] args) {
// 线程对象
Thread thread = new MyThread();
thread.start();
thread.setName("线程1");
Thread thread1 = new MyThread("线程2");
thread1.start();
}

–》Runnable的实现类,使用Thread(Runnable target, String name)

Runnable runnable = new MyRunnable();
Thread thread2 = new Thread(runnable, "任务线程");
thread2.start();

3.3 获取当前线程对象

Thread提供了一个静态方法,该方法可以获取当前线程对象
Thread.currentThread()

public class MyRunnable implements Runnable {
int i = 100;
@Override
public void run() {
for (int i = 0; i < 50; i++) {
// 获取当前线程对象
System.out.println("**********" + this.i-- +
Thread.currentThread().getName());
}
}
}

案例:
1)获取主线程的线程名称

在main方法中调用
System.out.println(Thread.currentThread().getName());//main
Thread.currentThread().setName(String name);
2)获取垃圾回收线程的名称
public class Demo {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Demo1();
System.gc();
}
}
}
class Demo1 {
@Override
protected void finalize() throws Throwable {
System.out.println(Thread.currentThread().getName());//Finalizer
}
}

3.4 线程休眠

Thread.sleep(毫秒值)
1)当线程抢夺到cpu执行权时,线程运行到sleep方法,会休眠一段时间
2)休眠时间到了,会继续运行
3)在休眠期间,他的cpu执行权时不释放的

3.5 守护线程

多线程中可以设置一些线程为守护线程,其他线程就为非守护线程了
非守护线程一旦全部运行完毕,守护线程如果没有运行完毕,也必须结束
设置方式:setDaemon(boolean on),参数为true时,该线程就为守护线程
判断是否是守护线程:isDaemon()

package com.thread;
public class Demo {
public static void main(String[] args) {
// // 线程对象
Thread thread = new MyThread();
// 守护线程的设置,需要在线程启动之前
thread.setDaemon(true);
thread.start();
Thread thread1 = new MyThread();
thread1.setDaemon(true);
thread1.start();
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName() + "---" + i);
}
}
}
class Demo1 {
@Override
protected void finalize() throws Throwable {
System.out.println(Thread.currentThread().getName());
}
}
垃圾回收线程是否是守护线程
package com.thread;
public class Demo {
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
new Demo1();
System.gc();
}
}
}
class Demo1 {
@Override
protected void finalize() throws Throwable {
System.out.println(Thread.currentThread().getName() +
Thread.currentThread().isDaemon());
}
}

3.6 线程优先级

线程可以设置优先级,优先级别高的线程,巧夺cpu执行去权的概率就高

三个字段
System.out.println(Thread.MAX_PRIORITY);// 线程的最高优先级 10
System.out.println(Thread.MIN_PRIORITY);// 最低 1
System.out.println(Thread.NORM_PRIORITY);// 默认 5
// 设置优先级
Thread thread = new MyThread();
thread.start();
//设置优先级
thread.setPriority(10);
Thread thread2 = new MyThread();
thread2.start();
thread2.setPriority(1);
// 获取优先级
System.out.println(thread2.getPriority());

4 线程安全

案例
火车站售票,多线程售票 西安-北京100

package com.thread;
public class Station extends Thread {
static int num = 100;
@Override
public void run() {
while (num > 0) {
System.out.println(getName() + "卖出一张票,剩余票数:" + num--);
}
}
}
package com.thread;
public class Demo3 {
public static void main(String[] args) {
Thread thread = new Station();
thread.setName("西安");
thread.start();
Thread thread1 = new Station();
thread1.setName("北京");
thread1.start();
Thread thread2 = new Station();
thread2.setName("上海");
thread2.start();
}
}
package com.thread;
public class Sell implements Runnable {
int num = 100;
@Override
public void run() {
while (num > 0) {
System.out.println(Thread.currentThread().getName() + "卖出一张票,剩余
票数:" + num--);
}
}
}
package com.thread;
public class Demo4 {
public static void main(String[] args) {
Runnable sell = new Sell();
Thread xian = new Thread(sell);
xian.setName("西安");
xian.start();
Thread shanghai = new Thread(sell);
shanghai.setName("上海");
shanghai.start();
Thread beijing = new Thread(sell);
beijing.setName("北京");
beijing.start();
}
}

问题

出现了线程安全问题,因为有多个线程强夺cpu执行权,并且运行是共享的数据,就会造成代码运行的
错误

解决办法

将需要进行判断和逻辑运算的代码(可能出现安全问题的代码),保证他们能一个线程运行完毕,其他
线程才参与抢夺
使用同步锁关键词synchronized
格式:

synchronized (锁对象) {
}

作用:
将同步锁中的代码块,一个线程运行期间,其他线程不能抢夺,保证了代码的完整性

package com.thread;
public class Sell implements Runnable {
int num = 100;
Object obj = new Object();
@Override
public void run() {
while (true) {
synchronized (obj) {
if (num > 0) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖出一
张票,剩余票数:" + num--);
} else {
break;
}
}
}
}
}
public class Demo4 {
public static void main(String[] args) {
Runnable sell = new Sell();
Thread xian = new Thread(sell);
xian.setName("西安");
xian.start();
Thread shanghai = new Thread(sell);
shanghai.setName("上海");
shanghai.start();
Thread beijing = new Thread(sell);
beijing.setName("北京");
beijing.start();
Thread tianjin = new Thread(sell);
tianjin.setName("天津");
tianjin.start();
}
}

锁对象

可以是任意对象,但是包保证多个线程是同一个锁对象,才能达到同步的目的
1)当一个线程执行时,会先判断是否有锁对象,有锁对象,如果该锁对象没有线程占用,则该线程执行
同步代码块中的代码,再此期间,其他线程不能获取该同步锁
2)在一个同步锁被使用期间,其他的线程可以使用其他同步锁
3)即代码想要同步,需要使用同一个锁对象
线程安全问题一般不会出现,但是一旦出现就是大问题

package com.thread;
public class Station extends Thread {
static int num = 100;
Object obj;
public Station(Object obj) {
this.obj = obj;
}
@Override
public void run() {
while (true) {
synchronized (obj) {
if (num > 0) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖出一
张票,剩余票数:" + num--);
} else {
break;
}
}
}
}
}
public class Demo3 {
public static void main(String[] args) {
Object object = new Object();
Thread thread = new Station(object);
thread.setName("西安");
thread.start();
Thread thread1 = new Station(object);
thread1.setName("北京");
thread1.start();
Thread thread2 = new Station(object);
thread2.setName("上海");
thread2.start();
}
}

同步方法

想要一个方法中的所有代码需要同步,可以将该方法设置为同步方法
格式:
修饰词 [static] synchronized 返回值类型 方法名(){
//同步代码
}


package com.thread;
public class Sell implements Runnable {
int num = 100;
Object obj = new Object();
@Override
public void run() {
while (true) {
if (!test()) {
break;
}
}
}
// 同步方法
public synchronized boolean test() {
if (num > 0) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖出一张票,剩余
票数:" + num--);
return true;
} else {
return false;
}
}
}

1)同步方法的同步所对象:调用该方法的对象,this
2)静态方法的同步锁对象:该类型的字节码对象 类名.class

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值