多线程
1、线程中的概念
1.1、什么是进程、什么进程
进程是一个应用进程(1个进程就是一个软件)
线程是一个进程中的执行场景、执行单元
一个进程可以启动多个线程。
1.2、进程和线程的关系
在java语言中:
? 线程A和线程B,堆内存和方法区内存共享。
? 但是栈内存独立,一个线程一个栈。
java中之所以有多线程机制,目的就是为了提高程序的处理效率。
2、Java中的多线程
java支持多线程机制,并且java已经将多线程实现了,我们只需要继承就行了。
第一种方式:编写一个类,直接继承java.lang.Thread ,重写run方法。
package com.caopeng.java.thread;
public class ThreadTest01 {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
for(int i = 0;i < 1000;i++) System.out.println("主线程--->" + i);
}
}
class MyThread extends Thread {
@Override
public void run() {
for(int i = 0;i < 1000;i++) System.out.println("分支线程--->" + i);
}
}
第二种方式:编写一个类实现java.lang.Runnable 接口
package com.caopeng.java.thread;
public class ThreadTest02 {
public static void main(String[] args) {
Thread thread = new Thread(new MyRunnable());
thread.start();
for(int i = 0;i < 1000;i++) System.out.println("主线程--->" + i);
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
for(int i = 0;i < 1000;i++) System.out.println("分支线程--->" + i);
}
}
使用匿名内部类:
package com.caopeng.java.thread;
public class ThreadTest03 {
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0;i < 1000;i++) System.out.println("分支线程--->" + i);
}
});
thread.start();
for(int i = 0;i < 1000;i++) System.out.println("主线程--->" + i);
}
}
线程生命周期
获取线程对象
package com.caopeng.java.thread;
public class ThreadTest05 {
public static void main(String[] args) {
Thread currentThread = Thread.currentThread();
System.out.println(currentThread.getName());
MyThread2 myThread1 = new MyThread2();
System.out.println(myThread1.getName());
myThread1.setName("t2");
System.out.println(myThread1.getName());
MyThread2 myThread2 = new MyThread2();
System.out.println(myThread2.getName());
myThread1.start();
myThread2.start();
}
}
class MyThread2 extends Thread {
@Override
public void run() {
Thread thread = Thread.currentThread();
System.out.println(thread.getName());
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ne0bDgYK-1635059132255)(C:\Users\Crescent_P\AppData\Roaming\Typora\typora-user-images\image-20211023170447329.png)]
线程的sleep()方法
package com.caopeng.java.thread;
public class ThreadTest06 {
public static void main(String[] args) {
for(int i = 1;i <= 10;i++){
System.out.println(Thread.currentThread().getName() + "--->" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
package com.caopeng.java.thread;
public class ThreatTest07 {
public static void main(String[] args) {
Thread t = new MyThreat3();
t.setName("t");
t.start();
try {
t.sleep(1000 * 5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(("Hello World"));
}
}
class MyThreat3 extends Thread{
@Override
public void run() {
for (int i = 0;i < 1000;i++) System.out.println(Thread.currentThread().getName() + "--->" + i);
}
}
package com.caopeng.java.thread;
public class ThreatTest08 {
public static void main(String[] args) {
Thread thread = new Thread(new MyRunnable2());
thread.setName("t");
thread.start();
try {
Thread.sleep(1000 * 5);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.interrupt();
}
}
class MyRunnable2 implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "---> begin");
try {
Thread.sleep(1000 * 60 * 60 * 24 * 365);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "--->end");
}
}
强制终止线程
package com.caopeng.java.thread;
public class ThreadTest09 {
public static void main(String[] args) {
Thread thread = new Thread(new MyRunnable3());
thread.setName("t");
thread.start();
try {
Thread.currentThread().sleep(1000 * 5);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.stop();
}
}
class MyRunnable3 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "--->" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
合理终止线程
package com.caopeng.java.thread;
public class ThreadTest10 {
public static void main(String[] args) {
MyRunnable4 myRunnable4 = new MyRunnable4();
Thread thread = new Thread(myRunnable4);
thread.setName("t");
thread.start();
try {
Thread.sleep(1000 * 5);
} catch (InterruptedException e) {
e.printStackTrace();
}
myRunnable4.run = false;
}
}
class MyRunnable4 implements Runnable {
boolean run = true;
@Override
public void run() {
for (int i = 0; i < 10; i++) {
if(run){
System.out.println(Thread.currentThread().getName() + "--->" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
return;
}
}
}
}
Java中线程调度方法
实例方法:
void setPriority(int newPriority)
int getPriority()
最低优先级 1
默认优先级 5
最高优先级 10
void join()
class MyThread1 extends Thread{
public void dosome(){
Mythread2 t = new Mythread2();
t.join();
}
}
class Mythread2 extends Thread{}
静态方法:
static void yield()
暂停当前正在执行的线程方法,并执行其它线程
yield()方法不是阻塞方法,让当前线程让位,让给其它线程使用
yield()方法的执行会让当前线程从“运行状态”回到“就绪状态”
package com.caopeng.java.thread;
public class ThreadTest11 {
public static void main(String[] args) {
Thread.currentThread().setPriority(1);
Thread thread1 = new Thread(new MyRunnable5());
thread1.setPriority(10);
thread1.start();
for (int i = 0; i < 10000; i++) {
System.out.println(Thread.currentThread().getName() + "--->" + i);
}
}
}
class MyRunnable5 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
System.out.println(Thread.currentThread().getName() + "--->" + i);
}
}
}
package com.caopeng.java.thread;
public class TreadTest12 {
public static void main(String[] args) {
Thread thread = new Thread(new MyRunnable6());
thread.setName("t");
thread.start();
for (int i = 1; i <= 10000; i++) {
System.out.println(Thread.currentThread().getName() + "--->" + i);
}
}
}
class MyRunnable6 implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 10000; i++) {
if(i % 100 == 0) Thread.yield();
System.out.println(Thread.currentThread().getName() + "--->" + i);
}
}
}
package com.caopeng.java.thread;
public class ThreadTest13 {
public static void main(String[] args) {
System.out.println("main begin...");
Thread thread = new Thread(new MyRunnable7());
thread.setName("t");
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main over....");
}
}
class MyRunnable7 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "--->" + i);
}
}
}
线程安全
什么时候存在线程安全问题呢?
满足以上三个条件,就会存在线程安全问题
怎么解决?
线程排队执行,不能并发。称为:线程同步机制
会牺牲一定的效率,但是安全。
模拟取钱出错
Account.java
package com.caopeng.java.thread.threadsafe;
public class Account {
private String accountId;
private double balance;
public Account() {
}
public Account(String accountId, double balance) {
this.accountId = accountId;
this.balance = balance;
}
public String getAccountId() {
return accountId;
}
public void setAccountId(String accountId) {
this.accountId = accountId;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public void withDraw(double money){
double before = this.balance;
double after = before - money;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.setBalance(after);
}
}
AccountThread.java
package com.caopeng.java.thread.threadsafe;
public class AccountThread extends Thread{
private Account account;
public AccountThread(Account account) {
this.account = account;
}
@Override
public void run() {
double money = 5000;
account.withDraw(money);
System.out.println(Thread.currentThread().getName() + "对账户" + account.getAccountId() + "取款: "+ money+ " 成功,余额为:" + account.getBalance());
}
}
Test.java
package com.caopeng.java.thread.threadsafe;
public class Test {
public static void main(String[] args) {
Account account = new Account("act-001",10000);
AccountThread thread1 = new AccountThread(account);
AccountThread thread2 = new AccountThread(account);
thread1.start();
thread2.start();
}
}
解决
使用Synchronized关键字
package com.caopeng.java.thread.threadsafe2;
public class Account {
private String accountId;
private double balance;
public Account() {
}
public Account(String accountId, double balance) {
this.accountId = accountId;
this.balance = balance;
}
public String getAccountId() {
return accountId;
}
public void setAccountId(String accountId) {
this.accountId = accountId;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public void withDraw(double money){
synchronized(this){
double before = this.balance;
double after = before - money;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.setBalance(after);
}
}
}
package com.caopeng.java.thread.threadsafe2;
public class AccountThread extends Thread{
private Account account;
public AccountThread(Account account) {
this.account = account;
}
@Override
public void run() {
double money = 5000;
account.withDraw(money);
System.out.println(Thread.currentThread().getName() + "对账户" + account.getAccountId() + "取款: "+ money+ " 成功,余额为:" + account.getBalance());
}
}
package com.caopeng.java.thread.threadsafe2;
public class Test {
public static void main(String[] args) {
Account account = new Account("act-001",10000);
AccountThread thread1 = new AccountThread(account);
AccountThread thread2 = new AccountThread(account);
thread1.start();
thread2.start();
}
}
有线程安全问题的变量
Java中有三大变量。
实例变量:堆
静态变量:方法区
局部变量:栈
局部变量一定没有线程安全问题,因为局部变量不共享,一个线程一个栈。
synchronized出现在实例方法上
package com.caopeng.java.thread.threadsafe3;
public class Account {
private String accountId;
private double balance;
public Account() {
}
public Account(String accountId, double balance) {
this.accountId = accountId;
this.balance = balance;
}
public String getAccountId() {
return accountId;
}
public void setAccountId(String accountId) {
this.accountId = accountId;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public synchronized void withDraw(double money){
double before = this.balance;
double after = before - money;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.setBalance(after);
}
}
synchronized的三种写法:
-
同步代码块 synchronized(线程共享对象){
同步代码块
}
-
实例方法上使用synchronized,表示共享对象一定是this,并且同步代码块是整个方法体 -
在静态方法上使用synchronized,表示找类锁,类锁只有一把,不管创建了多少个对象,只有一把类锁
Synchronized面试题
题一:
package com.caopeng.java.thread.exam1;
public class Exam01 {
public static void main(String[] args) {
MyClass myClass = new MyClass();
Thread thread1 = new MyThread(myClass);
Thread thread2 = new MyThread(myClass);
thread1.setName("t1");
thread2.setName("t2");
thread1.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread2.start();
}
}
class MyThread extends Thread {
private MyClass myClass;
public MyThread(MyClass myClass) {
this.myClass = myClass;
}
@Override
public void run() {
if(Thread.currentThread().getName().equals("t1")){
myClass.doSome();
}
if(Thread.currentThread().getName().equals("t2")){
myClass.doOther();
}
}
}
class MyClass{
public synchronized void doSome(){
System.out.println("doSome begin...");
try {
Thread.sleep(1000 * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("doSome over...");
}
public void doOther(){
System.out.println("doOther begin...");
System.out.println("doOther over...");
}
}
题二:
package com.caopeng.java.thread.exam2;
public class Exam01 {
public static void main(String[] args) {
MyClass myClass = new MyClass();
Thread thread1 = new MyThread(myClass);
Thread thread2 = new MyThread(myClass);
thread1.setName("t1");
thread2.setName("t2");
thread1.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread2.start();
}
}
class MyThread extends Thread {
private MyClass myClass;
public MyThread(MyClass myClass) {
this.myClass = myClass;
}
@Override
public void run() {
if(Thread.currentThread().getName().equals("t1")){
myClass.doSome();
}
if(Thread.currentThread().getName().equals("t2")){
myClass.doOther();
}
}
}
class MyClass{
public synchronized void doSome(){
System.out.println("doSome begin...");
try {
Thread.sleep(1000 * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("doSome over...");
}
public synchronized void doOther(){
System.out.println("doOther begin...");
System.out.println("doOther over...");
}
}
题三:
package com.caopeng.java.thread.exam2;
public class Exam01 {
public static void main(String[] args) {
MyClass myClass1 = new MyClass();
MyClass myClass2 = new MyClass();
Thread thread1 = new MyThread(myClass1);
Thread thread2 = new MyThread(myClass2);
thread1.setName("t1");
thread2.setName("t2");
thread1.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread2.start();
}
}
class MyThread extends Thread {
private MyClass myClass;
public MyThread(MyClass myClass) {
this.myClass = myClass;
}
@Override
public void run() {
if(Thread.currentThread().getName().equals("t1")){
myClass.doSome();
}
if(Thread.currentThread().getName().equals("t2")){
myClass.doOther();
}
}
}
class MyClass{
public synchronized void doSome(){
System.out.println("doSome begin...");
try {
Thread.sleep(1000 * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("doSome over...");
}
public synchronized void doOther(){
System.out.println("doOther begin...");
System.out.println("doOther over...");
}
}
题四:
package com.caopeng.java.thread.exam2;
public class Exam01 {
public static void main(String[] args) {
MyClass myClass1 = new MyClass();
MyClass myClass2 = new MyClass();
Thread thread1 = new MyThread(myClass1);
Thread thread2 = new MyThread(myClass2);
thread1.setName("t1");
thread2.setName("t2");
thread1.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread2.start();
}
}
class MyThread extends Thread {
private MyClass myClass;
public MyThread(MyClass myClass) {
this.myClass = myClass;
}
@Override
public void run() {
if(Thread.currentThread().getName().equals("t1")){
myClass.doSome();
}
if(Thread.currentThread().getName().equals("t2")){
myClass.doOther();
}
}
}
class MyClass{
public synchronized static void doSome(){
System.out.println("doSome begin...");
try {
Thread.sleep(1000 * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("doSome over...");
}
public synchronized void doOther(){
System.out.println("doOther begin...");
System.out.println("doOther over...");
}
}
死锁
package com.caopeng.java.thread.deadlock;
public class DeadLock {
public static void main(String[] args) {
Object o1 = new Object();
Object o2 = new Object();
Thread myThread1 = new MyThread1(o1,o2);
Thread myThread2 = new MyThread2(o1,o2);
myThread1.start();
myThread2.start();
}
}
class MyThread1 extends Thread {
Object o1;
Object o2;
public MyThread1(Object o1, Object o2) {
this.o1 = o1;
this.o2 = o2;
}
@Override
public void run() {
synchronized(o1){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(o2){
}
}
}
}
class MyThread2 extends Thread {
Object o1;
Object o2;
public MyThread2(Object o1, Object o2) {
this.o1 = o1;
this.o2 = o2;
}
@Override
public void run() {
synchronized(o2){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(o1){
}
}
}
}
守护线程
java语言中线程分为两大类:
守护线程的特点:一般用户线程是一个死循环,所有的用户线程只要结束,守护线程自动结束。
主线程main就是一个用户线程。
守护线程一般用在什么地方呢?
? 每天00:00的时候系统数据自动备份
? 这个需要使用到定时器,并且我们可以将定时器设置为守护线程。
? 一直在那看着,每到00:00的时候就自动备份一份,所有的用户线程如果结束了,守护线程自动退出,没必要进行数据备份了。
package com.caopeng.java.thread;
public class ThreadTest14 {
public static void main(String[] args) {
Thread thread = new DateThread();
thread.setName("备份数据的线程");
thread.setDaemon(true);
thread.start();
for(int i = 0;i < 10;i++){
System.out.println(Thread.currentThread().getName() + "--->" + i);
}
}
}
class DateThread extends Thread {
@Override
public void run() {
int i = 0;
while(true){
System.out.println(Thread.currentThread().getName() + "--->" + (++i));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
定时器
定时器的作用:
? 每隔特定的时间,执行特定的程序
package com.caopeng.java.thread;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class TimerTest {
public static void main(String[] args) throws ParseException {
Timer timer = new Timer();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date firstDate = sdf.parse("2021-10-24 14:09:00");
timer.schedule(new LogTimerTask(),firstDate,1000 * 10);
}
}
class LogTimerTask extends TimerTask {
@Override
public void run() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time = sdf.format(new Date());
System.out.println(time+" 完成了一次备份");
}
}
实现线程的第三种方式
这种方式可以获取到线程返回值,之前的两种方式无法获得到线程的返回值。
实现Callable接口
package com.caopeng.java.thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class ThreadTest15 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask futureTask = new FutureTask(new Callable(){
@Override
public Object call() throws Exception {
System.out.println("call method begin");
Thread.sleep(1000 * 10);
System.out.println("call method end");
int a = 100;
int b = 200;
return a+b;
}
});
Thread thread = new Thread(futureTask);
thread.start();
Object o = futureTask.get();
System.out.println(o);
}
}
关于Object类中的wait方法和notify方法
-
wait方法和notify方法不是线程对象的方法,是java中任何一个java对象都有的方法,因为这两个方法是Object类自带的 -
wait方法作用 Object o = new Object();
o.wait();
表示:让正在o对象上活动的线程进入等待,无期限等待,知道被唤醒为止
-
notify方法的作用 唤醒正在o对象上等待的线程
生产者消费者模式
package com.caopeng.java.thread;
import java.util.ArrayList;
import java.util.List;
public class ThreadTest16 {
public static void main(String[] args) {
List list = new ArrayList();
Thread producer = new Thread(new Producer(list));
Thread consumer = new Thread(new Consumer(list));
producer.setName("producer");
consumer.setName("consumer");
producer.start();
consumer.start();
}
}
class Producer implements Runnable {
private List list;
public Producer(List list) {
this.list = list;
}
@Override
public void run() {
while(true){
synchronized (list) {
if(!list.isEmpty()){
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
Object o = new Object();
list.add(o);
System.out.println(Thread.currentThread().getName() + "--->" + o);
list.notify();
}
}
}
}
}
class Consumer implements Runnable{
private List list;
public Consumer(List list) {
this.list = list;
}
@Override
public void run() {
while(true){
synchronized(list){
if(list.isEmpty()){
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
Object o = list.remove(0);
System.out.println(Thread.currentThread().getName() + "--->" + o);
list.notify();
}
}
}
}
}
一直生产 while(true){ // 给list加锁 synchronized (list) { // 不为空 if(!list.isEmpty()){ //进入等待状态,当前线程进入等待状态,并且释放 List集合的锁 try { list.wait(); } catch (InterruptedException e) { e.printStackTrace(); } // 仓库为空 }else{ Object o = new Object(); list.add(o); System.out.println(Thread.currentThread().getName() + “—>” + o); // 唤醒消费者进行消费 list.notify(); } } } } }
// 消费线程 class Consumer implements Runnable{ private List list;
public Consumer(List list) {
this.list = list;
}
@Override
public void run() {
// 一直消费
while(true){
synchronized(list){
// 仓库空了,消费者进程进行等待
if(list.isEmpty()){
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
// 可以消费
Object o = list.remove(0);
System.out.println(Thread.currentThread().getName() + "--->" + o);
list.notify();
}
}
}
}
}
[外链图片转存中...(img-YQndGccp-1635059132268)]
|