🏠个人主页: 黑洞晓威
🧑个人简介:大家好,我是晓威,一名普普通通的大二在校生,希望在CSDN中与大家一起成长。🎁如果你也在正在学习Java,欢迎各位大佬来到我的博客查漏补缺呀,如果有哪里写的不对的地方也欢迎诸佬指正啊。
@[toc]
线程
程序:一段静态的代码进程:运行中的代码
线程:程序的一种执行路径
线程A和线程B是什么关系?
在java语言中:
线程A和线程B,堆内存
和 方法区
内存共享。但是 栈内存
独立,一个线程一个栈。
线程的创建方法一(继承)
- 创建一个继承与Thread的子类
- 重写Thread的run()方法
- 创建Thread类子类的对象
- 通过这个对象调用start()
class mythresd extends Thread{//创建一个继承与Thread的子类
@Override
public void run() {//重写Thread的run()方法
for(int i = 0;i<=100;i++){
if(i%2==0){
System.out.println(("分支线程--->" + i);
}
}
}
}
public class App {
public static void main(String[] args) {
mythresd mythresd = new mythresd();创建Thread类的子类对象
// 不会启动线程,不会分配新的分支栈。(这种方式就是单线程。)
//mythresd.run();
mythresd.start();//通过这个对象调用start()
for(int i = 0;i<=100;i++){
if(i%2==0){
System.out.println(("主线程--->" + i);
}
}
}
}
注意:
- t.run() 不会启动线程,只是普通的调用方法而已。不会分配新的分支栈。(这种方式就是单线程。)
- t.start() 方法的作用是:启动一个分支线程,在JVM中开辟一个新的栈空间,这段代码任务完成之后,瞬间就结束了。
这段代码的任务只是为了开启一个新的栈空间,只要新的栈空间开出来,start()方法就结束了。线程就启动成功了。启动成功的线程会自动调用run方法,并且run方法在分支栈的栈底部(压栈)。 - run方法在分支栈的栈底部,main方法在主栈的栈底部。run和main是平级的。匿名创建Thread类子类
匿名创建Thread类子类
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
for(int i = 0;i<=100;i++){
if(i%2==0){
System.out.println(i);
}
}
}
}.star();
Thread中的常用方法
- currentThread():静态方法,返回当前代码的线程
- getName():获得当先线程的名字
- setName():设置当前线程的名字
- yield():释放当前cpu的执行权
- join():在线程a中调用线程b的join();则线程a进入阻塞状态,等线程b结束后结束阻塞状态
- sleep(Long militime)让当前线程阻断指定的militime毫秒,此时为阻塞状态
- isAlive();判断线程是否存活
class mythresd extends Thread{
@Override
public void run() {
for(int i = 0;i<=100;i++){
try {
sleep(100);//阻断100毫秒
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if(i%2==0){
System.out.println(Thread.currentThread().getName()+" "+i);//输出线程名
}
}
}
}
public class App {
public static void main(String[] args) {
mythresd mythresd = new mythresd();
mythresd.setName("线程一");//设置线程名
mythresd.start();
Thread.currentThread().setName("主线程");
for(int i = 0;i<=100;i++){
if(i%2==0){
if(i==20){
mythresd.join();
} //阻断主线程
System.out.println(Thread.currentThread().getName()+" "+i);//设置主线程的名字
}
}
//判断mythread线程是否还存活
System.out.println(mythresd.isAlive());
}
}
线程的优先级
MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5(默认)
注意:高优先级抢占低优先级线程cpu,但不代表一定先执行完高优先级再执行低优先级
实例:三个窗口卖一百张票
class piao extends Thread{
private static int tickect=100;
@Override
public void run() {
while (tickect>0){
System.out.println(getName()+"卖了"+tickect);
tickect=tickect-1;
}
}
}
public class App {
public static void main(String[] args) throws InterruptedException {
piao p1 = new piao();
//给p1设定优先级
p1.setPriority(Thread.MAX_PRIORITY);
piao p2 = new piao();
piao p3 = new piao();
p1.setName("窗口一");
p2.setName("窗口二");
p3.setName("窗口三");
p1.start();
p2.start();
p3.start();
}
}
多线程的方法二(实现)
- 创建一个实现了Runnable接口的类
- 重写run();
- 创建实现Runnable类的对象;
- 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象;
通过Thread类的对象调用start();
//创建一个实现了Runnable接口的类 class mythread implements Runnable { private int tickect = 100; //重写run(); @Override public void run() { while (tickect > 0) { System.out.println(Thread.currentThread().getName() + "卖了第" + tickect + "张票"); tickect--; } } } public class App { public static void main(String[] args) throws InterruptedException { //创建实现类的对象; mythread mythread = new mythread(); //将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象; Thread t1 = new Thread(mythread); Thread t2 = new Thread(mythread); Thread t3 = new Thread(mythread); t1.setName("一号窗口"); t2.setName("二号窗口"); t3.setName("三号窗口"); t1.setPriority(Thread.MAX_PRIORITY); //通过Thread类的对象调用start(); t1.start(); t2.start(); t3.start(); } }
优势:实现的方式没有类的单继承的局限性
实现的方式更适合处理多个共享数据的类型
采用匿名内部类创建:
public class ThreadTest04 {
public static void main(String[] args) {
// 创建线程对象,采用匿名内部类方式。
Thread mythread = new Thread(new Runnable(){
@Override
public void run() {
for(int i = 0; i < 100; i++){
System.out.println("mythread线程---> " + i);
}
}
});
// 启动线程
mythread.start();
for(int i = 0; i < 100; i++){
System.out.println("main线程---> " + i);
}
}
}
线程的安全问题
- 问题:买票过程中出现了重票错票
- 原因:当某个线程尚未操作完成时,其他线程参与过来,也操作车票
1同步代码块
public void run() {
while (tickect > 0) {
//
synchronized (this){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + "卖了第" + tickect + "张票");
tickect--;
}
}
}
public class App {
public static void main(String[] args) throws InterruptedException {
mythread mythread = new mythread();
Thread t1 = new Thread(mythread);
Thread t2 = new Thread(mythread);
Thread t3 = new Thread(mythread);
t1.setName("一号窗口");
t2.setName("二号窗口");
t3.setName("三号窗口");
t1.start();
t2.start();
t3.start();
}
}
关键字synchronized(同步监视器)
- 操作共享数据的代码就是需要同步的代码
- 同步监视器俗称锁,任何一个类的对象都可以充当锁
- 要求:需要同步的线程要共用同一把锁
- 操作同步代码时只能运行一个线程,相当于一个单线程,但能保证安全问题
实现方法的synchronized
class mythread implements Runnable {
private int tickect = 100;
Object object=new Object();
/*如果是继承方法的多线程,要这样书写
Object static object=new Object();
因为在主函数中创建了三个mythread对象,要加static只生成一个object确保公用一把锁。*/
@Override
public void run() {
while (true) {
synchronized (object) {
//也可以直接写成synchronized (this)这里的this指myehread对象
if (tickect > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + "卖了第" + tickect + "张票");
tickect--;
} else {
break;
}
}
}
}
}
public class App {
public static void main(String[] args) throws InterruptedException {
mythread mythread = new mythread();
Thread t1 = new Thread(mythread);
Thread t2 = new Thread(mythread);
Thread t3 = new Thread(mythread);
t1.setName("一号窗口");
t2.setName("二号窗口");
t3.setName("三号窗口");
t1.start();
t2.start();
t3.start();
}
}
补充:在实现Runnable接口创建多线程的方式中,可以考虑用this充当对象
在继承thread创建多线程的方式中,慎用this充当对象,可以考虑用当前类充当对象
2同步方法
如果操作共享数据的代码在一个方法中
- 实现接口多线程使用同步方法
class mythread implements Runnable {
private int tickect = 100;
@Override
public void run() {
boolean ture = true;
while (ture) {
show();
}
}
//实现接口多线程使用同步方法
public synchronized void show(){
if (tickect > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + "卖了第" + tickect + "张票");
tickect--;
}
}
}
- 继承thread多线程同步方法需要调用静态方法
public static synchronized void show(){}
总结:
- 同步方法依然涉及到同步监视器,只是不显示说明。
- 非静态同步方法的同步监视器是:this
静态同步方法的同步监视器是:当前类
3Lock锁
class mythread implements Runnable {
private int tickect = 100;
//实例化
private ReentrantLock lock=new ReentrantLock();
@Override
public void run() {
while (true) {
//try/finally语句包含同步代码
try {
lock.lock();//lock锁
if (tickect > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + "卖了第" + tickect + "张票");
tickect--;
}else {
break;
}
}finally {
//finally解锁
lock.unlock();
}
}
}
}
lock与synchronized的区别
synchronized在执行完同步代码后,自动释放同步监视器。
lock需要手动启动同步(lock),同时结束同步也需要手动实现(unlock)
死锁
public class sisuo {
public static void main(String[] args) {
StringBuffer s1 = new StringBuffer();
StringBuffer s2 = new StringBuffer();
new Thread(new Runnable()){
@Override
public void run() {
synchronized (s1){
s1.append("a");
s2.append(1);
//拿到s1锁进入阻断状态等待s2锁
try {
sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (s2){
s1.append("b");
s2.append(2);
}
}
System.out.println(s1);
System.out.println(s2);
}
}.start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (s2){
s1.append("c");
s2.append(3);
//拿到s2锁进入阻断状态等待s1锁
try {
sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (s1){
s1.append("d");
s2.append(4);
}
}
System.out.println(s1);
System.out.println(s2);
}
}) .start();
}
}
练习
银行有一个账户.有两个储户分别向同一个账户存3000元,每次存1000,存三次.每次存完打印账户余额.
class Account{
//一个账户
private double balance;
public Account(double balance) {
this.balance = balance;
}
public synchronized void deposit(double money) {
balance+=money;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+balance);
}
}
class Customer extends Thread{
//两个储户实现公用一个账户
private Account att;
//构造器
public Customer(Account att) {
this.att = att;
}
@Override
public void run() {
for (int i = 0; i < 3; i++) {
att.deposit(1000);
}
}
}
public class sisuo {
public static void main(String[] args) {
Account account = new Account(0);
Customer c1 = new Customer(account);
Customer c2 = new Customer(account);
c1.setName("账户1 ");
c2.setName("账户2 ");
c1.start();
c2.start();
}
}
多线程通讯
例子:使用两个线程交替打印1-100
涉及到的三个方法:
wait():一旦执行此方法,当前线程进入阻塞状态,并释放同步监视器
notify():会唤醒被wait的一个线程,如果有多个线程wait则比较优先级
notifyAll():唤醒所有被wait的线程
注意:
- 上述三个方法必须在同步代码块或同步方法中使用
- 上述方法默认为this.方法,要与synchronized保持一致
class num implements Runnable{
private int num=1;
@Override
public void run() {
synchronized (this){
while (true){
notify();
if(num<=100){
System.out.println(Thread.currentThread().getName()+":"+num);
num++;
try {
wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}else {
break;
}
}
}
}
}
不懂的地方:上面代码的wait()与notify()放在循环外无法实现通讯
sleep与wait的异同
相同点:都进入阻塞状态
不同点:1.声明的位置不同:Thread类中声明的sleep,wait在object类中声明
2.调用要求不同:sleep可以在任何场景下调用,wait必须的同步代码中调用
3.sleep不会释放同步监视器(锁),wait会释放同步监视器(锁)
线程的创建方法三(Callable)
优点:1. 相比于run方法可以有返回值
2.可以抛出异常
3.支持泛型
操作步骤:
- 创建一个实现Callable的实现类
- 实现call方法,将操作声明在call方法中
- 创建Callable实现类的对象
- 将此实现类的对象作为参数,传递到FutureTask构造器,创建FutureTask对象
- 将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象
//1.创建一个实现Callable的实现类
class call implements Callable{
int sum = 0;
@Override
//2.实现call方法,将操作声明在call方法中
public Object call() throws Exception {
for (int i = 0; i <= 100; i++) {
System.out.println(i);
sum+=i;
}
return sum;
}
}
public class text01 {
public static void main(String[] args) {
//3.创建Callable实现类的对象
call call=new call();
//4,将此实现类的对象作为参数,传递到FutureTask构造器,创建FutureTask对象
FutureTask futureTask=new FutureTask(call);
//5. 将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象
new Thread(futureTask).start();
try {
Object sum = futureTask.get();
System.out.println("总计"+sum);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
}
线程的创建方法四(线程池)
好处:
- 提高响应速度
- 降低资源消耗
- 便于线程管理
- corePoolSize:核心池的大小
- maximumPoolSize:最大线程数
- keepAliveTime:线程没有任务时最多保存多长时间后终止
实现方法:
- 提供指定线程数量的线程池
- 执行指定的线程操作,需要实现Runnable接口或者Callable接口实现类的对象
- 将实例化的对象作为参数 执行service.execute();
- 关闭线程池
class thread implements Runnable{
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
if(i%2==0){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
class thread0 implements Runnable{
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
if(i%2!=0){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
public class pool {
public static void main(String[] args) {
//1提供指定线程数量的线程池
ExecutorService service = Executors.newFixedThreadPool(10);
//2执行指定的线程操作,需要实现Runnable接口或者Callable接口实现类的对象
thread thread1 = new thread();
thread0 thread2 = new thread0();
//3将实例化的对象作为参数 执行service.execute();
service.execute(thread1);
service.execute(thread2);
// service.submit();适用于callable
System.out.println(service.getClass());
ThreadPoolExecutor service1=(ThreadPoolExecutor)service;
service1.setCorePoolSize(15);
//4关闭线程池
service.shutdown();
}
}
🎉文章到这里就结束了,感谢诸佬的阅读。🎉💕欢迎诸佬对文章加以指正,也望诸佬不吝点赞、评论、收藏加关注呀😘