继承Thread类,重写run方法
public class MyThread extends Thread {
@Override
public void run() {
// 输出100次helloworld
for (int i = 0; i < 100; i++){
System.out.println(getName() + "HelloWorld");
}
}
}
实现Runnable,重写run方法
public class MyRun implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++){
// 获取到当前线程的对象
/*Thread t = Thread.currentThread();
System.out.println(t.getName() + "HelloWorld");*/
System.out.println(Thread.currentThread().getName() + "HelloWorld");
}
}
}
实现Callable,重写call(是有返回值的,表示多线程运行的结果)
import java.util.concurrent.Callable;
public class MyCallable implements Callable {
@Override
public Integer call() throws Exception {
// 求1~100之间的和
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum = sum + i;
}java
return sum;
}
}
1、创建子类对象,这里的子类叫MyThread(MyThread:多线程的类名)
2、开启线程
public class ThreadDemo {
public static void main(String[] args) {
// 创建子类对象
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
// 给线程设置名称
t1.setName("线程1");
t2.setName("线程2");
// 开启线程
t1.start();
t2.start();
}
}
1、创建子类对象,这里的子类叫MyRun(MyRun:多线程的类名)
2、创建多线程对象Thread
3、开启线程
public class ThreadDemo {
public static void main(String[] args) {
// 创建MyRun的对象
// 表示多线程要执行的任务
MyRun mr = new MyRun();
// 创建多线程对象
Thread t1 = new Thread(mr);
Thread t2 = new Thread(mr);
// 给线程设置名称
t1.setName("线程1");
t2.setName("线程2");
// 开启线程
t1.start();
t2.start();
}
}
1、创建子类对象,这里子类叫MyCallable(MyCallable:多线程的类名)
2、创建FutureTask对象(管理多线程运行的结果)
3、创建多线程对象Thread,
4、启动线程
5、获取多线程运行结果
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class ThreadDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建MyCallable对象(表示多线程要执行的结果)
MyCallable mc = new MyCallable();
// 创建FutureTask对象(作用管理多线成运行的结果)
FutureTask ft = new FutureTask<>(mc);
// 创建线程的对象java
Thread t1 = new Thread(ft);
// 启动线程
t1.start();
// 获取多线程运行的结果
Integer result = ft.get(); // 有异常直接抛出
System.out.println(result);
}
}
[子类对象名].setName(String name) 设置现成的名字(构造方法也可以设置名字)
细节:如果没有给线程设置名字,线程也是有默认的名字的——格式:Thread-X(X序号,从0开始)
// 测试类
public class ThreadDemo {
public static void main(String[] args) {
// 创建子类对象
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
// 给线程设置名称
t1.setName("线程1");
t2.setName("线程2");java
// 开启线程
t1.start();
t2.start();
}
}
String getName() 返回此线程的名称(仅限于继承Thread的类中使用)
// 子类 线程类
public class MyThread extends Thread{
public MyThread() {
}
public MyThread(String name) {
super(name);
}
@Override
public void run() {
System.out.println(getName()); // 返回此线程的名称
}
}
// 测试类
public class ThreadDemo {
public static void main(String[] args) {
//1、创建线程的对象
MyThread t1 = new MyThread("飞机"); // 通过构造参数设置线程名称
MyThread t2 = new MyThread("坦克");
// 2、开启线程
t1.start();
t2.start();
}
}
Thread.currentThread(); 获取当前线程的对象(哪条线程执行到这个方法,此时获取的就是哪条线程的对象)
细节:当JVM虚拟机启动之后,会自动的启动多条线程
其中有一条线程就叫做main线程
他的作用就是去调用main方法,并执行里面的代码
以前写的所有的代码,都是运行在main线程当中
// 测试类
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException {
// 获取当前线程对象
Thread t = Thread.currentThread();
String name = t.getName();
System.out.println(name);
String nameOne = Thread.currentThread.getName(); // 获取当前线程对象的名字
System.out.println(nameOne);
}
}
Thread.sleep(1000); 哪条线程执行到这个方法,那么哪条线程就会在这里停留对应的时间
细节:单位是毫秒:1 秒 = 1000毫秒
// 子类 线程类
public class MyThread extends Thread{
public MyThread() {
}
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
// 让线程休眠1000毫秒
Thread.sleep(1000); // 这个地方会有异常,要么try——catch,要么直接抛出
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(getName() + " " + i);
}
}
}
// 测试类
public class ThreadDemo {
public static void main(String[] args) {
//1、创建线程的对象
MyThread t1 = new MyThread("飞机"); // 通过构造参数设置线程名称
MyThread t2 = new MyThread("坦克");
// 2、开启线程
t1.start();
t2.start();
}
}
// 子类 线程类
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
System.out.println(Thread.currentThread().getName() + "---" + i);
}
}
}
[子类对象名].setPriority(1~10); 设置线程的优先级
[子类对象名].getPrioriry(); 获取线程的优先级
细节:默认线程优先级是:5
// 测试类
public class ThreadDemo {
public static void main(String[] args) {
// 创建线程要执行的参数对象
MyRunnable mr = new MyRunnable();
// 创建线程对象
Thread t1 = new Thread(mr,"飞机");
Thread t2 = new Thread(mr,"坦克");
// 获取线程优先级
System.out.println(t1.getPriority());
System.out.println(t2.getPriority());
// 设置线程优先级(1~10,数字越大越容易抢到执行权)
t1.setPriority(1);
t2.setPriority(10);
// 启动线程
t1.start();
t2.start();
}
}
当其他非守护线程执行完毕之后,守护线程会陆续的结束
当qq聊天的时候,同时给好友发送文件,当聊天界面关闭了,文件也没必要继续发送了(发送文件会停止)
// 子类 线程类
public class MyThread01 extends Thread{
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println(getName() + "---" + i);
}
}
}
// 子类 线程类
public class MyThread02 extends Thread{
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
System.out.println(getName() + "---" + i);
}
}
}
[子类对象名].setDaemon(true); 设置为守护线程
// 测试类
public class ThreadDemo {
public static void main(String[] args) {
// 创建子类对象
MyThread01 t1 = new MyThread01();
MyThread02 t2 = new MyThread02();
// 设置线程名称
t1.setName("女神");
t2.setName("备胎");
// 把第二个线程设置为守护线程()
t2.setDaemon(true);
// 启动线程
t1.start();
t2.start();
}
}
Thread.yield(); 表示出让当前CPU的执行权
// 子类 线程类
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
System.out.println(getName() + "---" + i);
// 表示出让当前CPU的执行权
Thread.yield();
}
}
}
细节:让出CPU的执行权以后,两条线程重新开始抢夺执行权,所以让CPU执行权的线程还可能会抢到线程执行权
// 测试类
public class ThreadDemo {
public static void main(String[] args) {
// 创建子类对象
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
// 给线程设置名称
t1.setName("飞机");
t2.setName("坦克");
// 启动线程
t1.start();
t2.start();
}
}
// 子类 线程类
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
System.out.println(getName() + "---" + i);
}
}
}
[子类对象名].join(); 表示把这个线程插入到当前线程之前
// 测试类
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException {
// 创建子类对象
MyThread t = new MyThread();
// 设置线程名称
t.setName("土豆");
// 启动线程
t.start();
// 表示把t这个线程插入到当前线程之前
// t:土豆
// 当前线程:main线程
t.join();
// 执行在main线程当中的
for (int i = 1; i <= 10; i++) {
System.out.println("main线程" + i);
}
}
}
线程安全性问题通常出现在多线程环境中,当多个线程同时访问和修改同一份数据时,如果没有适当的同步控制,可能会导致数据的不一致性,这就是线程安全性问题。
第一种:竞态条件
电影院售票:电影院有三个窗口共卖100张票
重复的票:由于线程在执行过程中随时会被别的线程抢走执行权,所以可能还没来得及输出结果,就被别的线程抢走了执行权,然后别的线程又执行,该条线程抢到执行权以后,就会打印重复的票。
超出范围的票:当售票到99张以后,窗口1抢到执行权,还没来得及输出,又被窗口二抢到执行权,这时候的值就变成了101,就会打印超出范围的票。
解决方法:使用同步代码块或同步方法
第二种:死锁
两个人抢筷子吃饭:两个人抢筷子吃饭,一次只能抢一只筷子,当一个人抢到一双筷子,另一个人抢一个筷子,这时候双方都会等对方先放下筷子,就会造成死锁。
解决方法:控制锁顺序,避免嵌套
第三种:活锁
夫妻用勺子吃饭:一对夫妻用一把勺子吃饭,双方都很有礼貌,总是让对方先吃,当发现对方饿了,就会把勺子让给对方,这会导致出现活锁。
解决方法:synchronized同步代码块,同步方法,Lock锁
同步代码块,当线程抢到执行权,进入同步代码块后,只有执行完同步代码块中的代码,别的线程才能重新抢夺CPU执行权
package com.zsh.threaddemo.threadtest01;
public class MyThread extends Thread{
// 表示这个类所有的对象,都共享num数据
static int num = 0;
// 锁对象,一定要是唯一的
static Object obj = new Object();
@Override
public void run() {
while (true){
// 同步代码块
synchronized (obj){ // obj:锁对象,任意对象都可以,但必须是唯一的(一般情况下用类名.class)
if (num < 100){
num++;
System.out.println(getName() + "正在出售第" + num + "张票!!!");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}else {
break;
}
}
}
}
}
package com.zsh.threaddemo.threadtest01;
public class ThreadDemo {
public static void main(String[] args) {
/*
* 某电影院目前正在上映一部国产大片,共有100张票,而且有3个售票口,请写出程序模拟该电影售票
* */
// 创建子类对象
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
// 给线程起名字
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
// 开启线程
t1.start();
t2.start();
t3.start();
}
}
细节:
如果不知道怎么写同步方法,那就先写同步代码块,
选中同步代码块中的代码按Ctrl+Alt+M自动创建方法,在修饰符和返回类型之间加上synchronized关键字即可
package com.zsh.threaddemo.threadtest02;
public class MyRunnable implements Runnable{
int num = 0;
@Override
public void run() {
// 循环
while (true){
// 同步代码块(同步方法)
if (method()) break;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
private synchronized boolean method() {
if(num == 100){
return true;
}else {
num++;
System.out.println(Thread.currentThread().getName() + "正在出售第" + num + "张票!!!");
}
return false;
}
}
package com.zsh.threaddemo.threadtest02;
public class ThreadDemo {
public static void main(String[] args) {
/*
* 某电影院目前正在上映一部国产大片,共有100张票,而且有3个售票口,请写出程序模拟该电影售票
* (使用同步方法写)
* */
// 创建MyRunnable对象,表示多线程要执行的任务
MyRunnable mr = new MyRunnable();
// 创建多线程对象
Thread t1 = new Thread(mr);
Thread t2 = new Thread(mr);
Thread t3 = new Thread(mr);
// 设置线程名称
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
// 开启线程
t1.start();
t2.start();
t3.start();
}
}
Lock锁提供了比Java内置的synchronized
关键字更灵活、更强大的同步控制功能。
lock.lock(); 获取锁
lock.unlock(); 释放锁
细节:创建Lock时,不能直接new Lock要new Lock的实现类RenntrantLock
package com.zsh.threaddemo.threadtest03;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyThread extends Thread{
static int num = 0;
// 为了保证多个线程用的是同一个锁,所以加static静态方法
static Lock lock = new ReentrantLock(); // 不能直接new Lock要new Lock的实现类RenntrantLock
@Override
public void run() {
while (true){
// 上锁
lock.lock();
try {
if(num == 100){
break;
}else {
num++;
System.out.println(getName() + "正在出售第" + num + "张票!!!");
}
} finally {
// 开锁
lock.unlock();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
}
package com.zsh.threaddemo.threadtest03;
public class ThreadDemo {
public static void main(String[] args) {
/*
* Lock锁
* */
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
要使用try,finally,来保证释放锁
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyClass {
private final Lock lock = new ReentrantLock(); // 使用final修饰Lock锁
public void doSomething() {
lock.lock(); // 获取锁
try {
// 在这里执行同步操作
} finally {
lock.unlock(); // 释放锁
}
}
}
线程池是一个容器,可以保存一些线程对象,这些线程可以反复使用。
降低资源消耗,重复利用线程池中的线程,不需要每次都创建、销毁。
便于线程管理,线程池可以集中管理并发线程的数量。
// 测试类
package com.zsh.demo10多线程;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo {
public static void main(String[] args) {
// 创建线程池
ExecutorService pool = Executors.newFixedThreadPool(3);
System.out.println("pool = " + pool);
// 提交Runnable任务
MyRunnable mr = new MyRunnable();
pool.submit(mr);
pool.submit(mr);
pool.submit(mr);
pool.submit(mr);
pool.submit(mr);
// 关闭线程池
pool.shutdown();
}
}
// 子类
package com.zsh.demo10多线程;
public class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "任务执行完成");
}
}
好处:有返回值。
可以抛异常。
// 测试类
package com.zsh.demo10多线程.callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Demo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建线程池
ExecutorService pool = Executors.newFixedThreadPool(3);
// 创建Callable任务
MyCallable mc1 = new MyCallable(100);
Future future = pool.submit(mc1);
MyCallable mc2 = new MyCallable(200);
Future future2 = pool.submit(mc2);
// 拿到返回值
Integer ret1 = future.get();
System.out.println("结果1:" + ret1);
Integer ret2 = future2.get();
System.out.println("结果2:" + ret2);
// 关闭线程池
pool.shutdown();
}
}
// 子类
package com.zsh.demo10多线程.callable;
import java.util.concurrent.Callable;
public class MyCallable implements Callable {
// 求1-n的值
int n;
public MyCallable(int n) {
this.n = n;
}
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= n; i++) {
sum += i;
}
System.out.println(Thread.currentThread().getName() + "执行任务完毕!");
return sum;
}
}