多线程
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